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.

2
  • I just took a look through this GitHub issue thread about people having the same issue as you. Looks like it's just not supported right now. It looks like some people on that thread were able to get results that were acceptable by forwarding some of the VM's ports to the host, but that may not work in your situation.
    – Sam Forbis
    Commented Sep 1, 2020 at 14:46
  • @SamForbis I saw the same thread, and agree, I don't think the workaround will do what I need. I am trying to write scripts to RSYNC files from a remote computer into WSL.A workaround might be to determine when WSL has booted, figure out its IP, and then do the routing. The host's IP is static, so if I could just make the v4tov4 routing always work at boot then it doesn't matter that WSL's IP isn't static.
    – Nick
    Commented Sep 1, 2020 at 17:11

11 Answers 11

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
9
  • 1
    Why don't we use set v4tov4 instead of add v4tov4?
    – Hunkoys
    Commented Oct 29, 2021 at 18:34
  • I didn't think to try it. Is there an advantage to using set rather than add? I'm always running this script at startup so there are no existing portproxies when I run it so I never encounter an issue with using add.
    – Nick
    Commented Nov 2, 2021 at 13:50
  • @Nick Hello sir, your answer was really helpful to me. It got me closer to what I'm looking for, but I haven't been able to completely solve my problem yet. I won't mind if you could help me take a look at this question that I posted over here superuser.com/questions/1685689/…
    – Ikechukwu
    Commented Nov 5, 2021 at 7:26
  • 1
    @Nick I just thought add might actually create a new set of rules everytime the ip changes. I haven't tried add though, so I can't prove it. set seems to work. It updates existing rules.
    – Hunkoys
    Commented Nov 10, 2021 at 5:49
  • 1
    Thanks to an upvote of my answer I can now comment, this answer is almost good for me, except wsl hostname -I returns two IP ("IP1 IP2") and I want first one, I then do: $wsl_ip = (wsl hostname -I).split(" ")[0]
    – gluttony
    Commented Jun 16, 2022 at 9:20
11

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.

4
  • 2
    This is perfect. This solved my problem. Thank you.
    – Rahmani
    Commented Jul 17, 2022 at 17:18
  • How do you run a sudo command on bootup inside WSL? For netsh I found social.technet.microsoft.com/Forums/office/en-US/…
    – chx
    Commented Sep 15, 2022 at 18:28
  • Hm, It just works inside my WSL) And netsh works on windows too (if you launch cmd like an Admin user) Commented Sep 18, 2022 at 17:20
  • Answer: wsl.exe -u root. Last line of my .zshrc: wsl.exe -u root service mysql status >/dev/null || wsl.exe -u root /etc/rc.local where rc.local starts mysql, apache, redis and adds this IP address.
    – chx
    Commented Oct 25, 2022 at 6:54
4

No need to use scripts to get the ip, just use Openssh server for windows and change the default shell from c:/system32/cmd.exe to c:/system32/bash.exe:

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

2
  • Would I be able to SCP and Rsync files into the WSL2 file system area just the same with the windows SSH server?
    – Nick
    Commented May 19, 2021 at 3:46
  • Rsync work like a charm for me. I think SCP will work too. Commented May 25, 2021 at 13:33
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
1
  • Nice, thanks for sharing. You might be able to get rid of the regex if you do some cutting in the bash command. But then again, if the output of ifconfig changes this will break. bash -c "ifconfig eth0 | grep 'inet ' | cut -d ' ' -f 10"
    – Nick
    Commented Feb 15, 2021 at 20:56
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
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
2
  • 1
    If you take a part of my powershell script: $wsl_ip = (wsl hostname -I).trim() netsh interface portproxy add v4tov4 listenport=22 connectport=22 connectaddress=$wsl_ip It will do the same thing as your batch file, but it will automate the step of reading/writing the IP address. You can still run it manually as an administrator. Just call it portproxy.ps1.
    – Nick
    Commented Dec 8, 2021 at 15:13
  • @Nick, I worked your quick trim into the script I use, it also deals with the firewall if you have that need: gist.github.com/axonxorz/612865c294df5b87ced06fc2717c1ffc
    – axon
    Commented Dec 13, 2021 at 17:50
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.

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).

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .