WSL2連線到宿主Windows程式的網路代理設定

overlordsnyt發表於2022-11-24

WSL2想要連上宿主機Windows裡設定的網路代理埠很是蛋疼。

前置條件

PS C:\Users\overlord> wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-20.04    Running         2

獲取Host和WSL的ip

首先我們需要獲取WSL2的動態IP,再每次Windows系統重啟之後,它會動態給WSL2分配一個新IP。

所以可以寫個指令碼displayip.sh,來動態獲取。

#!/bin/bash
echo "Host ip: $(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')"
echo "WSL client ip: $(hostname -I | awk '{print $1}')"

執行可檢視當前Windows host主機ip、WSL的ip。

?  chmod +x displayip.sh
?  ./displayip.sh
Host ip: 172.20.96.1
WSL client ip: 172.20.99.149

Windows使用的代理程式

我用的Clash for Windows支援http和socks5代理。

代理埠為1081。

錯誤嘗試

Windows Defender 裡新增入站規則

管理員許可權ps執行:

New-NetFirewallRule -DisplayName "WSL" -Direction Inbound  -InterfaceAlias "vEthernet (WSL)"  -Action Allow

會在Windows Defender 防火牆裡增加開啟一條入站規則“WSL”。這條規則允許全部介面名為 vEthernet (WSL) ——也就是WSL2的入站連線。

在WSL的bash裡ping宿主主機,測試連通:

?  ping 172.20.96.1 -c 4
PING 172.20.96.1 (172.20.96.1) 56(84) bytes of data.
64 bytes from 172.20.96.1: icmp_seq=1 ttl=128 time=0.373 ms
64 bytes from 172.20.96.1: icmp_seq=2 ttl=128 time=0.350 ms
64 bytes from 172.20.96.1: icmp_seq=3 ttl=128 time=0.398 ms
64 bytes from 172.20.96.1: icmp_seq=4 ttl=128 time=0.380 ms

--- 172.20.96.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3114ms
rtt min/avg/max/mdev = 0.350/0.375/0.398/0.017 ms

但是此時透過宿主機的ip並不能訪問到代理軟體使用的1081介面。用telnet測試介面可得如下結果。

?  telnet 172.20.96.1 1081
Trying 172.20.96.1...

對1081的連線請求會一直停留在嘗試連線的步驟上。

入站規則的用處

增加入站規則其實可以用來讓WSL2子系統訪問Windows宿主機起的http服務。

例如我用node在Windows宿主機的5000埠起了一個node的server。

PS D:\Codes\nodejs16\tsrctutorial> pnpm preview

> tsrctutorial@0.0.0 preview D:\Codes\nodejs16\tsrctutorial
> vite preview

  > Network:  http://172.24.0.1:5000/
  > Network:  http://192.168.56.1:5000/
  > Network:  http://192.168.3.130:5000/
  > Local:    http://localhost:5000/
  > Network:  http://172.20.96.1:5000/

WSL2內用telnet測試的結果就會變成:

?  telnet 172.20.96.1 5000
Trying 172.20.96.1...
Connected to 172.20.96.1.
Escape character is '^]'.

還可以用curl測試。

?  curl http://172.20.96.1:5000
<!DOCTYPE html>
<html lang="en">
  ...
</html>

從Windows宿主機訪問WSL2起的服務

新版WSL2裡系統已經對映,直接用localhost即可訪問。

比如在WLS2裡起個簡單的node伺服器,在Windows的瀏覽器中用 localhost:port 即可訪問。

Windows宿主中訪問WSL2的http服務

猜想

可能是Windows Defender的入站規則只對它認識的連線協議放行,而clash的混合http和socks5的協議不在它認識的範圍內。

正確方案

配置 Windows Defender

在“高階安全 Windows Defender 防火牆” -- 屬性,選項卡“公用配置檔案”,狀態組裡“受保護的網路連線:”右邊的“自定義”按鈕,開啟保護的網路連線視窗取消勾選“vEthernet (WSL)”核取方塊。

WSL2訪問Windows正確配置圖

不過配置的缺點是每次重啟必須重新配置。原因是vEthernet這個服務必須每次登入WSL2才會出現,Windows Defender 會預設將其納入保護,我還沒有找到Windows Defender預設不保護它的方法。

歡迎留言交流重啟後也能生效的WSL和宿主訪問的配置方法。

測試連線

telnet

?  telnet 172.20.96.1 1081
Trying 172.20.96.1...
Connected to 172.20.96.1.
Escape character is '^]'.

curl

?  curl -x "socks5://172.20.96.1:1081" https://www.google.com
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="https://www.google.com.hk/url?sa=p&amp;hl=zh-CN&amp;pref=hkredirect&amp;pval=yes&amp;q=https://www.google.com.hk/&amp;ust=1645782940260650&amp;usg=AOvVaw0PyWu1rzhLahRjGxgRdqPH">here</A>.
</BODY></HTML>

使用場景

先配置好Windows Defender的高階安全,再把宿主機的代理埠設定為WSL2系統代理,或者git的代理。

clash或者其他的代理程式需要開放區域網訪問(Allow LAN)。

WSL2裡設定系統代理部分,我使用一個bash指令碼來做這樣的配置。

socks5協議的proxy.sh指令碼

#!/bin/bash
hostip=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
wslip=$(hostname -I | awk '{print $1}')
port=1081
PROXY_SOCKS="socks5://${hostip}:${port}"

function display() {
    echo "Host ip: ${hostip}"
    echo "WSL client ip: ${wslip}"
    echo "current PROXY: ${PROXY_SOCKS}"
}

function set_proxy() {
    export http_proxy="${PROXY_SOCKS}"
    export https_proxy="${PROXY_SOCKS}"
    echo "env http/https proxy set."
}

function unset_proxy() {
    unset http_proxy
    unset https_proxy
    echo "env proxy unset."
}

function set_git_proxy() {
    git config --global http.proxy "${PROXY_SOCKS}"
    git config --global https.proxy "${PROXY_SOCKS}"
    echo "git config proxy set."
}

function unset_git_proxy() {
    git config --global --unset http.proxy
    git config --global --unset https.proxy
    echo "git conffig proxy unset."
}

if [ "$1" = "display" ]; then
    display
elif [ "$1" = "set" ]; then
    set_proxy
elif [ "$1" = "unset" ]; then
    unset_proxy
elif [ "$1" = "setgit" ]; then
    set_git_proxy
elif [ "$1" = "ungit" ]; then
    unset_git_proxy
else
    echo "incorrect arguments."
fi

第4行port=你Windows代理程式設定的代理埠。

儲存後記得chmod +x proxy.sh賦予執行許可權。

用法

?  ./proxy.sh display
Host ip: 172.20.96.1
WSL client ip: 172.20.99.149
current PROXY: socks5://172.20.96.1:1081
?  ./proxy.sh set
env http/https proxy set.
?  ./proxy.sh unset
env proxy unset.
?  ./proxy.sh setgit
git config proxy set.
?  ./proxy.sh ungit
git conffig proxy unset.

set之後可以curl或wget訪問需要代理連上的地址;setgit之後拉github上連線不上的包。

參考資料

Windows Defender Firewall blocks access from WSL2 · Issue #4139 · microsoft/WSL

Add "allow" rule to Windows firewall for WSL2 network · Issue #4585 · microsoft/WSL

Protecting X410 Public Access for WSL2 via Windows Defender Firewall

[WSL 2] NIC Bridge mode ? (Has TCP Workaround?) · Issue #4150 · microsoft/WSL

WSL2 中訪問宿主機 Windows 的代理 - ZingLix Blog

相關文章