Port Forwarding WSL 2 to Your LAN

Run a public server from WSL thats accessible on your local network
WSL
PowerShell
Networking
Picture of John Wright Stanly, author of this blog article
John Wright Stanly
Jun 25, 2021

-

-

image

The Windows Subsystem for Linux (WSL) is great for development. WSL gives you the functionality of UNIX with only a fraction of the resources required for a traditional VM. It also runs alongside your Windows environment, allowing you to access Windows files inside Linux. Microsoft continues to improve WSL like with the release of WSL2 in 2019

WSL2 noticeably does not share an IP address with your computer. Because WSL2 was implemented with Hyper-V, it runs with a virtualized ethernet adapter. Your computer hides WSL2 behind a NAT where WSL2 has its own unique IP. Although WSL2 can be accessed on your machine through localhost thanks to recent updates, these ports will not be open on your LAN.

image

Port Forwarding

You can fix this with port forwarding. First find WSL's IP address. Open WSL and run ifconfig or ip addr show or hostname -I depending on your distro.

image

Now open PowerShell. For every port you need to forward to your LAN, run this netsh command in PowerShell, replacing [PORT] and [WSL_IP].

netsh interface portproxy add v4tov4 listenport=[PORT] listenaddress=0.0.0.0 connectport=[PORT] connectaddress=[WSL_IP]

You can view all forwarded ports with netsh interface portproxy show v4tov4.

image

The Windows firewall might block these ports. To ensure WSL ports can be reachable, you can make a new inbound firewall rule. Open PowerShell as Administrator and run the following command. Replace -LocalPort with the list/range of ports you need.

New-NetFirewallRule -DisplayName "WSL2 Port Bridge" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 80,443,10000,3000,5000

image

Congrats! Your WSL ports are now reachable through your Windows computer's IP address.

However, there is a catch. WSL changes its IP on every Windows reboot. If you're looking for a one time solution, you'll need to automate this process.

Automating with PowerShell

We port forward WSL using a PowerShell script. Save the script below as Bridge-WslPorts.ps1. Change $ports to your desired ports.

$ports = @(80, 443, 10000, 3000, 5000);

$wslAddress = bash.exe -c "ifconfig eth0 | grep -oP '(?<=inet\s)\d+(\.\d+){3}'"

if ($wslAddress -match '^(\d{1,3}\.){3}\d{1,3}$') {
  Write-Host "WSL IP address: $wslAddress" -ForegroundColor Green
  Write-Host "Ports: $ports" -ForegroundColor Green
}
else {
  Write-Host "Error: Could not find WSL IP address." -ForegroundColor Red
  exit
}

$listenAddress = '0.0.0.0';

foreach ($port in $ports) {
  Invoke-Expression "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$listenAddress";
  Invoke-Expression "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$listenAddress connectport=$port connectaddress=$wslAddress";
}

$fireWallDisplayName = 'WSL Port Forwarding';
$portsStr = $ports -join ",";

Invoke-Expression "Remove-NetFireWallRule -DisplayName $fireWallDisplayName";
Invoke-Expression "New-NetFireWallRule -DisplayName $fireWallDisplayName -Direction Outbound -LocalPort $portsStr -Action Allow -Protocol TCP";
Invoke-Expression "New-NetFireWallRule -DisplayName $fireWallDisplayName -Direction Inbound -LocalPort $portsStr -Action Allow -Protocol TCP";

If your WSL distro doesn't have ifconfig, set the variable $wslAddress with another command like ip addr or hostname.

If you have multiple WSL distros, make sure to set your default so bash.exe will execute under the correct WSL. You can list and set distros with wslconfig /l and wslconfig /s <name>

image

We can now run this script either on demand or through a scheduled task.

1. On Demand

The command below can be run as administrator. Replace [PATH_TO_SCRIPT] with the directory of the script, like C:\Users\John\Documents.

powershell.exe -File "[PATH_TO_SCRIPT]\Bridge-WslPorts.ps1"

Not only does powershell.exe run in PowerShell and cmd, but also in WSL. Therefore you can try running this in WSL's .bashrc to open ports on launch, just as long as you always run WSL as administrator.

2. Scheduled Task

Rather than running the script after every reboot, we can rely on a Windows Task to run the script automatically. Since WSL changes IP during reboots, we'll trigger the task upon logon.

Create the task by running the following commands in PowerShell as administrator. Replace [PATH_TO_SCRIPT] with the absolute directory of the script, like C:\Users\John\Documents. Now, ports will be forwarded always to the correct WSL IP across reboots.

$a = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-File `"[PATH_TO_SCRIPT]\Bridge-WslPorts.ps1`" -WindowStyle Hidden -ExecutionPolicy Bypass"
$t = New-ScheduledTaskTrigger -AtLogon
$s = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$p = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Administrators" -RunLevel Highest
Register-ScheduledTask -TaskName "WSL2PortsBridge" -Action $a -Trigger $t -Settings $s -Principal $p

If you need to change the port list later on, feel free to change $ports and rerun the script using the command above. The task will also use the updated script.

The script's output window will be hidden when running. If you want logs, add to the top of the script Start-Transcript -Path "C:\Logs\Bridge-WslPorts.log" -Append. This will record all logs to the file passed in.

image

Now, you won't have to think about WSL port forwarding every time you restart your PC!

Comments

Be the first to add a comment!

Add Comment

Post