Skip to content

Instantly share code, notes, and snippets.

@nivranaitsirhc
Last active January 10, 2024 14:39
Show Gist options
  • Save nivranaitsirhc/e57a05de021842d93b0bc084433eb725 to your computer and use it in GitHub Desktop.
Save nivranaitsirhc/e57a05de021842d93b0bc084433eb725 to your computer and use it in GitHub Desktop.
WSL2 ~ WINHOST (windows localhost from WSL)

Update: September 19, 2023 - A New Experimental Feature was added

A new experimental feature was added by Microsoft that will resolve this issue in the future. You will need to update to Windows Insider Build for Windows 10 or latest Windows 11 and update WSL. More info here.

The WSL2 localhost issue?

On WSL1 we can access windows services running on localhost ports directly, but when WSL2 was introduced things started breaking. One of those issue is access to windows localhost ports 1

To understand this issue we need to learn what is WSL2:

  • WSL2 is a Managed Virtual Machine Running on Windows contrary to WSL1.
  • Windows implements a virtual NIC for WSL2 thus the distro's host ip is different from the Windows host ip. 2

To put into perspective WSL2 is just another computer connected in a private network with our host computer. The workaround to simulate to some extent the WSL1 behaviour we need to:

  • ditch the localhost dns name, as this would be targeting the WSL2 distro and instead point to another DNS Name. (eg. winhost)
  • dynamically map our new DNS Name to the windows host ip.
  • setup Windows firewall configurations to allow connection from WSL2. (Optional, but mandatory to certain Apps/Services).

Note: We are only addressing the WSL2_DISTRO -> Windows_HOST
Note: Accessing WSL2 App ports is accessable on Windows localhost. 2
Note: Accessing WSL2 App ports from the local-area-network of the Windows host is not covered here, and requires manual ipv4-to-ipv4 proxy configuration on the Windows Host. I recommend David Bombal's WSL2 Networking

  1. Distro Side (WSL2)
    • capture the windows host-ip during wsl boot
    • assgin a new dns name to it
    • register the dns record to distro's host file
    • make it dynamic and deploy-and-forget
  2. Windows Side
    • Manage firewall permissions

Requirements:

This method requires the latest WSL as it now supports systemd natively.

WSL version: 1.1.6.0
Kernel version: 5.15.90.1

[Distro Side] -- Preperation

Create a bash script that will handle the updating of the hosts file. Save the script and place it at anywhere you like. Make sure to set its execution attribute and permissions. (recomended location: /etc)

(eg. /etc/wsl.winhost.sh)

#!/usr/bin/bash
# wsl get windows host ip
winhost=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
if [ ! -n "$(grep -P "[[:space:]]winhost" /etc/hosts)" ]; then
	printf "%s\t%s\n" "$winhost" "winhost" | tee -a "/etc/hosts"
fi
sudo chmod +x /etc/wsl.winhost.sh

[Distro Side] -- Method-A: WSL Boot Command

Utilize the /etc/wsl.conf boot command config and run wsl.winhost.sh at boot.

/etc/wsl.conf [ref.]

[boot]
command="bash /etc/wsl.winhost.sh"

Steps:

1. add the command inside the wsl.conf file to execute the script. e.g. command="bash /etc/wsl.winhost.sh"

2. reboot wsl using wsl --shutdown or wsl --terminate <distro_name>.

