简体   繁体   中英

Access a web server which is running on WSL (Windows Subsystem for Linux) from local network

After installing Ubuntu as WSL(Windows Subsystem for Linux) I've run:

root@teclast:~# python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 ...

and try to access to this web server from my windows machine http://0.0.0.0:8000 or http://192.168.1.178:8000 but no success, web server available only by the address http://127.0.0.1:8000 or http://localhost:8000 it means that I can't connect to this web server from another pc in my.network. Is it possible to getting an access to WSL from outside?

None of the existing answers work for me, as WSL2 is running in its own VM and has its own network adapter. You need some sort of bridge or port forwarding for non-localhost requests to work (ie from another host on the same network).

I found a script in https://github.com/microsoft/WSL/issues/4150 that worked to resolve the issue:

$remoteport = bash.exe -c "ifconfig eth0 | grep 'inet '"
$found = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if( $found ){
  $remoteport = $matches[0];
} else{
  echo "The Script Exited, the ip address of WSL 2 cannot be found";
  exit;
}

#[Ports]

#All the ports you want to forward separated by coma
$ports=@(80,443,10000,3000,5000);


#[Static ip]
#You can change the addr to your ip config to listen to a specific address
$addr='0.0.0.0';
$ports_a = $ports -join ",";


#Remove Firewall Exception Rules
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";

#adding Exception Rules for inbound and outbound Rules
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_a -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_a -Action Allow -Protocol TCP";

for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport";
}

Running this script from an elevated/admin Powershell session did the trick for me. This allows accessing the WSL2 service running on a port to be accessible from the Windows host IP at that same port.

In your VM, execute ifconfig

You will see your IP in the first section (eth0:) inet xxxx

This xxxx is the IP you have to put in your browser.

Please follow the steps mentioned in the link shared by @erazerbrecht and run your HTTP server by providing your ip address (instead of using localhost) and port number.

example:
root@teclast:~# python3 -m http.server -b 192.168.1.178 8000 Serving HTTP on 192.168.1.178 port 8000 (http://192.168.1.178 :8000/) ...

Otherwise you can also do this instead of following the link :
1. Goto Windows defender firewall
2. select inbound
3. create new rule ; next
4. select Program as a rule type; next
5. select All Program ; next
6. select allow the connection ; next
7. check all 3 (Domain, Private, Public); next
8. provide rule a name
9. finish
10. Your are good to go

To anyone coming new to this post have a look at Microsoft doc https://docs.microsoft.com/en-us/windows/wsl/networking#accessing-linux-networking-apps-from-windows-localhost .

According to the docs if you are running an older version of Windows (Build 18945 or less), you will need to get the IP address of the Linux host VM

This should be a non-issue post the Windows Build 18945.

I followed the answer by @toran-sahu about adding an inbound rule to Windows Defender Firewall but recently (after adding a 2nd wsl2 instance) it stopped working again. I came across this issue thread and running the following in cmd prompt got it working again for me.

wsl --shutdown

update: it seems this issue comes from having Fast Startup enabled https://stackoverflow.com/a/66793101/8917310

Similar to @countach 's answer:

If using Ubuntu type ip address in the WSL terminal. Look for the entry that says #: eth0 ... where # is some small number. There is an ip address there. Use that.

I have written a PowerShell script, using 2 simple commands which worked perfectly for me.

# get the IP address of the WSL
$str = wsl -- ip -o -4 -json addr list eth0 | ConvertFrom-Json | %{ $_.addr_info.local } | ?{ $_ }
echo "Wsl IP: $str"

# establish the connection with the WSL
# listenport,connectport ports can be changes as per the needs
netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$str

Create a .ps1 file using this code and execute it from the PowerShell terminal.
It can be added to the scheduler so that the script will run every time at Windows boot.

For now (03.2022) to access from outside an app running on WSL 2, we need to do the following:

  • Make rules in the firewall for accepting incoming (and maybe also outgoing) connexions on the protocol and port on which the app is running (eg TCP/80)

  • Get WSL's vm IP: hostname -I

  • As said on this page ( Accessing a WSL 2 distribution from your local area network (LAN) ), use this IP address to add, in Windows, a proxy that listens on the port and redirects to WSL's vm. This is done by the following command in a PowerShell running as administrator:

netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=192.168.101.100

Where 192.168.101.100 is vm's IP from hostname -I and 80 the port we want to open to the outside.

As WSL's IP changes when rebooted, this should be automated in a PowerShell script, where the previous proxy is removed and a new one is set to the current IP. All credit goes to Edwindijas on Github from who's script this one is heavily inspired:

