Skip to content

Instantly share code, notes, and snippets.

@the-moog
Created June 6, 2023 19:48
Show Gist options
  • Save the-moog/e7a1b5150ce9017309afbcf91848e622 to your computer and use it in GitHub Desktop.
Save the-moog/e7a1b5150ce9017309afbcf91848e622 to your computer and use it in GitHub Desktop.
Make WSL2 networking play nicely with DNAT/dest-NAT/portproxy and/or give WSL a static IP address

Port forwarding from host (aka WSL2 Static IP)

I have read many articles about WSL networking that try and sort DNAT and static IP and none that I've found seem to work or only work for some. So here is my attempt....

It seems getting port forwarding to work in WSL is not easy. I think WSL has unusual hidden network mechanisms and that confuses the matter. Also when WSL boots the WSL init mechanism gives the guest a random address, which is no use for static NAT

This worked for me. Try it yourself and let me know if I've finally cracked it !!

0: Understand currently setup.

Find WSL the subnet matched to a Hyper-V Virtual Ethernet/Virtual Switch IP subnet, we need to find the name of that switch later.

1: Enable routing in the registry

C:\> Get-ItemProperty -path HKLM:\system\CurrentControlSet\Services\Tcpip\Parameters -name IPEnablerouter  
C:\> Set-ItemProperty -path HKLM:\system\CurrentControlset\services\Tcpip\parameters -name IPEnablerouter -Value 1

2: Disable forward ports

Edit C:\users\\<username>\.wslconfig

Turn off default setting that (fails) to forward ports to WSL unless they come from localhost.

[wsl2]
LocalHostForwarding=false  

=>> NOTE: LocalHostForwarding = 'true' seems to block all attempts to alter NAT

3: Setup DestNAT

.... but on a different IP, otherwise WSL may revert settings!

C:\> Get-NetAdapter

=>> Note the name, something like "vEthernet (WSL)"

C:\> Get-NetIPAddress

=>> Find the IP address with the same interface name "vEthernet (WSL)" You should see something like 172.x.y.z/m =>> Choose a subnet so there are no collisions, I will use 192.168.160.0/28.

C:\> New-NetIPAddress -IPAddress 192.168.160.1 -PrefixLength 28 -InterfaceAlias "vEthernet (WSL)"
C:\> Get-NetNatExternalAddress

=>> Note NAT "Name" attached to the external IP for me that was = "wsl-nat"

C:\> New-NetNat -name "wsl-nat" -InternalIPInterfaceAddressPrefix "192.168.160.1/28"
C:\> Add-NetNatStaticMapping -ExternalIPAddress "0.0.0.0/0" -ExternalPort 22 -InternalIPAddress 192.168.160.10 -InternalPort 22 -NatName "wsl-nat" -Protocol tcp

=>> Like most of PowerShell this will either work or return a meaningless large red box message which may as well read "The computer says NO!"

4: Firewall

(Perhaps check this sooner)
4a: Ensure the firewall in Windows allows incoming connections on port 22 on the external interface from an appropriate source network
4b: Ensure you don't have another ssh service already running on port 22 on the external interface

C:\> Get-NetTCPConnection -LocalPort 22  

=>> This should return no existing listening ports. If this is not the case find the process id - it will most likely be another running WSL or an open-ssh Windows System service. Shutdown WSL and/or stop the service (or use a port other than 22)

4: Configure Linux (WSL)

user@WSL:~$ sudo ip address list

=>> You should see an address like 172.x.y.z as seen above on device eth0

user@WSL:~$ sudo ip address add 192.168.160.10/28 dev eth0  

=>> DONE!

You should now be able to access ssh from another device on the same routable IP network.

5: Make WSL permanent

As I am on Ubuntu-WSL-23.04 I will use netplan.
NOTE: The DNS addresses come from whatever Windows has or you could use one or more of 8.8.8.8, 8.8.4.4, 1.1.1.[1|2|3]

C:\> Get-DnsClientServerAddress

Edit /etc/netplan/01-netcfg.yaml

# /etc/netplan/01-netcfg.yaml  
network:  
  ethernets:  
    eth0:  
      dhcp4: false  
      optional: false  
      addresses: [192.168.160.10/28]  
      nameservers:  
        addresses: \[1.1.1.1,8.8.8.8]  
        search: [local,localdomain,lan]  
      dhcp6: false  
  version: 2  
  renderer: networkd  

=>> Final test: Logout, shutdown WSL and re-start and see if it's still works!

@Spartan1776
Copy link

Spartan1776 commented Apr 21, 2024

Hey @the-moog, thank you so much for posting this. After trying networkingMode=mirrored in the .wslconfig file, bridging the network adapters, and third-party proxy configurations, this was a relief to come across.

I stepped through the instructions above but encountered some issues I was hoping you could help me with. For context, though:

  1. I'm using WSL2 with Ubuntu 22.04.03 LTS.
  2. I used a different subnet (192.168.3.1/24) for integration with the WSL vEthernet adapter.
  3. I'm couldn't figure out where the name "wsl-nat" came from, but I used "WSL-NAT" and everything seemed to work okay.
  4. When I SSH to the WSL box, I'm targeting the LAN IP of the host machine WSL is running on; it worked the first time, haha...

After stepping your configuration instructions (thank you again), I had to modify the Windows Firewall Inbound Rules to allow TCP traffic on port 22 from my network. After that, I was able to SSH to my WSL box from my LAN with no issues--perfect!!

I went ahead and copied the 01-netcfg.yaml file, then exited WSL, ran wsl.exe --shutdown in PowerShell, and restarted my machine. Unfortunately, after restarting, I realized I was having issues with the 01-netcfg.yaml file; it didn't apply on start, so I had to run sudo netplan apply, but that caused the nameservers > addresses block to fail due to the backslash. I removed the backslash, but I don't think the config persists across reboot. ifconfig only shows the 172.x.x.x address, though sudo ip address list shows both the "172.x.x.x" address and "192.168.3.x". Unless I run sudo netplan apply, though, I'm unable to reach the WSL box via SSH at all, even from the local machine hosting the WSL box (more on that later). However, I'm not too concerned about this, as I can probably just implement a patch of sorts.

The bigger issue I'm having is an inability to SSH to my WSL box now from outside of the local machine hosting the WSL box. I double-checked the configuration steps you provided to make sure everything had persisted across the restart. Turns out, New-NetIPAddress -IPAddress 192.168.160.1 -PrefixLength 28 -InterfaceAlias "vEthernet (WSL)" does not persist for me. Maybe this is due to the fact that I don't know where "wsl-nat" came from? I'm not quite sure. Get-NetIPAddress identifies "vEthernet (WSL (Hyper-V firewall))" as the InterfaceAlias for addresses pertaining to WSL, though, so that's what I used in place of "wsl-nat", and it takes the command just fine. If I try and use "wsl-nat", it pitches a fit.

Despite restoring the New-NetIPAddress config and checking to make sure everything else was still set up correctly, I'm unable to SSH to my WSL box. I can SSH from the machine hosting the WSL box to the WSL box via the 192.168.3.x IP, but not from outside of the local machine. I've fully disabled all firewall profiles on Windows and still can't SSH to the WSL box, so I want to say that's not the issue. UFW is active on Ubuntu, but SSH is permitted, and again, SSH works from the local machine to the WSL box.

Any thoughts on this?

@Spartan1776
Copy link

Okay, I don't know why this is working, but this is working:

  1. Add the New-NetIPAddress -IPAddress 192.168.160.1 -PrefixLength 28 -InterfaceAlias "vEthernet (WSL)" command after reboot.
  2. DON'T run sudo netplan apply

SSH to the WSL box --> it works. I have no idea why this wasn't working before...

Thank you so much, @the-moog! You are an absolute lifesaver.

@Spartan1776
Copy link

@the-moog Did you have any thoughts on why New-NetIPAddress -IPAddress 192.168.160.1 -PrefixLength 28 -InterfaceAlias "vEthernet (WSL)" won't persist for me? If not, no worries, I'll just throw it in a script or something and have it execute on startup.

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment