跳到主要内容

WSL 网络与代理

WSL2 的网络问题很容易让人混淆,因为它既不是“和 Windows 完全隔离”,也不是“和 Windows 完全同一台机器”。如果先不把网络关系想清楚,后面你会在 localhost、Windows 主机 IP、WSL IP 之间反复试错。

先分清三个地址

在默认的 WSL2 NAT 网络下,通常会同时出现这三类地址:

  • 127.0.0.1:当前环境自己的回环地址
  • Windows 主机 IP:对 WSL 来说,通常是默认网关地址
  • WSL IP:Linux 发行版自己的虚拟网卡地址

最常见的结论是:

  • Windows 访问 WSL 服务 时,很多服务可以直接用 localhost
  • WSL 访问 Windows 上的代理或服务 时,通常应该使用 Windows 主机 IP
  • 局域网其他机器访问 WSL 时,通常不能直接用 WSL IP,而是要经过 Windows 转发

如何在 WSL 中拿到 Windows 主机 IP

最稳妥的做法是取默认路由的下一跳:

ip route show default | awk '{print $3}'

很多机器上你也会在 /etc/resolv.conf 里看到同一个地址:

grep nameserver /etc/resolv.conf

如果两者一致,通常就说明你拿到的是 Windows 主机在 WSL 虚拟网络中的地址。

临时设置代理

下面以 Windows 上已有本地代理程序,且 HTTP 端口为 7890 为例。

先取 Windows 主机 IP:

export HOST_IP=$(ip route show default | awk '{print $3}')

再设置环境变量:

export http_proxy=http://$HOST_IP:7890
export https_proxy=http://$HOST_IP:7890
export all_proxy=socks5://$HOST_IP:7890

验证是否生效:

curl -I https://www.google.com
git ls-remote https://github.com/git/git.git HEAD

如果你主要是为了 aptpipgit 这类命令走代理,这种临时方式已经足够。

持久化为可开关的函数

与其把代理变量直接硬编码进 ~/.bashrc,我更建议写成显式的开关函数。这样切换网络环境时不会反复手改文件。

把下面内容加入 ~/.bashrc

set_wsl_proxy() {
local host_ip
host_ip=$(ip route show default | awk '{print $3}')

export http_proxy="http://$host_ip:7890"
export https_proxy="http://$host_ip:7890"
export all_proxy="socks5://$host_ip:7890"
}

unset_wsl_proxy() {
unset http_proxy
unset https_proxy
unset all_proxy
}

alias proxy-on='set_wsl_proxy'
alias proxy-off='unset_wsl_proxy'

重新加载 shell:

source ~/.bashrc

之后需要代理时执行:

proxy-on

不需要时执行:

proxy-off

sudo 命令不走代理怎么办

有时普通用户下 curl 能通,但 sudo apt update 还是失败。常见原因是 sudo 没有继承当前环境变量。

可以先临时试一下:

sudo -E apt update

如果你希望 apt 本身固定走代理,也可以单独写配置文件:

sudo nano /etc/apt/apt.conf.d/95proxies

写入:

Acquire::http::Proxy "http://<WINDOWS_HOST_IP>:7890";
Acquire::https::Proxy "http://<WINDOWS_HOST_IP>:7890";

这里的 <WINDOWS_HOST_IP> 需要替换成你当前查到的主机地址。因为 WSL IP 可能变化,这个文件更适合在你的网络环境比较稳定时使用。

什么时候不该继续折腾 WSL 里的代理变量

如果你已经设置了变量,但还是完全不通,优先检查 Windows 侧,而不是继续在 WSL 里盲改:

  1. 代理程序是否真的在 Windows 上运行
  2. 代理程序是否允许局域网或来自其他网卡的连接
  3. 端口号是否真的是你配置的那个
  4. Windows 防火墙是否拦截了对应端口

很多时候问题不在 WSL,而是 Windows 上的代理软件只监听了 127.0.0.1,没有对 WSL 所在的虚拟网卡开放。

最小排错顺序

我自己通常按这个顺序排:

  1. ip route show default
  2. echo $http_proxy
  3. curl -I https://www.google.com
  4. sudo -E apt update
  5. 在 Windows 上确认代理程序监听地址和端口

如果第 3 步都不通,先不要急着改 gitpipconda 的单独配置,因为底层链路大概率还没通。

常见问题

1. 为什么昨天能用,今天不能用

WSL2 重启后虚拟网络可能变化,Windows 主机 IP 也可能随之变化。只要你不是把 IP 写死到文件里,而是每次通过 ip route 动态获取,这类问题会少很多。

2. 为什么浏览器能上网,但命令行不行

浏览器可能使用的是 Windows 自己的代理配置;WSL 命令行不会自动继承这一层,所以仍然需要你在 WSL 里显式设置环境变量。

3. 为什么 localhost:7890 在 WSL 里不通

这是因为 localhost 指向的是 WSL 自己,不是 Windows 主机。默认 NAT 网络下,访问 Windows 上的代理更应该用主机 IP。

关联阅读