编按:逗死我了,标题和以下正文和附件都是 DeepSeek 杜撰的。 但是我对这个思路负责。对代码和写的一些具体内容,请各位看官自行判断。我没丢给它公众号后台必须手工配置 IP 白名单的信息。 标题的后半句是我手工添加的。
一个问题
我的\"小龙女\"AI 助手——部署在树莓派上,每天帮我自动抓取 RSS、生成 AI 解读、排版发布到LeisureLinux微信公众号。
这个小流程跑了好几个星期,一直很稳,直到有一天......
天光中一声脆响:
curl: (6) Could not resolve host: api.weixin.qq.com
公众号发布了。问题是——我的树莓派 IP 变了。
家里的宽带是 PPPoE 拨号,每次重新拨号 IP 就变。如果直接在外面部署一个 tinyproxy 或 squid,不仅要配安全组、开端口,还要担心代理暴露公网被扫描。对安全洁癖的我们来说,这不优雅。
需求梳理
- 树莓派(发布脚本)需要一个稳定的 HTTP/HTTPS 代理出口
- 不能依赖动态 IP,不受运营商拨号影响
- VPS 不能开放 8888/8080/3128 等代理端口——安全组只留 22 端口
- 全程加密,无人中转,无中间人
- 断网能自动恢复,7×24 无人值守
架构一览
树莓派(发布脚本) VPS(中转跳板) 微信API服务器 ┌────────────┐ SSH隧道加密 ┌──────────┐ │ Python 脚本 │───HTTP────→ autossh ◇──────→ sshd:22 │──→ api.weixin.qq.com │ 发布公众号 │ 127.0.0.1:8888 ◇ │ 无额外端口 │ └──────┬─────┘ └──────────┘ │ SOCKS5:1080 ▼ ┌──────────────┐ │ goproxy │ socks5 → http 转换 │ 127.0.0.1 │ └──────────────┘
三层结构:
- autossh——建立 SSH 反向隧道,本地提供 SOCKS5 代理(
:1080) - goproxy——把 SOCKS5 转成 HTTP/HTTPS 代理(
:8888) - 发布脚本——通过
HTTP_PROXY=http://127.0.0.1:8888调用
三条关键路径全部只监听 127.0.0.1,外网碰不到、扫不了、攻不进来。漂亮得像一把龙泉剑——铸在鞘里的锋芒,只在你需要的时候出鞘。
━━━ ━━━ ━━━
第一步:SSH 密钥免密登录
autossh 自动重连的前提是免密登录,否则断线后卡在密码输入,隧道永远起不来。
# 树莓派生成密钥(一路回车,不设密码)
ssh-keygen -t ed25519
# 推送到 VPS
ssh-copy-id root@你的VPS公网IP
测试一下是否能直接登录:
ssh root@你的VPS公网IP "echo OK"
如果打印出 OK,密钥配对成功。
━━━ ━━━ ━━━
第二步:安装 autossh(SSH 隧道守护神)
树莓派上安装:
sudo apt update
sudo apt install autossh -y
测试启动 SOCKS5 隧道
AUTOSSH_PIDFILE=/tmp/wechat_tunnel.pid AUTOSSH_GATETIME=0 autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -D 127.0.0.1:1080 root@你的VPS公网IP
参数解释:
| 参数 | 含义 |
|---|---|
-M 0 |
不占额外监控端口 |
AUTOSSH_GATETIME=0 |
断线立刻重连,不冷却 |
ServerAliveInterval=30 |
每 30 秒发 SSH 心跳 |
ServerAliveCountMax=3 |
连续 3 次心跳无响应判死 |
-D 127.0.0.1:1080 |
本地 SOCKS5 代理,仅本机可访 |
打开另一个终端,测试隧道连通性:
curl -x socks5://127.0.0.1:1080 https://api.weixin.qq.com
# 应该返回 {"errcode":40001,...} 而不是 Connection refused
━━━ ━━━ ━━━
第二步又半步:VPS 也配 SSH 保活(双向保活)
光客户端发心跳不够——有的云厂商/运营商会主动踢掉半天无交互的 SSH 连接。必须两端同时保活。
登录 VPS,修改 /etc/ssh/sshd_config:
ClientAliveInterval 30 ClientAliveCountMax 3
重启 sshd:
sudo systemctl restart sshd
现在心跳是双向的,服务器也会主动探测客户端状态——\"双剑合璧,谁先倒下都不可能。\"
━━━ ━━━ ━━━
第三步:创建 systemd 服务(开机自启 + 崩溃重启)
手动跑隧道不是长久之计,万一树莓派重启/断电,或者 autossh 进程异常退出,发布定时就断了。用 systemd 托管,做到开机自启 + 进程崩溃自动拉起。
创建服务文件 /etc/systemd/system/wechat-ssh-tunnel.service:
[Unit] Description=Wechat Publish SSH SOCKS Tunnel After=network-online.target Wants=network-online.target
[Service] User=pi Restart=always RestartSec=5 Environment="AUTOSSH_GATETIME=0" ExecStart=/usr/bin/autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -D 127.0.0.1:1080 root@你的VPS公网IP
[Install] WantedBy=multi-user.target
启动并设为开机自启:
sudo systemctl daemon-reload
sudo systemctl enable wechat-ssh-tunnel
sudo systemctl start wechat-ssh-tunnel
# 检查状态
systemctl status wechat-ssh-tunnel
━━━ ━━━ ━━━
第四步:goproxy——SOCKS5 转 HTTP 代理
为什么需要转?
大部分 Python 库(requests、urllib3)和微信 API 调用时,HTTPS_PROXY 期望的是 HTTP 代理,原生 SOCKS5 支持需要额外装 requests[socks]。与其在每台机器上折腾依赖,不如在系统层做完协议转换。
goproxy 是一个用 Go 写的轻量级代理工具,单二进制文件,无依赖。
# 下载 goproxy(到 /home/pi/ 下)
cd /home/pi
wget https://github.com/snail007/goproxy/releases/latest/download/proxy-linux-arm64.tar.gz
tar xzf proxy-linux-arm64.tar.gz
创建 systemd 服务 /etc/systemd/system/wechat-goproxy.service:
[Unit] Description=Socks5 to HTTP Proxy for Wechat API After=wechat-ssh-tunnel.service BindsTo=wechat-ssh-tunnel.service
[Service] User=pi Restart=always RestartSec=3
ExecStart=/home/pi/proxy http -s socks5://127.0.0.1:1080 -l 127.0.0.1:8888
[Install] WantedBy=multi-user.target
关键细节:
After+BindsTo:goproxy 依赖隧道服务启动,隧道挂了代理也挂,一起重启-l 127.0.0.1:8888:HTTP 代理仅监听本机,局域网不能碰
<!-- -->
sudo systemctl daemon-reload
sudo systemctl enable wechat-goproxy
sudo systemctl start wechat-goproxy
# 全链路测试
curl -x http://127.0.0.1:8888 https://api.weixin.qq.com
━━━ ━━━ ━━━
第五步:发布脚本调用代理
代理环境变量只在当前进程生效,不影响系统其他服务:
#!/bin/bash
# 推送发布脚本
export HTTP_PROXY=http://127.0.0.1:8888
export HTTPS_PROXY=http://127.0.0.1:8888
export NO_PROXY="localhost,127.0.0.1,192.168.*"
python3 publish_wechat.py
在 Python 里,requests 自动识别环境变量:
import os
os.environ['HTTP_PROXY'] = 'http://127.0.0.1:8888'
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:8888'
import requests
resp = requests.post('https://api.weixin.qq.com/cgi-bin/token', params={
'grant_type': 'client_credential', 'appid': APPID, 'secret': SECRET
})
一步到位。代理对脚本透明。
━━━ ━━━ ━━━
完整安全架构总结
┌─────────────────────────────────────┐ │ VPS 安全组 │ │ 22/tcp SSH 其他端口: ❌全部关闭 │ └─────────────────────────────────────┘ │ SSH 加密隧道(autossh) │ ┌─────────────────────────────────────┐ │ 树莓派 127.0.0.1 │ │ │ │ :1080 SOCKS5(仅本机) │ │ ↓ goproxy 转换 │ │ :8888 HTTP(仅本机) │ │ ↓ │ │ 发布脚本 → 微信 API │ └─────────────────────────────────────┘
安全收益
- VPS 零入站端口暴露:安全组仅开 22 端口,没有 8888/8080/3128,没有 tinyproxy,安全组规则看一眼就知道——干净的就像没人住过的出租屋
- 双重 SSH 保活:客户端(
ServerAliveInterval=30)+ 服务端(ClientAliveInterval=30),双向心跳检测 - 本地代理完全隔离:SOCKS5 和 HTTP 代理都只监听
127.0.0.1,局域网其他设备无法访问,即使不慎被攻击者拿到树莓派 shell,也无法用这个代理翻墙 - 全链路自动恢复:断网重连、进程崩溃、系统重启全部自动拉起,7×24 稳定运行
故障自检命令
# SSH 隧道状态
systemctl status wechat-ssh-tunnel
# HTTP 代理状态
systemctl status wechat-goproxy
# 全链路连通测试
curl -x http://127.0.0.1:8888 https://api.weixin.qq.com
# 查看隧道日志
journalctl -u wechat-ssh-tunnel -f
# 查看代理日志
journalctl -u wechat-goproxy -f
━━━ ━━━ ━━━
踩过的坑坑 1:autossh 不加 AUTOSSH_GATETIME=0
默认 autossh 有 30 秒冷却期,断线后要等半分钟才重连。对于定时发布来说,30 秒的窗口 + 刚好在那个窗口执行 cron——你的发布就失败了。AUTOSSH_GATETIME=0 让它断了秒连。
坑 2:goproxy 比隧道先启动
第一次配 systemd 时没加 BindsTo,树莓派重启后 goproxy 先启动、连接 127.0.0.1:1080 被拒、直接退出。加了 BindsTo=wechat-ssh-tunnel.service,systemd 保证隧道启动后再拉代理。
坑 3:NO_PROXY 不配全,代理把自己绕进去了
发布脚本里如果用微信 API 获取 token,api.weixin.qq.com 域名解析走代理没问题。但如果脚本里还有对本机服务的调用(比如 localhost:8080 的监控面板),没配 NO_PROXY 的话——请求走向代理 → 代理连接本机 → 代理又从本机连代理 → 死循环套娃。所以一定配好:
export NO_PROXY="localhost,127.0.0.1,192.168.*"
━━━ ━━━ ━━━
写在最后
这套方案最让我高兴的一点是:它解决了一个部署上的工程痛点,而不是引入一个新玩具。
- 以前:配 tinyproxy → 配安全组 → 配 ACL → 每天担心被扫描 → 隔三差五在日志里看到奇怪 IP 在撞 8888 端口
- 现在:autossh + goproxy 各一个 systemd 文件,VPS 安全组一眼望过去只有 22 端口,安心
树莓派换了三次 IP,隧道自动恢复,我一次都没有再去碰安全组。
如果你也在做本地 AI 自动发布公众号、或任何需要动态 IP 做稳定代理出口的场景,这套方案值得一试。
🐉 首发 LeisureLinux 公众号 | 实战出品
━━━ ━━━ ━━━
【WPAD,GoProxy,轻量服务器-哔哩哔哩】 https://b23.tv/Id2vcHD
【SSH隧道实现"外网穿透"访问云端受保护的服务-哔哩哔哩】 https://b23.tv/gfsb5WM
【SSH 客户端如何通过 HTTPS 代理访问 SSH Server-哔哩哔哩】 https://b23.tv/zSBVbq9
【[含源码]把 PAC 脚本用于 curl 自动使用对应的代理-哔哩哔哩】 https://b23.tv/1Q4ZxF4
不写代码、全靠「聊天」:Meta AI客服反成黑客帮凶,奥巴马白宫账号遭劫
性能飙升 30%!NGINX 1.30 稳定版发布:原生支持 MPTCP 与 HTTP/2 Backend
【技术前瞻】Claude Desktop + MCP:让 AI 成为你的 Kali 渗透助手
附录:一键部署脚本
# 请先配置 SSH 免密登录到 VPS
VPS_IP="你的VPS公网IP"
# 安装 autossh
sudo apt install autossh -y
# 创建隧道 systemd 服务
sudo tee /etc/systemd/system/wechat-ssh-tunnel.service > /dev/null <<EOF
[Unit] Description=Wechat Publish SSH SOCKS Tunnel After=network-online.target Wants=network-online.target
[Service] User=$USER Restart=always RestartSec=5 Environment="AUTOSSH_GATETIME=0" ExecStart=/usr/bin/autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -D 127.0.0.1:1080 root@$VPS_IP
[Install] WantedBy=multi-user.target EOF
# 下载 goproxy
cd /tmp
wget -q https://github.com/snail007/goproxy/releases/latest/download/proxy-linux-arm64.tar.gz
tar xzf proxy-linux-arm64.tar.gz
sudo mv proxy /usr/local/bin/goproxy
# 创建 goproxy systemd 服务
sudo tee /etc/systemd/system/wechat-goproxy.service > /dev/null <<EOF
[Unit] Description=Socks5 to HTTP Proxy for Wechat API After=wechat-ssh-tunnel.service BindsTo=wechat-ssh-tunnel.service
[Service] User=$USER Restart=always RestartSec=3
ExecStart=/usr/local/bin/goproxy http -s socks5://127.0.0.1:1080 -l 127.0.0.1:8888
[Install] WantedBy=multi-user.target EOF
# 启动服务
sudo systemctl daemon-reload
sudo systemctl enable --now wechat-ssh-tunnel
sudo systemctl enable --now wechat-goproxy
# 验证
sleep 3
curl -x http://127.0.0.1:8888 https://api.weixin.qq.com
echo ""
systemctl status wechat-ssh-tunnel wechat-goproxy --no-pager