$ports=@(80,21,22) # the ports you want to open
$addr='0.0.0.0';

$wslIP = bash.exe -c "hostname -I"
$found = $wslIP -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if(! $wslIP -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}') {
  echo "WSL's IP cannot be found. Aborting";
  exit;
}

$portsStr = $ports -join ",";
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $portsStr -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $portsStr -Action Allow -Protocol TCP";

for ($i = 0; $i -lt $ports.length; $i++) {
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$wslIP";
}

how ifconfig mentioned in the script works in windows? isn't it need to be 'ipconfig'?

Avoid using firewall rules used in some answers on the web, I saw some of them create some kind of allow-all firewall rule (allow any terrafic from any ip and any port), this can cause security problems

Simply use this single line from this answer which worked perfectly for me (just creates a port proxy):

netsh interface portproxy add v4tov4 listenport=<windows_port> listenaddress=0.0.0.0 connectport=<wsl_port> connectaddress=<WSL_IP>

where <WSL_IP> is the ip of wsl, you can get it by running ifconfig on wsl. Also <windows_port> is the port windows will listen on and <wsl_port> is the port server is running on WSL.

You can list current port proxies using:

netsh interface portproxy show all

it means that I can't connect to this web server from another pc in my network. Is it possible to getting an access to WSL from outside?

It should be noted, among all the answers here, that this question was originally for WSL1 (given when it was asked) and was self-answered by the OP as having been a WSL or Windows issue that was resolved by a Windows update.

Ironically, this is a known issue on WSL2 (see footnote), so years later, people started finding it (and answering it) in that context.

There multiple solutions for this on WSL2, but with no disrespect to the other answerers here, they have all missed the two easiest ones:

  • WSL1 : First, just use WSL1. For most (but not all) web development tasks where you need to access your app from another machine on the network, WSL1 will be better at networking and will run your application and tools just fine. WSL2 brings in real, virtualized Linux kernel, but the "fake kernel" (syscall translation layer) of WSL1 still works fine today for most tasks.

    When running python3 -m http.server under WSL1, you'll automatically be able to access it from other machines on the network via the Windows host's IP address (or DNS name). As the OP mentioned in the self-answer, that pesky WSL1 bug has been fixed for years now.


  • SSH reverse tunnel If you do need to use WSL2, here's an option that doesn't require port forwarding or any advanced firewall rules that need to be updated each time WSL restarts. The problem with those rules is that the IP address for WSL changes every time you restart, meaning those rules have to be deleted and recreated constantly.

    On the other hand, we can use SSH within WSL2 to connect to Windows, where the name, and perhaps even the IP address, is constant.

    One-time setup: Enable SSH

    There's some one-time setup for this, but it's useful regardless:

    First, install/enable Windows OpenSSH server. This is a feature that is built-in to Windows and just needs to be enabled. You'll find the full instructions here , but it's typically just a matter of (from an Admin PowerShell):

     Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 Start-Service sshd Set-Service -Name sshd -StartupType 'Automatic' if (,(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) { Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist. creating it..." New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 } else { Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists." }

    Edit your C:\ProgramData\ssh\sshd_config and set GatewayPorts yes . This is what allows us to use SSH as a gateway from other devices on the network.

    Finally, of course, you'll need a firewall rule in Windows allowing the HTTP(s) (or other) port you'll be using. Example (from Admin PowerShell):

     New-NetFirewallRule -DisplayName "WSL Python 8000" -LocalPort 8000 -Action Allow -Protocol TCP

    And restart the OpenSSH Server service:

     Restart-Service sshd

    Create the tunnel

    With that in place, it's a simple matter to create the tunnel we need:

     ssh -R 8000:localhost:8000 NotTheDr01ds@$(hostname).local

    Replacing, of course, NotTheDr01ds with your own Windows username, if it differs from the WSL username

    That's going to use your Windows username and password since SSH is running on the Windows side.

    Once you have ensured that it works, two other recommendations:

    • Set up key-based authentication so that you don't need to enter a password each time
    • Change the SSH connection to:
       ssh -fN -R 8000:localhost:8000 NotTheDr01ds@$(hostname).local
      That will put the SSH session into the background and not allocate a terminal for it.

在此处输入图像描述

There's a npm package for that now:

npx expose-wsl@latest

I've tested on Update for Microsoft Windows(KB4532132) with reinstalled WSL and it works as expected. Seems the issue was related to old windows version or old WSL version.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM