> /technology/

为 Raspberry Pi Zero 2 W 配置 USB SSH 与 Linux 主机代理

记录 Raspberry Pi Zero 2 W 通过 USB gadget 建立稳定 SSH 管理链路,并使用 Linux 主机上的 Clash/Mihomo 访问外网的配置流程。

  • Raspberry Pi
  • Zero 2 W
  • USB Gadget
  • SSH
  • Linux Networking
  • Clash

本文记录一次 Raspberry Pi Zero 2 W 实验环境中的最终配置。Zero 2 W 的 wlan0 用于 IBSS 自组网,USB 数据线同时提供一条独立的以太网管理链路。树莓派通过这条链路使用 SSH 登录,并将需要访问公网的 HTTP、HTTPS、Git 和 APT 请求交给 Linux 主机上的 Clash/Mihomo 代理。

1. 环境与目标

实验环境

设备 系统与内核 作用
Linux 主机 Ubuntu 24.04.4 LTS,Linux 7.0.0-28-generic,x86_64 USB 网络对端、SSH 客户端、Clash/Mihomo 代理
Raspberry Pi Zero 2 W Debian GNU/Linux 13.5,Linux 6.18.34+rpt-rpi-v8,aarch64 FANET/IBSS 节点、USB 网络客户端

最终地址规划如下:

Linux 主机 USB 网卡  192.168.7.3/24
Raspberry Pi usb0     192.168.7.2/24
IBSS wlan0            10.10.0.3/32
Clash/Mihomo mixed    192.168.7.3:7897
Clash/Mihomo DNS      192.168.7.3:7854

USB 网卡名称由 Linux 根据设备 MAC 地址生成,不应写死为某个名称。本文实测时主机名称为 enx9a1511acea67;重新插拔或更换 USB 设备后,应先通过 ip -br link 确认实际名称。

方案边界

这套方案是“静态 USB 管理网段 + 显式应用代理”:

  • USB 链路只负责 SSH、管理和访问主机代理端口。
  • usb0 不设置默认网关,Zero 2 W 原有 IBSS 路由不受影响。
  • 普通公网 HTTP/HTTPS 请求通过主机的 192.168.7.3:7897 代理。
  • 10.0.0.0/8、192.168.0.0/16、172.16.0.0/12 和 USB 管理网段直连,不进入代理。

这不是完整的三层透明网关。若需要让树莓派的所有未匹配流量都经主机转发,还需要额外配置 Linux 转发、NAT、DNS 和防火墙规则;本实验没有采用这种方式。

2. 先确认 USB 网络设备

将 Zero 2 W 通过支持数据传输的 USB 线连接到 Linux 主机后,在主机执行:

ip -br link
ip -br addr

找到新出现的 enx… 接口。树莓派一侧应出现 usb0:

ssh pi_2w 'ip -br addr show usb0'

如果主机没有新接口,优先检查 USB 线是否支持数据传输、Zero 2 W 是否启用了 USB gadget 网络,以及内核日志:

dmesg | tail -50

3. 配置主机 USB 静态地址

使用 NetworkManager 创建持久化连接。将下面的 USB_IF 替换为实际的 enx… 名称:

USB_IF=enx9a1511acea67

sudo nmcli connection add type ethernet \
  ifname "$USB_IF" \
  con-name rpi-zero2w-usb \
  ipv4.method manual \
  ipv4.addresses 192.168.7.3/24 \
  ipv4.never-default yes \
  ipv6.method disabled

sudo nmcli connection up rpi-zero2w-usb

如果连接配置已经存在,使用修改命令而不是重复创建:

sudo nmcli connection modify rpi-zero2w-usb \
  connection.interface-name "$USB_IF" \
  ipv4.method manual \
  ipv4.addresses 192.168.7.3/24 \
  ipv4.never-default yes \
  ipv6.method disabled
sudo nmcli connection up rpi-zero2w-usb

ipv4.never-default yes 很重要。它确保 USB 管理链路不会抢走 Linux 主机访问校园网或互联网的默认路由。

检查结果:

ip addr show "$USB_IF"
ip route show dev "$USB_IF"

预期至少包含:

192.168.7.3/24
192.168.7.0/24 dev enx... scope link

4. 配置 Zero 2 W 的 usb0

在树莓派上创建持久化 NetworkManager 连接:

sudo nmcli connection add type ethernet \
  ifname usb0 \
  con-name usb0-static \
  ipv4.method manual \
  ipv4.addresses 192.168.7.2/24 \
  ipv4.never-default yes \
  ipv6.method disabled

sudo nmcli connection up usb0-static

已有连接时使用:

sudo nmcli connection modify usb0-static \
  connection.interface-name usb0 \
  ipv4.method manual \
  ipv4.addresses 192.168.7.2/24 \
  ipv4.never-default yes \
  ipv6.method disabled
sudo nmcli connection up usb0-static

这里不设置网关。USB 网段只需要一条二层直连路由,树莓派访问主机代理时直接使用 192.168.7.3。

验证链路:

ping -c 3 192.168.7.2  # Linux 主机执行
ping -c 3 192.168.7.3  # Zero 2 W 执行

5. 配置 SSH 密钥登录

在 Linux 主机确认已有密钥;没有时生成一对 Ed25519 密钥:

test -f ~/.ssh/id_ed25519.pub || ssh-keygen -t ed25519

将公钥安装到树莓派。首次安装可以使用树莓派账户密码:

ssh-copy-id -i ~/.ssh/id_ed25519.pub lixueyang@192.168.7.2

树莓派端的权限应为:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

主机端为 Zero 2 W 增加 SSH 别名:

Host pi_2w
    HostName 192.168.7.2
    User lixueyang
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

使用 IdentitiesOnly yes 可以避免 SSH 依次尝试大量无关密钥。测试必须使用批处理模式,确保不会回退到密码:

ssh -o BatchMode=yes pi_2w 'hostname; ip -br addr show usb0'

6. 验证 Linux 主机上的代理

主机上的 Clash/Mihomo 应监听可被 USB 网段访问的地址。实测使用 mixed 端口 7897,而不是仅供控制面使用的管理端口 9097:

ss -ltnp | rg ':(7897|7854|9097)\b'

预期代理端口类似:

*:7897       mixed HTTP/SOCKS proxy
*:7854       DNS service
10.112.50.238:9097  management API

代理监听端口和管理端口的含义不同,树莓派应使用 7897。端口还必须允许来自 192.168.7.0/24 的连接,不能只监听 127.0.0.1。

7. 在树莓派上持久化显式代理

创建 /etc/profile.d/linux-host-proxy.sh:

sudo install -m 0644 /dev/stdin /etc/profile.d/linux-host-proxy.sh <<'EOF'
export http_proxy="http://192.168.7.3:7897"
export https_proxy="http://192.168.7.3:7897"
export all_proxy="socks5h://192.168.7.3:7897"

export HTTP_PROXY="$http_proxy"
export HTTPS_PROXY="$https_proxy"
export ALL_PROXY="$all_proxy"

export no_proxy="localhost,127.0.0.1,::1,192.168.7.2,192.168.7.3,192.168.7.0/24,10.10.0.1,10.10.0.2,10.10.0.3,10.10.0.4,10.0.0.0/8,192.168.0.0/16,172.16.0.0/12,.local"
export NO_PROXY="$no_proxy"
EOF

重新登录,或在当前 shell 中加载配置:

. /etc/profile.d/linux-host-proxy.sh
env | rg -i '^(http|https|all|no)_proxy='

no_proxy 同时列出具体的 IBSS 节点地址和私有网段。部分旧程序对 CIDR 的支持不完整,因此保留具体地址可以提高兼容性。

8. 为 APT 和 Git 配置代理

APT 不一定读取交互式 shell 的代理变量。使用自动判断脚本,让私有地址直连、其他地址使用主机代理:

sudo install -m 0755 /dev/stdin /usr/local/bin/linux-host-apt-proxy <<'EOF'
#!/bin/sh
url="$1"
host="$(printf '%s\n' "$url" | sed -e 's#^[^:]*://##' -e 's#/.*$##' -e 's/:[0-9][0-9]*$//')"

case "$host" in
  localhost|127.*|::1|192.168.7.2|192.168.7.3|10.*|192.168.*|172.16.*|172.17.*|172.18.*|172.19.*|172.20.*|172.21.*|172.22.*|172.23.*|172.24.*|172.25.*|172.26.*|172.27.*|172.28.*|172.29.*|172.30.*|172.31.*)
    echo DIRECT
    ;;
  *)
    echo http://192.168.7.3:7897
    ;;
esac
EOF

