野声

Hey, 野声!

谁有天大力气可以拎着自己飞呀
twitter
github

Some network access issues with WSL2

The exams are almost over. This semester, I've been using WSL for development, whether it's Python, C, or React, all developed using VSCode Remote WSL, and the experience has been excellent.

Unless otherwise specified, WSL mentioned in the following content refers to WSL2.

This article roughly contains the following content:

  1. Connecting to the host proxy in WSL2
    Allowing WSL2 to connect to the proxy software on Windows
  2. Accessing WSL2 from the host
    This mainly relies on setting up hosts to resolve a domain name to the IP of WSL2.
  3. Accessing WSL2 from the local area network
    Accessing your Windows host within the local area network, with Windows forwarding ports to WSL2.

There are also a few prerequisite knowledge points:

  1. Windows and WSL2 are considered to be on the same local area network, which is created by Hyper-V.
  2. The network adapter used by WSL2 is 'Default Hyper-V Switch', which gets deleted and recreated every time it restarts, which is why WSL2 does not have a fixed IP.
  3. There are some things specifically made by Microsoft within WSL2:
    1. Requests sent to WSL2's IP will be forwarded to Windows' IP, but this can be inconsistent.

Connecting WSL2 to the Host Proxy#

Actually, I briefly mentioned this issue in my previous ArchWSL configuration article. The general process is as follows:

  1. Obtain the Windows IP
  2. Allow local area network access in the proxy software on Windows
  3. Set the proxy in WSL2

Obtaining the Host IP#

Since WSL2 is implemented using Hyper-V virtual machines, it cannot share the same localhost with Windows, and the IP changes every time it restarts. Currently, you can use the following two commands in WSL to obtain the host's IP:

ip route | grep default | awk '{print $3}'
# or
cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }'

For more details, see: User Experience Changes Between WSL 1 and WSL 2

image.png

Setting the Proxy#

Now that we have the Windows IP, for example, if my proxy software listens on port 7890, I just need to set the proxy link to {windows_ip}:7890.

If you can't connect, please check if your proxy software on Windows allows local area network access.

If you still can't connect, it might be due to the Windows Firewall; I have turned off the firewall.


Thanks to commenters Xing Fang and twinmegami for the command to open the firewall.

Command source: https://github.com/microsoft/WSL/issues/4585

# Directly open the firewall for the `vEthernet (WSL)` network adapter
New-NetFirewallRule -DisplayName "WSL" -Direction Inbound -InterfaceAlias "vEthernet (WSL)" -Action Allow

Also, this must-read article:

The author provides a detailed introduction on how to use a proxy in WSL.

Following the author's idea, I also implemented my own "one-click" proxy setup script:

One-click proxy setup

Using Network Card Proxy#

Of course, there's an even more convenient way to use a network card proxy, without needing to set any HTTP_PROXY, just a network card-level proxy.

I use the TUN mode provided by Clash.

Clash creates a network card that takes over the system's traffic.

image.png

This way, you don't need to set anything; after enabling the network card proxy, all system traffic will go through the network card.

You can also use the following:

  • TAP mode provided by cfw
  • Netch
    Netch is an open-source game acceleration tool for Windows that can implement process proxy similar to SocksCap64, as well as global TUN/TAP proxy like SSTap, and local Socks5, HTTP, and system proxy like Shadowsocks-Windows.

Accessing WSL2 from the Host#

The IP of WSL2 changes, so how can we access WSL2 anytime and anywhere? I've looked at many issues, and the solutions vary.

This problem has troubled me for a long time because when developing in WSL2, sometimes I start some web applications, and then localhost cannot be accessed... The official documentation states that after updating Windows to version 18945, programs listening on 0.0.0.0 can be accessed via localhost in Windows, but during my testing, I found that it often doesn't work; maybe it depends on luck.

Automatically Setting Hosts to Associate with WSL's IP#

The solution is to automatically add the IP to the hosts file whenever WSL updates its IP, resolving it to a domain name of your choice.

Please see:

I found this issue on GitHub: [WSL 2] NIC Bridge mode 🖧 (Has Workaround🔨) #4150, and based on the replies, I got an idea.

The idea is to use Task Scheduler to execute a PowerShell script to do some tasks.

We need to write a script (link) to achieve the functionality we want.

The script's functionality is roughly:

  1. Read the IPs of WSL and Windows
  2. Write the IP and the desired domain name into Windows' hosts file.

This way, you can access both systems using your defined domain name. WSL2 can access win.local because it queries the host for DNS (since the default nameserver of WSL2 points to the Windows host), and the host caches the domain names in the hosts file directly as DNS records.