3. verify.

  • cat /etc/hosts look for winhost
  • run a node server and access your app (eg. https://winhost:3000) Note: This might fail if windows is blocking the port for 3000. See Windows Firewall Configuration below..

4. Enjoy & Relax!

[Distro Side] -- Method-B: As a Service (systemd)

Utilize the systemd native support of WSL2 by enabling it on /etc/wsl.conf and running the wsl.winhost.sh as a service.

/etc/wsl.conf [ref.]

[boot]
systemd=true

Steps:

1. create a new service file at /etc/systemd/system/ and name it anything you like (eg. winhost.service)

2. paste the code below to the service file. sudo vim /etc/systemd/system/winhost.service

Useful reference for Systemd [ref]

[Unit]
Description=Map windows localhost IP to dns winhost

[Service]
Type=oneshot
ExecStart=/etc/wsl.winhost.sh
RemainAfterExit=false

[Install]
WantedBy=multi-user.target 
  • set the permissions
sudo chmod 640 /etc/systemd/system/winhost.service
  • check if the service config is correct
systemctl status winhost

3. Starting the winhost Service

  • reload the systemctl daemon.
sudo systemctl daemon-reload
  • enable the service at boot
sudo systemctl enable winhost
  • start the Service
sudo systemctl start winhost

4. Verifying the Service

  • Check the status for error
sudo systemctl status winhost
○ winhost.service - Map windows localhost IP to dns winhost
     Loaded: loaded (/etc/systemd/system/winhost.service; enabled; vendor preset: enabled)
     Active: inactive (dead)
Apr 04 15:49:06 win10 systemd[1]: Starting Map windows localhost IP to dns winhost...
Apr 04 15:49:06 win10 wsl.winhost.sh[513]: 172.26.32.1        winhost
Apr 04 15:49:06 win10 systemd[1]: winhost.service: Deactivated successfully.
Apr 04 15:49:06 win10 systemd[1]: Finished Map windows localhost IP to dns winhost.
  • check if winhost is enabled at boot, it should say enabled at STATE
sudo systemctl list-unit-files --type=service
UNIT FILE                                              STATE           VENDOR PRESET
winhost.service                                        enabled         enabled
  • cat /etc/hosts look for winhost
  • run a node server and access your app (eg. https://winhost:3000) Note: This might fail if windows is blocking the port for 3000. See Windows Firewall Configuration below..

5. Enjoy & Relax!

[Windows Side] -- Configuring Windows Firewall

The Allow All Connection on this NIC method 3

Create a new firewall rule that allows all connection via the NIC "vEthernet (WSL)", allowing unfiltered netowrking between WSL VM and Windows Host.
Note: Run this script only once as it will create a new firewall rule with the same name.

# Requires -RunAsAdministrator

# Inbound Rule
New-NetFirewallRule -DisplayName "WSL" -Direction Inbound   -InterfaceAlias "vEthernet (WSL)"  -Action Allow
# Outbund Rule
New-NetFirewallRule -DisplayName "WSL" -Direction Outbound  -InterfaceAlias "vEthernet (WSL)"  -Action Allow

To modify the rule open the Windows Defender Firewall with Advanced Security Settings or run wf.msc. Look for WSL in "Inbound" and "Outbound" tab.

The Disable Firewall for this NIC method 4

This will disable the firewall for the NIC "vEthernet (WSL)".
Note: Windows Defender will nag you about this.

# Requires -RunAsAdministrator

Set-NetFirewallProfile -Profile Public,Private -DisabledInterfaceAliases "vEthernet (WSL)"

To revert run this command.

# Requires -RunAsAdministrator
Set-NetFirewallProfile -Profile Public,Private -DisabledInterfaceAliases @()

Footnotes

  1. WSL2 localhost Issue #4619

  2. WSL Networking 2

  3. Allow Firewall for WSL2 by dansanduleac

  4. Allow Firewall for WSL2 by kenvix

[Unit]
Description=Map Windows host IP to Local Dynamic Name Server (DNS) name winhost
[Service]
Type=oneshot
ExecStart=/etc/wsl.winhost.sh
RemainAfterExit=false
[Install]
WantedBy=multi-user.target
#!/usr/bin/bash
# wsl get windows host ip
winhost=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
if [ ! -n "$(grep -P "[[:space:]]winhost" /etc/hosts)" ]; then
printf "%s\t%s\n" "$winhost" "winhost" | tee -a "/etc/hosts"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment