Edit: Dec 12, 2022
Click on image to enlarge
I want to share with you a pro tip to make programming on Windows a little easier with the help of the WSL 2.
Imagine you are developing an API, web app, website, etc. on your Windows machine under WSL2. For example, in my case, my own website. I use the React framework Next.js, and yarn as package manager, so if I want to start the development server to check my site I just do:
yarn dev
With the above command, the development server will start and listen on port 3000. It will serve the page to every device that can reach it, as it’s serving the site on the 0.0.0.0 address by default.
Click on image to enlarge
Then, if I visit localhost:3000 on my Windows machine, everything works. The server returns my live website. However, what happens when I try to open my live site from my mobile phone? I navigate to <my_windows_local_ip>:3000 and then
Click on image to enlarge
The solution: forwarding ports in WSL 2 NAT
WSL 2 creates a virtualized Ethernet adapter with its own unique IP address, so in fact it creates a NAT between your WSL instance and your Windows host computer. So to be able to access applications running in your WSL from within your Local Area Network (LAN), you must port forward the desired ports.
The solution is to add a port proxy in the Windows Firewall, so we open a PowerShell terminal with administrator rights and execute the following command:
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=<the_wsl_ip>
You can get the WSL virtual machine IP right from PowerShell, by executing bash:
bash.exe -c "ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'"
Or the same from within the WSL instance:
ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'
After executing the netsh interface command in an administrator PowerShell, the server can now be reached without further configuration needed, nor restarting the server.
Click on image to enlarge
Bonus: a PowerShell script to simplify this task
The IP address of the WSL VM changes every time it reboots (like when you power off your Windows), so if you have already added a port proxy for an IP address, it might not work if you restart your computer.
I have created a PowerShell script to simplify this task when you need it to be done. You could also configure a Windows task to execute this PowerShell script on every reboot, if you also start WSL on boot (because WSL needs to be launched to get its IP address). Or could be a way to executing it from WSL once it starts. I’m not explaining here how to achieve this, as I don’t have configured it that way. I don’t usually access apps running inside WSL from my LAN, so when I need to do this, I just execute the following script in an administrator PowerShell terminal (explanation below):
$remoteport = bash.exe -c "ip -4 addr show eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'"
$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,3000,3001,3002,3333,5000,5432,6000,8080,8081,19000,19001);
#[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";
}
What this script does is:
- Gets the IP address of the WSL VM.
- Defines the ports you want to proxy/forward. You can therefore edit the
$portsarray as you wish. Here I have written all the ports I could potentially use, so the script will be valid for multiple apps/technologies. - Adds Windows Firewall rules to allow all inbound/outbound traffic to the WSL. Also removes old rules from previous script executions (with different IP addresses).
- Adds the required port proxies, at the same times that removes old ones from old script executions (with different IP addresses).
If this is the first time that you execute this script, or you have added a new port, you can get a lot of not found messages. These are normal warnings that you can safely ignore, as these occur because the script is trying to delete non-existent firewall rules (first time execution) or port proxies (when you add a new port).
I hope this serves you to improve your developer experience in WSL and make you more productive. If you want to continue improving your WSL experience, check how to run GUI applications on WSL2.
I hope my article has helped you, or at least, that you have enjoyed reading it. I do this for fun and I don’t need money to keep the blog running. However, if you’d like to show your gratitude, you can pay for my next coffee with a one-time donation of just $1.00. Thanks!
WSL-Port-Forwarding
Windows Subsystem for Linux 2 (WSL2) has a virtualized ethernet adapter with its own unique IP address (usually 172.x.x.x). Services run in WSL2 cannot be directly accessed by remote computers. There is a simple solution: using netsh to forward connection.
This script will discover service run on WSL, and then forward the host port to WSL and configure appropriate firewall rules.
TODO
- a launcher to make it faster to start this script
- get the notification of program binding port in real time (NEED HELP)
Limitations
- Since we use
netsh, we cannot support UDP. - We scan service periodicity and it might not suitable for service that frequently changing listening ports.
Principle
Built on Hyper-V, WSL2 is a a real virtual machine, which has independent virtual network (in a different network segment from the host). We discuss the incoming and outgoing directions of network packets from external network respectively:
INCOME: If the firewall of the Host is configured properly, the external computer can access the port on the physical network interface of the host, but the service on WSL listens on its own virtual network interface.
OUTCOME: Windows will complete the network address translation (NAT address translation), and WSL always accesses the external network with the host’s IP, Windows can also correctly handle the response from the external network in the way of NAT address translation. Therefore, there is no obstacle for WSL to access the external network or PASSIVELY accept the results returned by the external network.
Reference:
https://docs.microsoft.com/en-us/windows/wsl/compare-versions
If you rely on a Linux distribution to have an IP address in the same network as your host machine, you may need to set up a workaround in order to run WSL 2. WSL 2 is running as a hyper-v virtual machine. This is a change from the bridged network adapter used in WSL 1, meaning that WSL 2 uses a Network Address Translation (NAT) service for it’s virtual network, instead of making it bridged to the host Network Interface Card (NIC) resulting in a unique IP address that will change on restart. To learn more about the issue and workaround that forwards TCP ports of WSL 2 services to the host OS, see WSL GitHub repository issue 4150, NIC Bridge mode (TCP Workaround).
Therefore, if we can listen to some ports on the host, and then forward the packets of external computers to appropriate port of WSL, the external computers can «directly» access the services on WSL.
The Simplest Way to Use
Out of the box, no configuration required, only three simple step.
-
Open Windows Terminal with administrators privilege.
The simplest way:
Win+X-> Windows PowellShell**(admin)** -
Open WSL Terminal
Run
wslin the console. (If you install multiple distribution, you may usewsl -d Ubuntu-20.04etc.) -
Run this script
If you haven’t installed this script, run:
pip install wsl_port_forwarding
Once it is installed, just run:
port_forwarding. If everything is OK, something like this will be displayed in console.
Script will run automatically configure forwarding and firewall rules for the ports that WSL service bind on. You can access the services on the WSL as if they were on the host. For example, when the host IP is 192.168.1.1 and the WSL IP is 172.28.1.1, you can just connect to python3 by 192.168.1.1:11311 instead of 172.28.1.1:11311, while 192.168.1.1 may be directly accessed by external network.
Requirement
- WSL2
- python3.6+ in WSL2
- Windows administrators privilege
Install
there are two ways to install
- Use pip to install (recommend)
pip install wsl_port_forwarding
then: port_forwarding to start this program.
- Download from source
wget https://raw.githubusercontent.com/ColorsWind/WSL-Port-Forwarding/main/src/wsl_port_forwarding/port_forwarding.py
then: python3 port_forwarding.py to start.
Restore
The added rules will be deleted automatically when the script exits normally, so you only need to delete the script. That is, if you install by pip, use pip uninstall wsl_port_forwarding, or if you install from source, just delete port_forwarding.py.
If you need to manually delete the configuration added by the script, you can run in powershell (or cmd) in Windows with administrators privilege.
netsh.exe interface portproxy reset ipv4 netsh.exe advfirewall firewall del rule name='WSL Auto Fowards'
WARNING: All netsh portproxy ipv4 rules will be clean.
Advance Usage
Command parameters:
usage: port_forwarding [-h] [--mode {auto,manual}] [--interval INTERVAL] [--allow ALLOW]
[--disallow DISALLOW] [--windows_ip WINDOWS_IP] [--wsl_ip WSL_IP]
[--ignore_exception] [--gen_config] [--clean_rules]
A script that enable outside program access WSL2 ports by port forwarding.
optional arguments:
-h, --help show this help message and exit
--mode {auto,manual} when to update port forwarding rule.
--interval INTERVAL how often are network state changes detected (auto mode only).
--allow ALLOW program name that allows external access to the port
--disallow DISALLOW program name that disallows external access to the port
--windows_ip WINDOWS_IP
windows ip that external can access
--wsl_ip WSL_IP wsl ip that windows can access
--ignore_exception whether interpret script when exception occurs
--gen_config generate config in ~/.wsl_port_forwarding.json
--clean_rules clean all netsh port forwarding rule and relevant firewall rules
Configuration:
Although the script can work well without configuration, we still provide some customizable configurations. Run port_forwarding --gen-config, then edit ~/.wsl_port_forwarding.json. Note when there is a configuration, it will print Successfully load config. first every time you run this script.
{
"update_interval": 0.5,
"windows_ip": "0.0.0.0",
"wsl_ip": "172.28.1.1",
"ignore_exception": false, // if true, every time program encounter a exception it will pause
"allow_program_name": [], // if set, program that is not in this list will be ignore
"disallow_program_name": [] // if set, program that is in this list will be ignore
}
License
MIT License
Copyright (c) 2022 ColorsWind
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the «Software»), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED «AS IS», WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Limitations
-
At present,
netshonly supports TCP forwarding, so the script can only deal with TCP protocol.https://docs.microsoft.com/en-us/windows-server/networking/technologies/netsh/netsh-interface-portproxy
-
No simply way to get notification when a program bind a port in Linux is found, so currently the script just gets binded ports at regular intervals (default: 0.5s). In the worst case, there is a delay of 0.5s from the external network can access service on WSL to that port has been bound. Therefore, the script may not be suitable for occasions with high frequency and low delay.
In this post you will learn how to open / forward ports of programs running in WSL2 in the Windows firewall so that they are accessible from the home network. How can I access a WSL2 Linux with SSH? How do I share a web server via WSL2? These questions are answered below
Find out IP address in WSL2
First, the WSL2 IP address is required so that Windows can be told to which address the request must be forwarded. The command depends on the Linux distribution.
Ubuntu
ifconfig
Debian
ip a
In my case the IP address of the WSL2 Linux was 172.29.192.157 , this should be adjusted in all following commands.
Run PowerShell or Command Prompt as Administrator
For the following commands it is necessary to start Windows PowerShell or Command Prompt as administrator (right click: Run as administrator)
It is important again to adjust the IP address in the commands.
Forward OpenSSH server
If you want to enable OpenSSH, this is port 22 by default, alternatively you can also set a different port in the config of the SSH server.
Proxy forwarding
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=22 connectaddress=172.29.192.157 connectport=22
Firewall rule
netsh advfirewall firewall add rule name=”WSL2 Forward Port 22” dir=in action=allow protocol=TCP localport=22
Forward web server
If you want to enable Apache or Nginx, this is port 80 by default.
Proxy forwarding
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=80 connectaddress=172.29.192.157 connectport=80
Firewall rule
netsh advfirewall firewall add rule name=”WSL2 Forward Port 80” dir=in action=allow protocol=TCP localport=80
Check proxy rules
The following PowerShell command can be used to display all entered proxy rules
netsh interface portproxy show v4tov4
Check firewall rules
Windows Defender Firewall -> Advanced Settings -> Inbound Rules
Delete proxy rules
If you want to delete a proxy rule with a specific port, this works with the following PowerShell command, before using {port} should be replaced with the desired port to be deleted
netsh interface portproxy delete v4tov4 listenport={port} listenaddress=0.0.0.0
Delete firewall rules
Windows Defender Firewall -> Advanced Settings -> Inbound Rules
Release no longer works after the reboot
After each reboot, the WSL2 Linux gets a new IP address, which means that the old rules no longer work. I wrote a PowerShell script to solve them.
The script retrieves the IP address of a WSL2 Ubuntu distribution, deletes all existing PortProxy rules and creates them again with the new WSL2 IP address
#### ------------ Set WSL 2 Machine IP ------------ ####
$wsl_ip = (ubuntu.exe -c "ifconfig eth0 | grep 'inet '").trim().split()| where {$_}
$regex = [regex] "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"
$ip_array = $regex.Matches($wsl_ip) | %{ $_.value }
$wsl_ip = $ip_array[0]
Write-Host "WSL Machine IP: ""$wsl_ip"""
#### ------------ Delete PortProxy rules ------------ ####
netsh int portproxy reset all
#### ------------ Rule: SSH - Port 22 ------------ ####
netsh interface portproxy add v4tov4 listenport=22 listenaddress=0.0.0.0 connectport=22 connectaddress=$wsl_ip
#### ------------ Rule: Webserver SSL - Port 443 ------------ ####
netsh interface portproxy add v4tov4 listenport=443 listenaddress=0.0.0.0 connectport=443 connectaddress=$wsl_ip
#### ------------ Rule: Webserver SSL Regel - Port 80 ------------ ####
netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=$wsl_ip
Vishnu
Posted on
• Edited on
When you start a server in WSL, both Windows and Linux share the same local host. When using a WSL 1 distribution, if your computer was set up to be accessed by your LAN, then applications running in WSL could be accessed on your LAN as well. This isn’t the default case in WSL 2.
So, in order to access the server from your local devices, you need to port forward the WSL local IP using netsh.
First, install net-tools in your linux distro.
For Ubuntu,
sudo apt install net-tools
Enter fullscreen mode
Exit fullscreen mode
Next, in Windows, create network.ps1 PowerShell script file with the following content.
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
$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 {
Write-Output "IP address could not be found";
exit;
}
$ports = @(5001, 19000, 19001);
for ($i = 0; $i -lt $ports.length; $i++) {
$port = $ports[$i];
Invoke-Expression "netsh interface portproxy delete v4tov4 listenport=$port";
Invoke-Expression "netsh advfirewall firewall delete rule name=$port";
Invoke-Expression "netsh interface portproxy add v4tov4 listenport=$port connectport=$port connectaddress=$remoteport";
Invoke-Expression "netsh advfirewall firewall add rule name=$port dir=in action=allow protocol=TCP localport=$port";
}
Invoke-Expression "netsh interface portproxy show v4tov4";
Enter fullscreen mode
Exit fullscreen mode
This script is taken from here, I added command to automatically ask for admin permission, when opening the script, instead of right clicking and clicking run as admin every time you run the script.
Note: The network should be a private network.
The script will ask for admin access.
The script gets the WSL IP address and then executes netsh commands to forward the connection to your Windows’ local IP, which you can see by executing ipconfig in either PowerShell or Command Prompt.
The script also resets all previous forwards and at the end displays the list of ipv4 connections forwarded.
You can modify the $ports array with your commonly used ports.
Since WSL2 uses virtual network adapters, its IP address resets after every startup.
For Expo users
-
Include both ports 19000 and 19001 in the
$portsarray. (Don’t include 19002 — dev tool server). -
In your Android device (connected to the same network as your PC), copy
exp://<your-windows-ip-address>:19000to the clipboard. -
Open Expo Go, «Open from Clipboard» option will appear.
Notes
- PowerShell command to view all forwards
netsh interface portproxy show v4tov4
Enter fullscreen mode
Exit fullscreen mode
-
Have a desktop shortcut for the script. It could be handy.
-
The network should be a private network.
-
If your primary purpose is to only check your website for responsive design with your mobile, there is a simple way for that in chrome. See Chrome remote debugging
References
- Accessing a WSL 2 distribution from your local area network (LAN) — WSL Docs
- WSL issues on Github
- Script Source
- Running a command as Administrator using PowerShell — From StackOverFlow
700 Words | Read in about 3 Min | View times
Overview
With the WSL2 Released, Microsoft has made a big change on the system architecture, including changing from the default bridged network adapter to a hyper-v virtual network adapter.
The new version of WSL is built on Hyper-V, which provides independent virtual network adapter for the virtual matchine. Therefore, WSL2 cannot share the network with host directly. The work around is to forward the TCP ports of WSL2 to the host OS.
In addition, the virtual adapter on WSL2 changes it’s ip address during reboot, which makes it tough to implement a run once solution. So we need a solution to auto update the forwarding TCP ports during host OS reboot.
TCP ports forwarding
If we had installed multiple version of WSL on Windows 10, we need to set the WSL2 as the default distribution:
1// Query versions of different WSL distribution
2> wsl -l -v
3 NAME STATE VERSION
4* Ubuntu-20.04 Running 2
5 Ubuntu-16.04 Stopped 1
6
7// set your own default distribution
8> wsl --set-default Ubuntu-20.04 2
We will open the right version of WSL, Ubuntu20.04 for example, when executing wsl.exe in powershell or command line only if we had set the Ubuntu20.04 as the default distribution.
Now we create a powershell script wslbridge.ps1 to implement TCP ports forwarding:
1# Run as administrator
2if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
3 Start-Process PowerShell -Verb RunAs "-NoProfile -ExecutionPolicy Bypass -Command `"cd '$pwd'; & '$PSCommandPath';`"";
4 exit;
5}
6
7# Read ip address of WSL2
8$remoteport = wsl ifconfig eth0 | wsl grep 'inet '
9$found = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
10
11if( $found ){
12 $remoteport = $matches[0];
13} else{
14 echo "The Script Exited, the ip address of WSL 2 cannot be found";
15 exit;
16}
17
18#[Ports]
19# Modify the forwarding ports here, seperated with comma mark
20$ports=@(80,443,3633,4101,4102,10501);
21
22#[Static ip]
23$addr='0.0.0.0';
24$ports_a = $ports -join ",";
25
26# Remove old Windows firewall rule
27iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";
28
29# Add new Windows firewall rule for inbound and outbound
30iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_a -Action Allow -Protocol TCP";
31iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_a -Action Allow -Protocol TCP";
32
33for( $i = 0; $i -lt $ports.length; $i++ ){
34 $port = $ports[$i];
35 iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
36 iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport";
37}
After executing the wslbridge.ps1 script, we can check whether the TCP ports forwarding takes effect. Execute the following command in powershell:
1> netsh interface portproxy show all
2Listen on ipv4: Connect to ipv4:
3
4Address Port Address Port
5--------------- ---------- --------------- ----------
60.0.0.0 80 172.18.48.151 80
70.0.0.0 443 172.18.48.151 443
80.0.0.0 3633 172.18.48.151 3633
90.0.0.0 4101 172.18.48.151 4101
100.0.0.0 4102 172.18.48.151 4102
110.0.0.0 10501 172.18.48.151 10501
Update forwarding ports during reboot
Press Win + R and input shell:startup, open the Windows startup directory C:\Users\[Your-User-Name]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup.
We can place some vbs scripts here and Windows will auto execute these them during reboot.
Now we can write a vbs script, wsl2autostart.vbs for example, to auto update the forwarding ports on the initialization for WSL2 startup, and update the Windows firewall rules in the meanwhile.
WSL2 will not run the init services on Windows reboot, so manually execution is required. We can put it into the vbs script.
1' run the Linux custom init script as root
2Set ws = WScript.CreateObject("WScript.Shell")
3ws.run "wsl -d Ubuntu-20.04 -u root /etc/init.wsl"
4
5' start the Windows firewall forwarding rules
6Set app = CreateObject("Wscript.Shell")
7app.run "powershell -ExecutionPolicy Unrestricted -file wslbridge.ps1"
The content of /etc/init.wsl depends on your own requirements. For example,
1#! /bin/bash
2
3cd /home/zhxilin/dev/script
4
5./runrendis.sh
6./runmysql.sh
7./runnginx.sh
8./rundistccd.sh
Prev Post: 『What’s std::ios::sync_with_stdio(false) and std::cin.tie(nullptr)?』
Next Post: 『Update multiple versions of dev tools under Ubuntu』
