microsoft / WSL

Issues found on WSL
https://docs.microsoft.com/windows/wsl
MIT License
16.7k stars 795 forks source link

[WSL 2] NIC Bridge mode 🖧 (Has TCP Workaround🔨) #4150

Open jkasten2 opened 4 years ago

jkasten2 commented 4 years ago

Issue

WSL 2 seems to NAT it's virtual network, instead of making it bridged to the host NIC. My goal is for a service running in Ubuntu in WSL 2 to be accessible from anywhere on my local network.

Issue Details

Accessing 172.18.72.60 from my Window host does work, however this IP is not accessible from another system on my network.

Workaround

See @edwindijas https://github.com/microsoft/WSL/issues/4150#issuecomment-504209723

briandesousa commented 4 years ago

Same issue here.

IP address of Windows machine is 192.168.1.95 and the eth0 interface in Ubuntu on WSL2 is getting 172.18.47.17.

I originally had Ubuntu on WSL1 running and then upgraded the installation to WSL2 but same problem. Then I uninstalled Ubuntu altogether and reinstalled fresh but same issue after reinstall.

Prior to using WSL2 I had an instance of Ubuntu setup as a Hyper-V VM on the same machine (Hyper-V NIC bridge already existed).

zzz1990771 commented 4 years ago

Same issue. I have a rstudio server running in docker container in WSL2 and wanted to access it from other machine but failed. Really need some fix/idea on it.

nicolazj commented 4 years ago

I can confirm this issue. I can no longer access the service running in subsystem from my mobile device, which is within the same wifi network with my windows laptop.

edwindijas commented 4 years ago

It's not a bug with WSL 2, WSL 2 is running as a hyper-v virtual machine. The hyper-v adapter can be found in network adapters. You can use port forwarding to forward the port with netsh as below. Example command below will forward tcp from port 3000 of the WSL 2 client to port 3000 of the host OS. netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=172.18.28.x Next allow incoming and outgoing ports on port 3000 in firewall.

zzz1990771 commented 4 years ago

It's not a bug with WSL 2, WSL 2 is running as a hyper-v virtual machine. The hyper-v adapter can be found in network adapters. You can use port forwarding to forward the port with netsh as below. Example command below will forward tcp from port 3000 of the WSL 2 client to port 3000 of the host OS. netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=172.18.28.x Next allow incoming and outgoing ports on port 3000 in firewall.

Hi! Did you try it on your end? It didn't work for me when I was trying to access it from another machine in the same network. Although I did work when I use the windows host.

edwindijas commented 4 years ago

I tried it. It worked but the craziest thing is happening, the ip address is changing on reboot. Don't forget to add inbound and outbound rules. Microsoft will fix this issue in the future

edwindijas commented 4 years ago
WSL 2 TPC NETWORK FORWARDING

Introduction

With the introduction of WSL 2 Beta, Microsoft has made changes to the system architecture. The changes include changing from the default bridged network adapter to a hyper-v virtual network adapter. The implementation was not completed during the launch of the beta program. This makes accessing of network resources under WSL 2 complex. The work around is to forward the TCP ports of WSL 2 services to the host OS. The virtual adapter on WSL 2 machine changes it's ip address during reboot which makes it tough to implement a run once solution. Also a side note, windows firewall will block the redirected port.

The work around is to use a script that does :

  1. Get Ip Address of WSL 2 machine
  2. Remove previous port forwarding rules
  3. Add port Forwarding rules
  4. Remove previously added firewall rules
  5. Add new Firewall Rules

Configuration

The script must be run at login ,under highest privileges to work, and Powershell must be allowed to run external sources.

PowerShell Configuration

Enable power shell to run external scripts, run the command below in power shell with administrative privileges.

How To: Go to search, search for task scheduler. In the actions menu on the right, click on create task. Enter Name, go to triggers tab. Create a new trigger, with a begin task as you login, set delay to 10s. Go to the actions and add the script. If you are using Laptop, go to settings and enable run on power.

Finally: I am no expert at security nor scripting and technically new to the windows OS.

Update The update adds the feature to remove unwanted firewall rules. Here is the script.


$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";
}
jkasten2 commented 4 years ago

@edwindijas Awesome, that worked for me! 👍 🎉 I can access my running Linux service from any system on my network via my Windows host IP! Thanks for all the detail and Task Scheduler suggestion too.

In addition, this workaround means localhost works too.

I was trying to go down another route by forcing bridge mode of WSL virtual adapter, that didn't work. Just including it here for completeness.

