28

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,263
  • 2
  • 12
  • 15
  • I just took a look through this [GitHub issue thread](https://github.com/microsoft/WSL/issues/4210) 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 Sep 01 '20 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 Sep 01 '20 at 17:11

9 Answers9

24

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,263
  • 2
  • 12
  • 15
  • Why don't we use `set v4tov4` instead of `add v4tov4`? – Hunkoys Oct 29 '21 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 Nov 02 '21 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 https://superuser.com/questions/1685689/update-windows-host-file-with-wsl-ip-on-start-up – Ikechukwu Nov 05 '21 at 07: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 Nov 10 '21 at 05:49
  • 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 Jun 16 '22 at 09:20
  • You can skip `sudo` by using this instead of your first line: `wsl.exe -u root -e /etc/init.d/ssh start` (or if you use systemd, `wsl.exe -u root -e systemctl start sshd.service`) – Arle Camille Jan 29 '23 at 14:19
  • thanks @Nick this was and worked for me – lux Feb 11 '23 at 11:09
10

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.

  • 2
    This is perfect. This solved my problem. Thank you. – Rahmani Jul 17 '22 at 17:18
  • How do you run a `sudo` command on bootup inside WSL? For `netsh` I found https://social.technet.microsoft.com/Forums/office/en-US/cdf96a00-f0d1-4ba1-b887-3f0035fb01af/running-netsh-commands-on-start-up?forum=win10itprogeneral – chx Sep 15 '22 at 18:28
  • Hm, It just works inside my WSL) And `netsh` works on windows too (if you launch cmd like an Admin user) – Artemius Pompilius Sep 18 '22 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 Oct 25 '22 at 06:54
3

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

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
  • Nice, thanks for sharing. You might be able to get rid of the regex if you do some `cut`ting 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 Feb 15 '21 at 20:56
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
    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 Dec 08 '21 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: https://gist.github.com/axonxorz/612865c294df5b87ced06fc2717c1ffc – axon Dec 13 '21 at 17:50
1

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
  • 172
  • 1
  • 1
  • 12
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).

dan
  • 131
  • 4
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
-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