sudo install -m 0644 /dev/stdin /etc/apt/apt.conf.d/95linux-host-proxy <<'EOF'
Acquire::http::Proxy-Auto-Detect "/usr/local/bin/linux-host-apt-proxy";
Acquire::https::Proxy-Auto-Detect "/usr/local/bin/linux-host-apt-proxy";
EOF

sudo apt-get update

Git 使用 HTTPS 访问 GitHub 时可以只为 GitHub 设置代理,避免把校园网或实验网中的其他 Git 服务全部送入代理:

git config --global http.https://github.com.proxy http://192.168.7.3:7897
git config --global http.noProxy 'localhost,127.0.0.1,192.168.7.2,192.168.7.3,10.0.0.0/8,192.168.0.0/16,172.16.0.0/12'

如果使用 GitHub SSH 协议,则代理环境变量不会自动作用于 git@github.com。本实验使用已配置的 GitHub SSH 密钥,网络可用的树莓派可以直接执行:

git -C ~/code/fanet fetch origin --dry-run

不要把密码、访问令牌、私钥或包含明文凭据的 .git-credentials 提交到公开仓库。

9. 持久化检查与重启验证

NetworkManager 连接配置会在重启后自动恢复:

nmcli connection show rpi-zero2w-usb  # Linux 主机
ssh pi_2w 'nmcli connection show usb0-static'

树莓派重启后检查:

ssh pi_2w 'ip -br addr show usb0; ip route; systemctl is-system-running'

重点确认:

  • usb0 仍为 192.168.7.2/24。
  • USB 路由只有 192.168.7.0/24,没有通过 USB 生成默认路由。
  • ssh -o BatchMode=yes pi_2w 可以直接登录。
  • 新开的登录 shell 能读取 /etc/profile.d/linux-host-proxy.sh。
  • sudo apt-get update 和 git ls-remote https://github.com/ForRMByishi/fanet.git HEAD 成功。
  • 10.0.0.0/8、192.168.0.0/16 等私有地址按设计直连。

10. 实验中的坑点

USB 网卡名称不是固定的

Linux 的 USB gadget 网卡通常命名为 enx 加 MAC 地址。不能假设每次都是同一个名称;应使用 ip -br link 检查,并让 NetworkManager 连接绑定实际接口。

USB 链路不应随意设置默认网关

USB 网络的目标是管理和访问主机代理。设置 192.168.7.3 为树莓派默认网关会改变路由行为,可能干扰 IBSS 的 OLSRv2 路由,也可能导致校园网地址绕行。最终配置使用 ipv4.never-default yes,只保留 USB 直连路由。

SSH 密钥存在不等于 SSH 会使用它

当本机有多把密钥时,SSH 可能尝试错误的密钥或超过服务端尝试次数。树莓派端要检查 authorized_keys 权限,主机端要在 ~/.ssh/config 指定正确的 IdentityFile 和 IdentitiesOnly yes,再用 BatchMode=yes 验证无密码登录。

Clash 的管理端口不是代理端口

9097 是控制面 API 端口,不能当作普通 HTTP/SOCKS 代理使用。树莓派应连接主机的 mixed 代理端口 7897;端口必须监听在 USB 网段可达的地址上,不能只监听 127.0.0.1。

no_proxy 需要覆盖整个私有地址范围

只排除 192.168.7.2 不够。校园网、IBSS 和 Docker 等本地服务还可能使用其他私有地址,因此最终配置排除了 10/8、192.168/16 和 172.16/12,并额外列出本实验的具体节点地址。

APT 与交互式 Shell 的代理配置相互独立

/etc/profile.d 只影响登录 shell,APT 可能不会读取这些变量。必须单独配置 Proxy-Auto-Detect,并在脚本中区分 DIRECT 和代理地址。

树莓派时间错误会导致 APT 签名校验失败

实验中 Zero 2 W 的系统时间落后,APT 出现仓库签名“尚未生效”的错误。这不是代理故障。遇到类似问题应先检查:

date -R
timedatectl status

修正系统时间后再重复 apt-get update,不要为了绕过时间错误而关闭 APT 签名校验。

显式代理不等于透明代理

本方案只影响遵守 HTTP、HTTPS、SOCKS 或 Git/Apt 代理配置的程序。原始 TCP/UDP、DNS、容器流量和不读取代理环境变量的程序不会自动经过 Clash。若需求变为“所有流量经主机出网”,应单独设计转发、NAT、DNS 和防火墙策略,并重新评估 IBSS 路由冲突。