PS C:\WINDOWS\system32> Set-VMSwitch WSL -NetAdapterName 'Name_of_your_phsyical_windows_NIC'
Set-VMSwitch : Failed while adding virtual Ethernet switch connections.
External Ethernet adapter 'Name_of_your_phsyical_windows_NIC' is already bound to the Microsoft Virtual Switch
protocol.
At line:1 char:1
+ Set-VMSwitch WSL -NetAdapterName 'Name_of_your_phsyical_windows_NIC'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Set-VMSwitch], VirtualizationException
    + FullyQualifiedErrorId : InvalidParameter,Microsoft.HyperV.PowerShell.Commands.SetVMSwitch

@edwindijas Linked your comment as a workaround for this issue in my original post https://github.com/microsoft/WSL/issues/4150#issue-456591548.

edwindijas commented 4 years ago

The script only opens ports you desired. And the ports are redirected to WSL machine. That said, I am no security expert, if you have a better suggestion, I am open to suggestions.

fengrk commented 4 years ago

my method to solve this problem: refresh ip in win10:hosts file

gstorelli commented 4 years ago
WSL 2 TPC NETWORK FORWARDING

Introduction .... Configuration

The script must be run at login ,under highest privileges to work, and Powershell must be allowed to run external sources.

PowerShell Configuration

Enable power shell to run external scripts, run the command below in power shell with administrative privileges.

I saved your script in a file called "wslbridge.ps1" and then in Windows Scheduler just set Powershell.exe as Action and as argument I wrote this (instead of setting the Unrestricted ExecutionPolicy): -ExecutionPolicy Bypass c:\scripts\wslbridge.ps1

Thanks

shayne commented 4 years ago

I wrote a Windows Service that automatically writes the WSL2 VM's IP address to the Windows hosts file. You can then just always reference "wsl.local" from your host machine and it will resolve to the WSL2 VM.

I've been using this for a week now and just open-sourced it.

https://github.com/shayne/go-wsl2-host

shayne commented 4 years ago

I've collected a few WSL2 hacks into a repo: https://github.com/shayne/wsl2-hacks

One thing I wanted to point out, relevant to this thread, was "Access localhost ports from Windows", a way to access ports bound to 127.0.0.1 / localhost from the Windows host.

joaopluigi commented 4 years ago

Thanks @edwindijas, it is a great workaround.

For people using Debian, which does not come with ifconfig out of the box, you can try:

$remoteport = bash.exe -c "ip addr | grep -Ee 'inet.*eth0'"

Also, the hint from @gstorelli of using -ExecutionPolicy Bypass inside the arguments of your Task Scheduler script action is awesome!

Thanks, guys!

QingGo commented 4 years ago

If there are multi wsl2 in your system, remember use this command on cmd to change the wsl2 which you want to bind these port as the default one, because bash.exe will run command in the default wsl2 environment:

# list the wsl on you system
wslconfig /l
# set Debian as default for example
wslconfig /setdefault Debian
abnerfreitas commented 4 years ago

@edwindijas I was hoping to make my Google Chrome (inside my Kali Distro) recognize my Chromecast, but i couldn't find anywhere the actual ports chromecast uses to connect, however now i can access my Kali from anywhere trought SSH.

sangemaru commented 4 years ago

You can now use localhost to connect in recent WSL2 versions

ericblade commented 4 years ago

You can now use localhost to connect in recent WSL2 versions

That's really sort of a different problem -- you can use "localhost" from the host itself, but there's no obvious way to get there from a different machine on the network:

WSL2 is running on Windows host A WSL2 on Windows host A is running server application B You can connect to the application from host A by just doing "localhost:port" You can't connect to the application from Windows Host C in any obvious way.

xorinzor commented 4 years ago

You can connect to the application from host A by just doing "localhost:port" You can't connect to the application from Windows Host C in any obvious way.

If you open the port, why would you not be able to connect to host A from host C by just doing "host-A-ip:port"?

This behaviour shouldn't be any different then it is for linux.

ericblade commented 4 years ago

I don't know the technical reason for it, but it doesn't work by default, and is what brought me here.

timesnewmen commented 4 years ago

You can connect to the application from host A by just doing "localhost:port" You can't connect to the application from Windows Host C in any obvious way.

If you open the port, why would you not be able to connect to host A from host C by just doing "host-A-ip:port"?

This behaviour shouldn't be any different then it is for linux.

Although you can open Win app by localhost:port from WSL2, but they are definitely not sharing the same network like WSL1.

This is working in WSL1, but in WSL2 it's not. Still waiting form a solution.

Yolo-plop commented 4 years ago

