当远程服务器连接全局 VPN 时,默认路由变化可能导致现有 SSH 会话中断。如果需求只是让部分命令或应用通过另一条网络出口,更稳妥的方式是部署一个应用层正向代理,再让需要代理的客户端显式使用它。

1. 准备 Squid 配置

创建工作目录:

mkdir -p ~/squid
cd ~/squid

保存以下配置为 squid.conf

# 只允许可信内网与 Tailscale/CGNAT 地址。
acl localnet src 10.0.0.0/8
acl localnet src 100.64.0.0/10
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16
acl localnet src fc00::/7
acl localnet src fe80::/10

acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 21
acl Safe_ports port 443
acl Safe_ports port 70
acl Safe_ports port 210
acl Safe_ports port 280
acl Safe_ports port 488
acl Safe_ports port 591
acl Safe_ports port 777
acl Safe_ports port 1025-65535
acl CONNECT method CONNECT

http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localnet
http_access deny all

http_port 3128
coredump_dir /var/spool/squid

refresh_pattern ^ftp:             1440    20%     10080
refresh_pattern ^gopher:          1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0       0%      0
refresh_pattern .                 0       20%     4320

这里的关键不是端口,而是 http_access 的顺序:

  • 先拒绝不安全的目标端口;
  • 只允许 localnet 中定义的来源;
  • 最后使用 http_access deny all 拒绝其他请求。

如果只需要一个更小的网段,应把 ACL 收紧到实际地址。例如只允许 AOI 内网时,可以使用 10.10.10.0/24,不必允许整个 10.0.0.0/8

2. 启动 Squid 容器

MR 37 使用 Ubuntu 官方 Squid 镜像,并把主机的 3129 映射到容器的 3128

docker run -d \
  --name squid \
  --restart unless-stopped \
  -e TZ=UTC \
  -p 3129:3128 \
  -v "$PWD/squid.conf:/etc/squid/squid.conf:ro" \
  ubuntu/squid:5.2-22.04_beta

如果宿主机启用了 SELinux,可以把挂载选项改为 :Z,ro

-v "$PWD/squid.conf:/etc/squid/squid.conf:Z,ro"

日志和缓存也可以按需持久化:

挂载路径用途
/var/log/squidSquid 日志
/var/spool/squid磁盘缓存
/etc/squid/squid.conf主配置文件
/etc/squid/conf.d/配置片段

3. 验证容器和配置

检查容器状态与日志:

docker ps --filter name=squid
docker logs --tail=100 squid

在容器内验证配置语法:

docker exec squid squid -k parse

从允许访问的客户端测试 HTTP:

curl -I -x http://10.10.10.11:3129 http://example.com

测试 HTTPS:

curl -I -x http://10.10.10.11:3129 https://example.com

普通 HTTPS 访问使用 CONNECT 隧道,不需要在 Squid 上生成自签名证书。客户端与目标网站之间的 TLS 仍然保持端到端加密。

4. 配置客户端

命令行程序通常会读取以下环境变量:

export http_proxy=http://10.10.10.11:3129
export https_proxy=http://10.10.10.11:3129

虽然第二个变量名是 https_proxy,代理 URL 仍然使用 http://:客户端先与 HTTP 代理协商 CONNECT,再在隧道内建立到目标网站的 TLS 连接。

需要持久保存时,可以把环境变量加入 ~/.bashrc~/.zshrc。并非所有程序都会读取这些变量,浏览器和桌面应用可能需要单独配置。

5. 通过 Tailscale 使用

配置中允许 100.64.0.0/10,因此客户端可以通过 Tailscale 地址访问代理,而无需把 3129 暴露到公网:

export http_proxy=http://100.x.y.z:3129
export https_proxy=http://100.x.y.z:3129

这里的 100.x.y.z 应替换为运行 Squid 的 Tailscale 节点地址。除了 Squid ACL,还应通过主机防火墙和 Tailscale ACL 限制访问来源。

6. 更新配置

修改 squid.conf 后先验证,再重新创建容器:

docker exec squid squid -k parse
docker restart squid

如果需要从本地部署到远程服务器,可以复制配置后通过 SSH 执行启动命令,但脚本中不应硬编码用户名、公网 IP 或密码:

scp squid.conf server:~/squid/squid.conf
ssh server 'cd ~/squid && docker restart squid'

7. 安全边界

不要在配置中使用 http_access allow all,也不要直接向公网开放代理端口。Squid 的应用层 ACL、主机防火墙和 Tailscale ACL 应形成多层限制。若需要用户名和密码认证,可以在此基础上增加 proxy_auth,但 Basic 凭据也不应通过不可信网络裸露传输。