35

I am running an SSH server within WSL2 on a WIN10 machine. To make that work I am using:

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=22 connectaddress=172.19.237.178 connectport=22

This works fine initially. 172.19.237.178 is the IP of the WSL2 VM.

There is just one problem. I have the sshd set to run when the PC boots, and every time I boot the machine WSL2 has a different IP. Is there any way to configure WSL2 to use a static IP?

Edit: See this question for a workaround to determine the WSL machine's IP.

Nick
  • 1,533
  • 2
  • 14
  • 15

12 Answers12

28

The IP address of a WSL2 machine cannot be made static, however it can be determined using wsl hostname -I

Based on this I was able to create the following powershell script that will start sshd on my WSL machine and route traffic to it.

wsl.exe sudo /etc/init.d/ssh start
$wsl_ip = (wsl hostname -I).trim()
Write-Host "WSL Machine IP: ""$wsl_ip"""
netsh interface portproxy add v4tov4 listenport=22 connectport=22 connectaddress=$wsl_ip

I added the following to my sudoers file via visudo to avoid needing a password to start sshd

%sudo ALL=(ALL) NOPASSWD: /etc/init.d/ssh

Finally, from an administrative powershell terminal, I scheduled my script to run at startup

$trigger = New-JobTrigger -AtStartup -RandomDelay 00:00:15
Register-ScheduledJob -Trigger $trigger -FilePath C:\route_ssh_to_wsl.ps1 -Name RouteSSHtoWSL
Nick
  • 1,533
  • 2
  • 14
  • 15
14

This solution helped me to set up a static ip of my wsl, try:

Run this on your windows host machine:

netsh interface ip add address "vEthernet (WSL)" 192.168.99.1 255.255.255.0

And this on your wsl linux machine:

sudo ip addr add 192.168.99.2/24 broadcast 192.168.99.255 dev eth0 label eth0:1;

But to keep this IP after the rebooting your sytem you need to set up those commands in the startup scrip.

5

No need to use scripts to get the IP address, just use the OpenSSH server for Windows and change the default shell from C:\Windows\System32\cmd.exe or C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe to C:\Windows\System32\bash.exe:

https://docs.microsoft.com/en-US/windows-server/administration/openssh/openssh_server_configuration

2

Using wsl and wsl2 at the same time caused problems for me. Could not get correct wsl hostname from the powershell command:

wsl hostname -I

Building on the answer from Nick, I needed to forward web 80 and 443, along with some other app ports.