Hi, Is there any update on this? I can access my apache in wsl2 fine but it seems like it is not on the same company network as Windows which causes some issues.

ViezeVingertjes commented 4 years ago

This only solves it from Windows to WSL i guess? But i am relying on the otherway around, WSL to Windows, which seems to be broken too.

Scenario: I am using the Docker Client within WSL, Docker Desktop (Server) on the Host (Windows). It can only be accessed by tcp://localhost:2375. This worked prior to upgrading to WSL2.

enjibby commented 4 years ago

This only solves it from Windows to WSL i guess? But i am relying on the otherway around, WSL to Windows, which seems to be broken too.

Scenario: I am using the Docker Client within WSL, Docker Desktop (Server) on the Host (Windows). It can only be accessed by tcp://localhost:2375. This worked prior to upgrading to WSL2.

That is a different issue and you should search/create a new issue in this list for that.

gionkunz commented 4 years ago

That is a different issue and you should search/create a new issue in this list for that.

I think this use-case would also be solved with a proper bridged network like in WSL1, wouldn't it?

CreepyGnome commented 4 years ago

Wait I am confused, I cannot access my WSL Ubuntu from another computer or even from a Hyper-V VM on the same machine and I have disabled the firewall so changing firewall rules don't fix anything for me.

How do you make this work without a firewall? How can I allow my WSL to get its own IP from the DHCP server on my network so that it can be accessed via that?

Not sure why this is complicated can't it just be a passthrough as well, windows wins first if somethings on the requested port, however, if not just auto bridge/route to the WSL. However I think just like VM's get their own IP if you have bridge mode setup, why can't the WSL instance get its own ip in a similar fashion would be a better out of the box option.

diqidoq commented 4 years ago

my method to solve this problem: refresh ip in win10:hosts file

WSL 2 TPC NETWORK FORWARDING

Here is the script.

For those who kindly posted solutions I strongly recommend to add advices how to do this manually too. To only post scripts which update a host file etc. is not a very secure official advice, since some users do not understand what the script is doing. For example: try to find a solution on google to manually update the host file and how the line has to look like to bridge to the wsl2 ip and you will find nothing but this issue thread here.

diqidoq commented 4 years ago

As long as this issue https://github.com/shayne/go-wsl2-host/issues/7 still persists I actually do not recommend to have (Has Workaround) in title of this issue here. From what I understood in other issue comments, it is rather referring to Shaynes addition now than to the original Workaround linked in the OT?`The provided workaround is harming a system slidely with an additional service running and cannot be removed easely for basic users at the moment. I think this needs to be considered before installing it.

alvitawa commented 4 years ago

If you do not wish to clutter your system with scripts and you have access to a different computer on your network through ssh you can do:

ssh -R 0.0.0.0:8080:localhost:80 -N root@othercomputer

Remember to enable GatewayPorts in sshd_config, as outlined in the following question: https://superuser.com/questions/588591/how-to-make-ssh-tunnel-open-to-public

It is probably possible to forward the port to the same windows machine that is running wsl, but I haven't bothered to check.

This approach can be used to expose the server publicly as well, using a service like serveo.net

gdevacc12 commented 4 years ago

At least with Windows 10 version 1903 build 19018.1 with WSL2, it appears binding a service to a port on 0.0.0.0 can also be used without having to find the IP address of eth0. From WSL2 if a service (e.g. httpd) is bound to a port (e.g. 8085) using address 0.0.0.0 (interface sit0@NONE the V6overV4 virtual adapter), then the service can be accessed from Windows using localhost. Applying the firewall rules above (allowing inbound & outbound rules and portproxy) will disable access via localhost but allow a (remote) host to access the service on WSL2 via the Windows IP address. Deleting the rules will allow localhost to be used again. This could make it a little easier for switching between testing on the local Windows OS and a remote machine.,as it would not necessary to spot the service to switch the binding between localhost and the IP address of eth0.

jbmorgado commented 4 years ago

The script by @edwindijas seems to break WinRM. After using it I am stuck with the following error when trying to use WinRM: WinRM firewall exception will not work since one of the network connection types on this machine is set to Public

Although I am very definitively connected to a Private Network.

WimRm creates an HTTP listener this way, but fails to create the HTTPS listener.

I tried to reboot and to delete the Firewall rule that the script created but the error persists.

LeisureLinux commented 4 years ago

Just found something interesting related to SEO, our web page: https://tech.yj777.cn/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3-wsl2/ was referer from this page. Because our page referred this page. So for those can read Chinese, you can also take a look into that article. Sorry, not that much related.

