I'm developing a rails app using WSL2/Ubuntu on my Windows 10 machine, which is great! The problem is I can't connect to my server from another computer in the same network.
For further clarity, I am running a Puma server on localhost:3000
I have tried the following:
rails s -b 172.26.208.1 -p 3000
None of the above have worked thus far... What I'd like to do is:
Is there anything I'm missing to at least see the website correctly? (and any comments on the VScode part would be appreciated)
While this is not a common scenario, you can follow these steps to make it work. Obtain the IP address of your host machine by running this command from your Linux distribution: cat /etc/resolv. conf Copy the IP address following the term: nameserver. Connect to any Windows server using the copied IP address.
As for accessing the WSL instance from the local Windows machine or from other devices on the network, because the WSL interface is NAT'd behind the Windows interface, it requires port forwarding.
Alternatively, you can open a Remote WSL window directly from VS Code: Start VS Code. Press F1, select Remote-WSL: New Window for the default distro or Remote-WSL: New Window using Distro for a specific distro. Use the File menu to open your folder.
See this video, it helped me:
https://www.youtube.com/watch?v=yCK3easuYm4
netsh interface portproxy add v4tov4 listenport=<port-to-listen> listenaddress=0.0.0.0 connectport=<port-to-forward> connectaddress=<forward-to-this-IP-address>
for example
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=172.30.16.3
Microsoft has published a little bit of information about this on their WSL1 to WSL2 comparison page
This one worked for me:
The firewall commands in that script didn't work on my system. So I deactivated the Windows firewall completely and use the following stripped version. (The final users will use a 3d party firewall anyway, so that's ok).
$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=@(123,456);
#[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 ",";
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";
}
#Then so something, e.g.
#bash.exe -c "cd /g-inverter; ./start_docker.sh"
You may need to "apt install net-tools" for ifconfig in the script. There's also a solution with "ip addr" in the internet somewhere that does not need ifconfig" in a great thread, I haven't a link for here and now.
Caveat
This works only for TCP traffic. netsh interface portproxy does not support port forwaarding of UDP traffic.
Solution: Switch from NAT to Bridge mode
WSL2 comes by default in NAT mode. There the wsl2 system has another ip in another subnet than the host. The PC is from external peers only visible by the windows IP and the wsl2 ip/net is hidden/internal. So all traffic would need to be accepted by the windows IP and then forwarded to the wsl2 ip (port forwarding).
There is another mode called bridge mode. In bridge mode your network interface card will be shared to the wsl2 system, and it will get its own IP/Net in wsl2. So in effect your network card is shared to both systems (windows / wsl2) and will have two IPs, as if you'd have two systems with its own network card each. Cool thing: You will never have port conflicts when Windows uses the same port as well, as your wsl2 app (like 111).
Enable bridge mode
Open Hyper-V Manager as administrator
Select your pc, open Virtual Switch Manager
Select WSL
Set to external network
Select the network card the traffic runs through
Then login to wsl2 terminal and configure an IP address. E.g.
sudo ip addr add 192.168.0.116/24 dev eth0
You need to use another free IP (not your Windows IP). If your network has a DHCP server your wsl can get one by:
sudo ip addr flush dev eth0
sudo dhclient eth0
Caveat
I haven't elaborated yet, how to get DNS working in this scenario in case you want to still be able to access the internet (apt etc.). There's some documentation from MS written in /etc/resolv.conf and maybe executing what's written there and installing resolvconf (prior to all steps above, as you have no internet once you start to bridge) might do the trick.
First, you will need to open a port in your machine to be able to access it from your network.
netsh advfirewall firewall add rule name="Allowing LAN connections" dir=in action=allow protocol=TCP localport=5000
After you open the port (5000 in my case) you will need to make port forwarding from this port to the port that your app is listening on in the WSL.
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=5000 connectaddress=localhost connectport=<the port that your app is listening on>
Notice: I set the connectaddress to localhost not to the IP address of the WSL because by default the requests that go to localhost are forwarded to the WSL. By doing this you won't need to set the port forwarding every time you restart your machine because the IP address of the WSL is dynamic.
WSL2 exposes ports on the local interface (which is why in Windows you can access localhost:8080
when your 8080
service is running in WSL2), but they listen on 127.0.0.1
(which is why you can't access yourhostname:8080
on other computers your LAN).
There's a tool to fix this called WSLHostPatcher, which you can find here (download via the Releases section): https://github.com/CzBiX/WSLHostPatcher
WSLHostPatcher changes the behaviour to listen on all IPs, exposing any WSL2 services to all computers on your network.
WSLHostPatcher.exe
The service should now be accessible via other computers on your network.
Building on Roelofs suggestion no.2, here's what made everything tick in my case. I'm too fresh to just leave a comment unfortunately.
My starting point:
Win 10 Pro
Ubuntu under WSL2
(Docker with Linux containers)
My goal:
Getting an rtmp stream from a client on the network into and back out of an nginx server running on the Ubuntu machine.
In my case, I could not get Hyper-V to set the bridge up properly. After selecting External network for the WSL switch in the Virtual switch section of Hyper-V Manager and hitting apply, it eventually failed with error 0x80070490. Don't know why and didn't have the time to investigate. WSL was not running and neither was the Docker service.
Instead, I just left the setting on Internal network and bridged the interfaces the manual way, under Network Connections (run->ncpa.cpl). In my case, the WiFi connection and vEthernet (WSL). Immediately after doing this, I lost internet connectivity and it took me an embarrassingly long time to find out that a reboot was needed. (Windows for once did not ask me to!)
After the reboot, I now had internet access from the host, the bridge was set to DHCP and had inherited the IP of the WiFi interface (192.168.1.246). Great.
The VM however was still getting the IP of the virtual switch (or however you want to view it, the random 172.x.x.x address that windows seems to assign to the switch as well as the VM).
Firing up Ubuntu, I decided to do a:
sudo ip addr flush dev eth0
Continuing with:
sudo dhclient eth0
threw a handful of errors at me since I was using the vanilla Ubuntu distro from Windows store, no systemd, no fun. Despite that, it did manage to add the IP of the bridge to eth0. As this was not very handy, I got rid of that with:
sudo ip addr del 192.168.1.248/24 dev eth0
but not before taking a sneak peek at the routing table:
user@vm:~$ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
After deleting the old IP, I added a unique one from outside of my DHCP range:
sudo ip addr add 192.168.1.50/24 dev eth0
I checked the routing table again and the first entry was gone. At this stage I could ping LAN but not WAN.
Added the entry back with:
sudo ip route add default via 192.168.1.1 dev eth0
Pinging WAN IPs was now possible, but no DNS resolution. LMGTFM: Adding permanent DNS In case the solution goes missing, here it is, credit to non-static:
Create a file: /etc/wsl.conf. Put the following lines in the file [network] generateResolvConf = false In a cmd window, run wsl --shutdown Restart WSL2 Create a file: /etc/resolv.conf. If it exists, replace existing one with this new file. Put the following lines in the file nameserver 8.8.8.8 Or the IP of whatever DNS server you want to use, repeat step 3 and 4.
So, to conclude, check your routing and setup your DNS-conf properly. Unfortunately, the IP settings are reverted every time you restart WSL. If you are not ok with doing this manually every time there are discussions on how to automate it here and here. I haven't had the time to find my favorite.
Best regards Alexander
Taking into consideration the above (correct) solutions this is a simplified one liner version that works for me:
Run the following command using the IP found in the previous step in connectaddress parameter:
netsh interface portproxy add v4tov4 listenport= listenaddress=0.0.0.0 connectport= connectaddress=172.24.26.277
Parameters explanation:
listenport: the port that Windows will listen
listenaddress: the address that your Windows will listen. Usually 0.0.0.0 should do.
connectport: the port that your Linux Distro through wsl2 will listen.
connectaddress: the public IP of your Linux wsl2 instance (found in step 1)
Use cmd or PowerShell as Administrator to run the above command.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With