The key is that we need to use Task Scheduler to execute this script when WSL updates its IP.

The specific steps to run a specific script when WSL updates its IP are as follows:

  1. Save the code from this link to a local file with the .ps1 extension.
  2. Open Event Viewer. You can search for it in Cortana's search box.
  3. Click Windows Logs -> System.
  4. Find the Hyper-V-VmSwitch event and check if there are any events similar to Port ... (Friendly Name: ...) successfully created on switch ... (Friendly Name: WSL)..
  5. Right-click that event and select Attach Task to This Event.
  6. In Action, select Start a Program, fill in powershell in Program, and -file your script's absolute path in Arguments. Adding -WindowStyle Hidden in the arguments can hide the window when executing the script.
  7. Then, find Event Viewer Tasks -> the task you just created in the left resource pane of Task Scheduler, right-click Properties, and check the box: Run with highest privileges.

Check the effect:

Start an HTTP server in WSL:
wsl_http_server.png

Request it from Windows:
curl_wsl.png

Awesome! Success!

You can also use this small tool to achieve similar functionality:
shayne/go-wsl2-host

This is a small tool written in Go that creates a Windows service to automatically update your Windows hosts file with the WSL2 VM IP address.

Allowing Windows to Access Applications Listening Locally in WSL#

Source: https://github.com/shayne/wsl2-hacks
See the Access localhost ports from Windows section in the README

We've already achieved accessing applications listening on 0.0.0.0 in WSL from Windows via a fixed domain name, but what about programs in WSL that listen on 127.0.0.1 by default?

Illustration of listening on 127.0.0.1:
image.png

So we need to forward requests from 0.0.0.0 to 127.0.0.1.

Now:
image.png

You can achieve this by executing two commands in WSL (the two commands inside the curly braces), adding iptables routing rules:

expose_local(){
    sudo sysctl -w net.ipv4.conf.all.route_localnet=1 >/dev/null 2>&1
    sudo iptables -t nat -I PREROUTING -p tcp -j DNAT --to-destination 127.0.0.1
}

Accessing WSL2 from Other Machines in the Windows Local Area Network#

The issue mentioned above, [WSL 2] NIC Bridge mode 🖧 (Has Workaround🔨) #4150, actually addresses the problem of accessing WSL2 from the local area network by forwarding the required ports through the Windows proxy to WSL.

The principle is similar to the two illustrations above.

If you used the script for accessing WSL2 from the host, you can skip this section, as that script includes the content of this section.

The key code is as follows:

If you don't understand, please refer to the comments.

# Get the IPs of Windows and WSL2
$winip = bash.exe -c "ip route | grep default | awk '{print `$3}'"
$wslip = bash.exe -c "hostname -I | awk '{print `$1}'"
$found1 = $winip -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
$found2 = $wslip -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if( !($found1 -and $found2) ){
  # If WSL's IP is not found, exit the script
  echo "The Script Exited, the ip address of WSL 2 cannot be found";
  exit;
}

# Ports you need to map to the local area network
$ports=@(80,443,10000,3000,5000,27701,8080);

# Listening IP, this way allows access from the local area network
$addr='0.0.0.0';
# Listening ports, which are accessed by others
$ports_a = $ports -join ",";

# Remove old firewall rules
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' " | Out-Null

# Allow firewall rules through these ports
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_a -Action Allow -Protocol TCP"  | Out-Null
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_a -Action Allow -Protocol TCP"  | Out-Null

# Use portproxy to forward ports from Windows to WSL
# https://docs.microsoft.com/en-us/windows-server/networking/technologies/netsh/netsh-interface-portproxy
for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr"  | Out-Null
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$wslip"  | Out-Null
}

In lines 2 and 3 of the code, `$ means to prevent PowerShell from escaping $ when executing the script.
If you encounter errors when executing the code, you can try adding a \ before `$: \`$.

In PowerShell syntax, @() means an array; this script iterates through the array of ports you set to expose to the local area network and uses portproxy to reverse proxy Windows' ports to WSL.

One-click Proxy Setup#

First, here’s the effect:
image.png
And it can also set the proxy for git and ssh simultaneously.

See the code:
https://github.com/bytemain/dotfiles/blob/master/ubuntu_wsl/zshrc

Key functions include proxy, unpro, getIp, proxy_git, proxy_npm, etc.

This way, when we execute the proxy command, we can connect to the host's proxy with one click.

If you find it useful, don't forget to give it a star, thank you!

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.