nickweavers commented 4 years ago

On WSL-1 I could have an apache web server using vhosts to serve a number of websites. In my windows hosts file I just needed to have entries to the vhost domains assigning them the loopback IP address 127.0.0.1.

This was great, I could replicate setup's like for example, having two websites using services form a third; a couple of Wordpress sites, say, my-wp-site-1.local and my-wp-site-2.local that use a plugin to communicate with a rest based API service hosted at, say, my-api-site.local. I could set my hosts file to resolve all of those domains to 127.0.0.1, then setup the corresponding vhosts.conf files in WSL-1 /etc/apache2/sites-available, a2ensite them, reload apache and hey presto my wp sites would appear in my browser for http://my-wp-site-1.local or http://my-wp-site-2.local and their plugin could use the web services at http://my-api-site.local.

I addition, the web files could be in the windows file system so my windows based coding IDE worked as normal, but also accessed in WSL-1 via /mnt so the webserver running there could access them (vhosts.conf's) and I could use git commands there in the bash terminal to push/pull code to origin.

It wasn't a very portable setup, but still better than anything I'd had before.

Then came WSL-2 and I saw that it could use Linux containers. This looked even better; there is potential for a portable setup. I can have my vhosting wp server in one container for my-wp-site-1.local and my-wp-site-2.local, my rest api server my-api-site.local in another (they have different php mods and stuff) and I imagined each container could run with it's own IP address assigned so I can again use the windows hosts file to resolve domains so that http requests go to the correct container.

And better still, this setup would mirror what I could implement, say, in AWS under ECS or Fargate.

Sadly, I was soon to be frustrated at how difficult this was to achieve and disappointed that such a promising development fell short of providing what I needed. I can't seem to find a way of setting this up that is within the limits of my basic networking knowledge. I also think that my use case is very probably a common one so for WSL-2 to be useful to people with it, it should not be this hard to achieve what I have described.

I hope cleverer minds that mine can find a way to make this happen so that I don't need an elaborate setup to achieve it.

Things I've noticed while trying to make it work: ifconfig in my my WSL-2 session shows: eth0: inet 172.19.71.88 docker0: inet 172.17.0.1

and in my container when I start the apache daemon I see: root@a50e655f4b8c:/# service apache2 start

So the container is getting allocated another IP address in WSL-2's network by incrementing the last octet , and I guess each new container would get the same.

If I try to browse to 72.17.0.2 from windows, the request times out as the address can't be reached. I understand that a fix for this was to add magic that caused localhosts to give you access, but that doesn't allow me to use my local dev domain names as was the case when using 127.0.0.1 under WSL-1.

I have no idea if the following suggestion I'm going to make is possible, but from an end user perspective it would make this kind of setup easier. I would configure my router to reserve an IP address range to be excluded from DHCP (say 192.168.0.1 to 192.168.0.50), and then add my dev domain names to my hosts file using ip address from within that range. I would then use the docker run command using, the --ip option, like --ip 192.168.0.1

Better still would be a flag that let me pass a local domain name and which and updated docker run command could do the lookup on my hosts file to get the corresponding ip address.

hwasoocho commented 4 years ago

Until this issue is solved, I am using ngrok. It allows you to expose localhost with public ngrok urls.

gmorenz commented 4 years ago

One liner forward ports. Listenport is the world visible port, connectport is the port on the linux (and windows localhost) side of things.

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

If you're using the windows firewall you're going to need to open the port in that before other computers can access it still.

Gorthog commented 4 years ago

@gmorenz does this workaround persist after restart?

luxzg commented 4 years ago

Please note that in case of multiple distros running as WSL2, you still need network bridging, and workaround using port forwarding would quickly become unusable (and not "just" awkward as is), eg. running same service type on multiple WSL2 distros, using same ports.

Osodarck commented 4 years ago

Bump!

What we want? We want bridge network on WSL2. When we want? NOW

nirkons commented 4 years ago
WSL 2 TPC NETWORK FORWARDING

Introduction

With the introduction of WSL 2 Beta, Microsoft has made changes to the system architecture. The changes include changing from the default bridged network adapter to a hyper-v virtual network adapter. The implementation was not completed during the launch of the beta program. This makes accessing of network resources under WSL 2 complex. The work around is to forward the TCP ports of WSL 2 services to the host OS. The virtual adapter on WSL 2 machine changes it's ip address during reboot which makes it tough to implement a run once solution. Also a side note, windows firewall will block the redirected port.

This works great for me, except for forwarding a WOL magic packet coming from inside WSL2, I tried forwarding port 7,9 (the common WOL ones) but its not working since it probably doesn't work the same way, any ideas how to forward that packet?

thanks

tomcanham commented 4 years ago

Just seconding that we either need a separate issue, or we need to remove the "Has Workaround" title. AFAIK, it is simply not possible to access a server hosted under WSL2 from a remote computer. I have completely disabled the firewall, forwarded ports, everything. On-box, all is smooth -- localhost:port works for my web dev work. However, viewing my dev sites in a mobile browser is impossible without ridiculous workarounds (deploying the site on another "real" linux box, for instance).

Here's the 10,000th vote for a proper bridged networking mode.

jayknott commented 4 years ago

My temporary workaround is to use the https://github.com/shayne/go-wsl2-host service to map the dynamic IP every time WSL starts. This works really well and you only need to reinstall it when a new insider build installs. Then I use this command to map my external services:

netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=dns.to.wsl

Portproxy can take a DNS address for the connectaddress argument. This allows it to always work as long as you use the IP mapping service.

You still need to open your Windows firewall to the listen port just like you would for any service running on Windows.

I can now access my WSL2 host from any network computer and even internet clients if I open my external firewall.

But, yes, PLEASE Microsoft, allow bridged network mode. Took me way to long to figure this out.

ThoMint commented 4 years ago

Im trying to get Traffic over UDP working with WSL 2. It seems that i can't forward UDP Ports. I have set my firewall to not block the Ports. Any ideas how to solve my problem? I know for sure a Network Bridge would solve all my and others Problems. Also the "Has Workaround" is not precise becouse it does not work completely. Edit: Found a solution to my problem: You can set the WSL vEthernet Adapter to Bridge-Mode in Hyper-V Manager. Although this works, you cannot use it with GUIs.

kalagxw commented 4 years ago

my method to solve this problem: refresh ip in win10:hosts file

PermissionError: [Errno 13] Permission denied: '/mnt/c/Windows/System32/drivers/etc/hosts'

Nirusu commented 4 years ago

+1 for this issue. The workaround is not sufficient for the WSL experience in my opinion, as it creates a border between the two environments rather than working hand in hand as it does on WSL 1. Hope we'll get bridged network mode soon, as right now staying on WSL 1 provides a better experience for a couple of use cases (e.g. using adb, hosting local web servers etc.).

ThoMint commented 4 years ago

+1 for this issue. The workaround is not sufficient for the WSL experience in my opinion, as it creates a border between the two environments rather than working hand in hand as it does on WSL 1. Hope we'll get bridged network mode soon, as right now staying on WSL 1 provides a better experience for a couple of use cases (e.g. using adb, hosting local web servers etc.).

I can only agree with you as it truly does create a border between the host and the VM. Therefore I went back to WSL 1. I think we are all waiting for a proper bridged network mode. And proper support for GUI.

Yehonal commented 4 years ago

I'm struggling from days with it, I tried all workarounds without success. I've created a proxy that creates a tunnel to the WSL2 IP, but it's very limited in this case, we need a real bridge

luxzg commented 4 years ago

RIP all hopes of improved WSL2 networking, with WSL2 going to release with W10 2004.

ThoMint commented 4 years ago

That really sucks. Maybe in future updates...

luxzg commented 4 years ago

That really sucks. Maybe in future updates...

Btw, can you explain a bit what exactly you did when bridging the adapter in Hyper-V? Is it creating true bridge (becoming a workaround) or is it just for limited scope of situations. No GUI mentioned, I assume you did it via Powershell

ThoMint commented 4 years ago

Workaround: To put WSL2 into bridged Network Mode you have to:

  1. Open Windows-Features (Just search for it in the Start-Menu) and scroll down to Hyper-V
  2. Enable Hyper-V by checking the Box next to it and click OK
  3. Restart your Machine
  4. Open the Hyper-V-Manager (Just search for it in the Start-Menu, you find it under Administrative Tools)
  5. Click "Aktion" (Top-Left) and click connect to server
  6. Choose local machine, click OK
  7. Click "Aktion" (Top-Left) and click Manager for virtual Switches
  8. Select WSL and configure the virtual Switch to briged network mode

Although it does activate a network bridge, it completely isolates the Subsystem from the Host. If the network adapter you choose to bridge to is not connected, it is not possible to use X-Server to work with a GUI. Follow these steps at your own risk, it may mess up your WSL since this is officially not supported. Furthermore, I was not able to share the connenction of the Bridge with the Host-System (even though i choose to when creating the bridge). Hope this helps you out :)