Score:2

Access WSL2 Ubuntu's application on hosted machine's network

tr flag

I'm running a app in WSL2 whose distribution is Ubuntu 20.04. I can access the application in windows browser with Ubuntu's IP.

Now, I want to access this application on my network ( I mean in my office network on other machine)

I understand that ubuntu's IP is only available to host machine. How can i make it available to others on my network?

How can we do this? Please help.

hr flag
WSL or WSL2? the procedure appears to be different - see for example [WSL Port Forwarding](https://dev.to/vishnu12/wsl-port-forwarding-2e22)
tr flag
Its WSL2, but the above example is for WSL. Please correct me if wrong
hr flag
I *think* it's the case that you don't need to do anything for WSL (it should"just work"), whereas for WSL2 you need to set up port forwarding as described in the linked article. But let's see what answers you get.
NotTheDr01ds avatar
vn flag
@steeldriver You are correct. I'll add a lot more detail in my answer. It's been a while since I've done this, but I have a few new ideas to try since the last time that I'll propose if they work. Port forwarding will work, but that post seems to overcomplicate it. I'm hoping I can propose something more efficient, but we'll see.
hr flag
@NotTheDr01ds great - look forward to reading!
NotTheDr01ds avatar
vn flag
And @Raushan, just for clarity, the doc that steeldriver linked is for WSL2. As it notes at the top, WSL1 didn't need anything like that. But again, don't follow that doc. It will work, but I have a solution (that I've now confirmed) that I am writing up as an answer that I believe is easier.
Score:3
vn flag

Short-answer for WSL1:

Configure your firewall rules once, and everything should "just work". This is the easiest method.

Short-answer for WSL2:

Once you have things configured properly, you can initiate port-forwarding very easily with a single command from Ubuntu/WSL2:

ssh -f -N -R 8080:localhost:8080 "$(hostname).local"

Details on how to configure are below. It's not necessarily easy, but it's all just one-time setup.


(Much) more detail:

As noted by Steeldriver in the comments, WSL1 and WSL2 behave differently here.

Side-note just to clarify terminology (since it differed slightly in the comments): "WSL" refers to the subsystem itself which controls both versions -- WSL1 and WSL2.

Side-note 2. Please don't let the length of this post scare you away. I'm just very detailed. Considering the related Github issue is up to (currently) 536 comments, I think I'm fairly concise by comparison ;-).


WSL1

WSL1 is certainly the easier use-case. Since it's a "translation layer" between Linux syscalls and the Windows kernel, it actually utilizes the "real" Windows network interface(s). For this reason, you are able to connect directly from another device on the network to a port in WSL1.

You likely do still need a firewall rule, however. I'll cover that in the WSL2 answer, since it's common to both. See the New-NetFirewallRule command in the WSL2 section. Just run that one command (one time) for the firewall rule.

However, if your web app doesn't require WSL2 (and most do not), it's usually far easier to run it from a WSL1 instance (or at least it used to be). That's what most people end up doing. On the other hand, with the new (to me at least) WSL2/SSH method I propose below, it's not nearly as painful as it used to be to do this in WSL2. I'll leave it to you which route to choose. At this point, I consider either to be equally valid.

If you choose to go with WSL1, I would recommend keeping two Ubuntu instances around -- One with WSL1 and another with WSL2. That's what I do.

To copy your existing WSL2 instance to a new WSL1, exit the existing one and, from PowerShell:

# Adjust base path as desired
$WSL_ROOT = "$env:USERPROFILE\WSL"
$WSL_IMAGE_NAME = "$(get-date -UFormat `"%Y-%m-%d`") Ubuntu Backup.tar"
mkdir -p "$WSL_ROOT\images"
mkdir -p "$WSL_ROOT\instances\Ubuntu_WSL1"
cd $WSL_ROOT
wsl -l -v
# Confirm name of distribution - If it isn't "Ubuntu", adjust the following line as needed
wsl --export Ubuntu "$WSL_ROOT\images\$WSL_IMAGE_NAME"
wsl --import Ubuntu_WSL1 .\instances\Ubuntu_WSL1\ .\images\$WSL_IMAGE_NAME --version 1
wsl ~ -d Ubuntu_WSL1

At this point, you'll be in an Ubuntu WSL1 instance, but you'll be root, since WSL doesn't "remember" the default username for --import'd instances. Follow my "Method 1" from this answer to set your default username.

At this point, you have two WSL Ubuntu instances, one for WSL1 (Ubuntu_WSL1) and another for WSL2 (likely Ubuntu or perhaps Ubuntu-20.04). If you are using Windows Terminal, it will detect both of them and create profiles for launching. Or you can always launch manually using wsl ~ -d <distroname>.

Another option would be to simple convert to WSL1 and use it exclusively. The steps for this are similar to copying it, since you likely still want to back up. Again, exit the instance, and from PowerShell:

# Adjust base path as desired
$WSL_ROOT = "$env:USERPROFILE\WSL"
$WSL_IMAGE_NAME = "$(get-date -UFormat `"%Y-%m-%d`") Ubuntu Backup.tar"
mkdir -p "$WSL_ROOT\images"
cd $WSL_ROOT
wsl -l -v
# Confirm name of distribution - If it isn't "Ubuntu", adjust the following line as needed
wsl --export Ubuntu "$WSL_ROOT\images\$WSL_IMAGE_NAME"
wsl --set-version Ubuntu 1

There is no need to reset the default username in this case.


WSL2

WSL2 starts to get a lot more complicated. While there's a link in the comments to a doc on how to do it, I'll point you to the original Github issue and comment that really started people in this direction.

There are two real issues that have to be solved to get this working in WSL2:

  • First, the WSL2 network is a virtual network (actually a Hyper-V vNIC). It doesn't reside on your "office network", as you note in your question. You first need to have some way of telling the Windows host to route the packets to your WSL2 virtual network for that port.

  • That forwarding is complicated by the fact that the virtual WSL2 network address changes on every reboot (or wsl --shutdown). That means (at least with the method documented in the comments and that Github issue) that you have to repeat the process of:

    • Finding the WSL2 IP address
    • Deleting old firewall rules
    • Deleting old forwarding rules
    • Creating new firewall rules
    • Creating new forwarding rules

... each time you reboot. What a pain, right?!

So I'm going to propose what I believe is an easier method. You can still fall back to the other method if you desire. This has some slightly complicated set-up, but almost all of it only has to be done once.

This method utilizes SSH to provide the port forwarding. Since this is initialized from the WSL2-end, it has several advantages:

  • First, it can be done as part of the same step when you run your application (web server). You don't mention the architecture/language of the app, but I'll assume one of the most common -- Node. If that's the case, you can likely even include it in your npm run script. There's almost certainly a technique that will work for any architecture.

  • Most importantly, it does not need the IP address of the WSL2. This avoids the need to do 4 of those above steps each time you reboot.

So, here we go. First, there's the "one time" setup:

  • Enable Windows OpenSSH server. You can follow the Microsoft instructions, but I'll summarize here. Start by opening a PowerShell prompt as Administrator, then:

    Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
    Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
    Set-Service -Name sshd -StartupType 'Automatic'
    

    This should automatically create the SSH forwarding rules. Again, see the doc if you run into any trouble.

  • Edit C:\ProgramData\ssh\sshd_config and make sure that GatewayPorts yes is not commented out. I believe it is disabled by default.

  • Back in your admin PowerShell, run:

    Start-Service sshd
    
  • You don't mention the port number that your web app runs as, so I'll choose 8080 for the sake of these examples. Adjust as needed. Still in admin PowerShell, run:

    New-NetFirewallRule -DisplayName 8080 -Direction Inbound -LocalPort 8080 -Protocol TCP -Action Allow -Profile Private
    

    This:

    • Allows inbound TCP traffic
    • On port 8080
    • From devices on the private network

    If your network is set Public, drop the -Profile Private

  • Exit your admin PowerShell

With that out of the way, everything's in place. To start the forwarding at this point, execute the following from Ubuntu/WSL2:

ssh -f -N -R 8080:localhost:8080 "$(hostname).local"

Use your Windows username and password.

At this point, you should have access to your web app from another computer (or phone, or whatever) on the same office network.

Explanation:

  • Connects from Ubuntu/WSL2
  • To the OpenSSH Server that we set up
  • Using the "$(hostname).local" which (should) always find the correct DNS name via mDNS (explanation in this answer.
  • It does not allocate a terminal (-N) and runs in the background (-f) after requesting the login credentials
  • It tells the remote SSH host (Windows) to forward traffic received on its port 8080 to the local (WSL2) port 8080.
  • Because we specified GatewayPorts yes in the server config, this means that it will extend that forwarding to other hosts on the network as well.
Score:0
cn flag

While the answer provided by @NotTheDr01ds is correct and very informative, here is another workaround which uses PowerShell powered by Bash on Ubuntu on WSL :)

Add the following function at the bottom of your ~/.bashrc file. If you have more than one WSL instances you can do that for each of them.

Change the values for wsl_port and win_port according to your needs. Optionally change the value of win_ip if you want to listen at certain network interface instead of all available interfaces, which is the meaning of 0.0.0.0.

wsl_win_proxy() {
    wsl_ip="$(ip route | grep -oP '^.*src \K[0-9\.]+')"
    wsl_port="8080"

    win_ip="0.0.0.0"
    win_port="8080"

    rule_name="Inbound TCP ${win_port}"
    win_get_fw_rule_cmd="Get-NetFirewallRule | Where { \$_.DisplayName -eq '${rule_name}' }"
    win_new_fw_rule_cmd="New-NetFirewallRule -DisplayName '${rule_name}' -Direction Inbound -Action Allow -Protocol TCP -LocalPort ${win_port}"

    if ! netsh.exe interface portproxy show all | grep -q -P "${win_ip}\s+${win_port}\s+${wsl_ip}\s+${wsl_port}"
    then
        powershell.exe Start-Process -Verb runAs -FilePath "netsh.exe" \
            -ArgumentList "interface","portproxy","add","v4tov4",\
                    "listenport=$win_port","listenaddress=$win_ip",\
                    "connectport=$wsl_port","connectaddress=$wsl_ip"

        if [[ $? -eq 0 ]]
        then
            echo "Port proxy '${win_ip}:${win_port} > ${wsl_ip}:${wsl_port}' is created."
        else
            echo "Port proxy '${win_ip}:${win_port} > ${wsl_ip}:${wsl_port}' failed."
        fi

        if ! powershell.exe ${win_get_fw_rule_cmd} | grep -q "$rule_name"
        then
            echo "Open PowerShell as Admin and create the following firewall rule:"
            echo -e '\033[1;33m'"$win_new_fw_rule_cmd"'\033[0m'
        fi
    fi
}
wsl_win_proxy

Now when you open WSL the function will create port proxy automatically. The used PowerShell's command must be executed with administrator's privileges, so Windows will ask you for conformation. If the port proxy already exists nothing will happen.

The second part of the function will test for appropriate firewall rule. The test will be done by the rule's DisplayName, so you can remove the previously created rules for the target port to allow the proper work of the function. If the firewall rule already exists nothing will happen, otherwise you will be instructed how to create it. You will be prompted for that action only when a new port proxy is created.

Optionally you can remove or comment the line wsl_win_proxy which invokes the function and start it as shell command when you need that.


The following PowerShell's commands will help you to mange the created port proxies.

netsh interface portproxy show all
netsh interface portproxy reset

References:

Score:0
ve flag

I built an npm package that automatically downloads and runs this WSLHostPatcher.

expose-wsl

So, if you have Node.js installed inside WSL, the simplest way would be to run this in your WSL terminal before starting the server application:

npx expose-wsl@latest
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.