ubuntu2004.exe -c "sudo /etc/init.d/ssh start"
$wsl_ip = (ubuntu2004.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"""

netsh interface portproxy add v4tov4 listenport=443 listenaddress=0.0.0.0 connectport=443 connectaddress=$wsl_ip netsh interface portproxy add v4tov4 listenport=8080 listenaddress=0.0.0.0 connectport=8080 connectaddress=$wsl_ip netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=$wsl_ip netsh interface portproxy add v4tov4 listenport=3001 listenaddress=0.0.0.0 connectport=3001 connectaddress=$wsl_ip netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=2222 connectaddress=$wsl_ip netsh interface portproxy add v4tov4 listenport=22 listenaddress=0.0.0.0 connectport=22 connectaddress=$wsl_ip

DanielV
  • 21
2

I have not enough reputation to comment then I put my own answer but approved answer by Nick (https://superuser.com/a/1619390/1023342) is the good one for me, expect for one small detail, for me wsl hostname -I returns two IP ("IP1 IP2") and I want first one, I then do:

$wsl_ip = (wsl hostname -I).split(" ")[0]
netsh interface portproxy add v4tov4 listenport=22 connectport=22 connectaddress=$wsl_ip
gluttony
  • 274
1

A no-brainer solution, I run this script manually(run as administrator) everytime I need to proxy a port to WSL.

proxyport.bat

wsl hostname -I
@echo off
set /p WSLIP=What is the current WSL IP address?
netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=2222 connectaddress=%WSLIP% connectport=2222

Then one more step in the WSL terminal:

sudo service ssh start
1

netsh interface portproxy add ...

every time I boot the machine WSL2 has a different IP

A great question at the time, and some great workarounds that are still valid.

However, note that the latest WSL2 releases (as of 1.1.0 in January 2023), will always attempt to reuse the same IP address.

This makes most of the shenanigans we used to have to jump through (e.g., the other answers) unnecessary now. A single netsh interface portproxy add ... should work across multiple reboots.

If you have not updated WSL2 in a while, make sure you install the latest from the Microsoft Store and you should gain access to this feature.

NotTheDr01ds
  • 28,025
1

If you're trying to ssh to wsl, and this idea makes you wanna make its IP static.

Maybe you can check out this post, THE EASY WAY how to SSH into Bash and WSL2 on Windows 10 from an external machine, which allows you ssh to your wsl env, without knowing wsl's IP

But this method only support bash.

If you wanna support your beautiful zsh, you may wanna check this post writen in Chinese, 如何远程 SSH 连入 WSL2,运行 ZSH 并支持 SFTP?


Original anwser

None of the existing answers fit my situation since I'm using Tailscale and WireGuard as my local/remote VPN network solution.

Instead, I install Tailscale in WSL. Then make the Tailscale IP static (which is way more easier). Then connect it through VPN.

The latency increase is less than 1ms.

KSroido
  • 11
0

Here's a very compact solution for WSL2 that will auto-start the SSH server. It eliminates having to deal with Powershell signing/execution policies and having to run it on a schedule.

  1. Run wsl sudo nano /etc/wsl.conf and add these lines:

    [boot]
    command="service ssh start"
    

    This will auto-start SSH server on every WSL startup.

  2. (Optional) If you'd like to use a custom port (like 2022) for SSH (for example, if you use multiple WSL distros), run:

    wsl sudo sed -i 's|.*Port.*|Port 2022|' /etc/ssh/sshd_config
    

This works because WSL2 maps ports from its distros to the Windows' localhost. Now you can just connect to your host using localhost:22 (or a custom port).

dinvlad
  • 131
0

I've insignificantly improved the script in Nick's answer because i needed to wait for docker desktop started, so here it is:

while ($true) {
    $wsl_ip = (wsl hostname -I).trim()
    $regex = [regex]"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"
    if ($regex.IsMatch($wsl_ip)) {
        Write-Host "IP Address: $wsl_ip"
        break
    }
    else {
        Write-Host "Invalid IP Address"
        Start-Sleep -Seconds 10
    }
}
netsh interface portproxy add v4tov4 listenport=2222 connectport=2222 connectaddress=$wsl_ip
0

Thanks to all who have responded here, it helped me cobble together my own take on this.

For context: Probably due to domain group policy, use of static IP addresses with WSL permission inbound/outbound rules on Windows Firewall did not work for me reliably. On some boots the static Linux IP address and SSH listener on it work, on other boots - not even pinging (from Windows host). Using powershell in windows explicitly to poke the linux VM is clumsy (gotta remember to do it on all reboots, or use task scheduler or some such...)

Ergo - let Linux guest do all its magic!

  • Probably need to get PowerShell installed into Windows, as for other solutions (in particular allows to elevate the call for port forwarding on host OS side) :)
  • Create /root/setup-network.sh:
#!/bin/sh

https://superuser.com/a/1723531/433945

Add to /etc/wsl.conf boot/command=... for autostart?

PATH="/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0:$PATH" export PATH

Fenced with a separate touch-file, an IP address might be not

available when this first runs - allow reentry until success!

Assumes first IP is the one generated by WSL anew for each VM boot.

AUTOIP="hostname -I | awk '{print $1}'" if [ -n "$AUTOIP" ] && [ ! -f /dev/shm/network-initialized-fwd ] ; then powershell.exe -Command "Start-Process netsh.exe -ArgumentList "interface portproxy add v4tov4 listenport=22099 connectport=22 connectaddress=$AUTOIP" -Verb RunAs" # Consider similar forwards for X11, VNC, etc. touch /dev/shm/network-initialized-fwd fi

(Optionally) shield the rest of such init (assuming SSHD listens

on IP_ANY, so starting it once suffices)

[ -f /dev/shm/network-initialized ] && exit

ps -ef | grep -v grep | grep sshd || sudo service ssh start

I've also had problems resolving Internet DNS, so this settled in

(also works if /etc/resolv.conf is a dangling symlink to non-existent

dir below, by default). Specifically, my problem was having the

resolver re-generated from Windows host settings, and not having

VPN etc. access to the domain DNS server to actually resolve stuff.

Per my notes, https://github.com/microsoft/WSL/issues/6977 might

also help: /etc/wsl.conf => [network] => generateResolvConf=false

grep 8.8.8.8 /etc/resolv.conf || { sudo mkdir -p /run/resolvconf/ &&
echo 'nameserver 8.8.8.8' | sudo tee -a /etc/resolv.conf }

touch /dev/shm/network-initialized

  • And add this into Linux guest's /etc/wsl.conf as
[boot]
command="/root/setup-network.sh"
  • Alternately, call it in your user's ~/.profile (maybe place in its homedir to be accessible) - it does work, but only after you first open a terminal to WSL. Some may deem it more secure and better facilitating host OS boots vs. UAC pop-up noted below.

Upon first start of the guest/session after host boot (or wsl --shutdown and subsequent start) Windows may ask for UAC permissions for that netsh command called from the guest system. But now it is all constrained in the guest and tied to using it.

-1
  1. Start wsl

  2. En terminal (wsl)

    sudo ifconfig eth0 172.27.100.100 netmask 255.255.255.0 broadcast 172.27.255.255

  3. En Powershell

    netsh interface ip set address name="vEthernet (WSL)" static 172.27.100.99 255.255.255.0 172.27.255.255

Access to wls to windows 172.27.100.99 Access to windows to wls 172.27.100.100