72602-minipc → ecs-99
SSH 反向隧道:72602-minipc → ecs-99(双入口)
本文档是 72602-minipc 的当前参考方案;执行前应核对 live unit、ECS 安全组、 SSH banner 和监控状态,目标是避免单端口掉线导致完全失联。
- 主入口:
10021 - 备入口:
10022 - 两个端口由两个独立 service 维护
一、架构
已登记且获安全组授权的来源客户端 ecs-99 (47.110.67.161) 72602-minipc (192.168.0.25)
ssh -p 10021 aaron@47.110.67.161 -> 0.0.0.0:10021 (sshd) --SSH reverse--> localhost:22
ssh -p 10022 aaron@47.110.67.161 -> 0.0.0.0:10022 (sshd) --SSH reverse--> localhost:22
ECS HAProxy :25/:465/:587/:993 -> 127.0.0.1:10225/:10465/:10587/:10993 (sshd)
--SSH reverse--> minipc hostPort :25/:465/:587/:993说明:反向隧道必须由 72602-minipc 主动发起。
0.0.0.0是 ECS 上 sshd 的 reverse-bind,并不等于对任意公网来源开放;安全组来源限制、SSH key 认证、已建立的 SSH child/session、banner 和监控恢复都必须分别验证。
二、上线前检查
2.1 在 72602-minipc 检查基础条件
# 1) 本机 SSH 服务
sudo systemctl is-active ssh
# 2) autossh 是否安装
autossh -V
# 3) 本机到 ECS 网络与认证
ssh -o ConnectTimeout=5 root@47.110.67.161 hostname
# 期望输出: ecs-992.2 在 ECS 检查前置配置
/etc/ssh/sshd_config 至少包含:
GatewayPorts clientspecified重载:
sudo systemctl reload sshd安全组仅应按当前 72602 出口地址来源受限地放行 10021/tcp、10022/tcp。
不要为 ZJLAB 的 10023/tcp、10024/tcp 添加公网规则;那两条 listener
属于 ECS loopback-only 的 ProxyJump 路径。
同时确认 ECS 本机防火墙(UFW)放行这两个端口:
sudo ufw status numbered
# 应包含 10021/tcp 和 10022/tcp 的 ALLOW 规则;公网来源边界由云安全组控制三、创建双 service(72602-minipc 上执行)
下面步骤全部在 72602-minipc 上执行。
3.1 统一 SSH 客户端配置(可选但推荐)
编辑 ~/.ssh/config:
Host ecs-99
HostName 47.110.67.161
User root
ServerAliveInterval 60
ServerAliveCountMax 3
ExitOnForwardFailure yes
TCPKeepAlive yes
ConnectTimeout 103.2 创建 systemd 用户服务目录
mkdir -p ~/.config/systemd/user3.3 新建 service(10021 主)
文件:~/.config/systemd/user/reverse-tunnel-ecs-10021.service
[Unit]
Description=Reverse SSH tunnel to ecs-99 (port 10021 -> local SSH)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment="AUTOSSH_GATETIME=0"
Environment="AUTOSSH_POLL=60"
Environment="AUTOSSH_FIRST_POLL=30"
ExecStart=/usr/bin/autossh -M 0 -N -R 0.0.0.0:10021:localhost:22 ecs-99
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target3.4 10022 备入口(含 Mailu)
文件:~/.config/systemd/user/reverse-tunnel-ecs-10022.service
[Unit]
Description=Reverse SSH tunnel to ecs-99 (port 10022 -> local SSH)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment="AUTOSSH_GATETIME=0"
Environment="AUTOSSH_POLL=60"
Environment="AUTOSSH_FIRST_POLL=30"
ExecStart=/usr/bin/autossh -M 0 -N \
-R 0.0.0.0:10022:localhost:22 \
-R 127.0.0.1:10225:127.0.0.1:25 \
-R 127.0.0.1:10465:127.0.0.1:465 \
-R 127.0.0.1:10587:127.0.0.1:587 \
-R 127.0.0.1:10993:127.0.0.1:993 \
ecs-99
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target说明:10022 只承载备用 SSH 和 Mailu 四个 hostPort。Web
80/443已迁移 到 HAProxy + WireGuard,不得重新加入该 service,除非按 WireGuard 回滚步骤 临时恢复旧路径。ECS 上的四个 Mailu入口必须是 loopback-only;公网绑定由 HAProxy 完成。
3.5 启用并启动
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user daemon-reload
systemctl --user enable --now reverse-tunnel-ecs-10021.service
systemctl --user enable --now reverse-tunnel-ecs-10022.service
systemctl --user status reverse-tunnel-ecs-10021.service --no-pager
systemctl --user status reverse-tunnel-ecs-10022.service --no-pager3.6 防登出失效(强烈建议)
sudo loginctl enable-linger aaron
loginctl show-user aaron | grep Linger
# 期望: Linger=yes四、连通性验证
4.1 在 ECS 上看监听
ssh root@47.110.67.161 "ss -tlnp | grep -E '10021|10022'"期望看到(监听归属示例;地址、来源限制和健康状态需按 2026-08-13 之后的 live 状态复核):
0.0.0.0:100210.0.0.0:10022127.0.0.1:10225,127.0.0.1:10465,127.0.0.1:10587,127.0.0.1:10993(sshd)0.0.0.0:80,0.0.0.0:443(HAProxy)0.0.0.0:51820/udp(WireGuard)
4.2 在外网验证
nc -zv 47.110.67.161 10021
nc -zv 47.110.67.161 10022
ssh -p 10021 aaron@47.110.67.161
ssh -p 10022 aaron@47.110.67.161若 10021/10022 外网超时,但 ECS 上已监听,优先检查:
- 云安全组来源网段是否覆盖当前出口 IP
- ECS UFW 是否放行对应端口
4.3 Mailu 入口验证
在 ECS 上确认公网 Web/Mail 端口归 HAProxy;10021/10022 SSH listener 和
10225/10465/10587/10993 Mailu loopback backend 归 sshd:
sudo systemctl is-active haproxy
haproxy -c -f /etc/haproxy/haproxy.cfg
sudo ss -ltnp从不发送邮件数据的测试客户端验证 banner、TLS 和 STARTTLS。relay 检查最多
发送 EHLO、MAIL FROM、RCPT TO、QUIT,不要发送 DATA,不要认证。
同时检查 ArgoCD 与 front rollout:
argocd app get ops-docs --refresh --insecure --grpc-web
argocd app get mailu --refresh --insecure --grpc-web
kubectl -n mailu rollout status deployment/mailu-front
kubectl -n mailu get endpoints mailu-front mailu-front-ext若 Dovecot 报 Client not trusted,先检查 Mailu realIpFrom 与 hostPort/CNI
实际传输源是否一致。修正必须提交到 Git 并等待 ArgoCD 自动同步,不能手动
patch、apply、sync 或重启 Mailu。
五、故障恢复(按顺序)
场景 A:ECS 本机 ssh localhost -p 10022 失败
这说明 ECS 上没有监听 10022,问题几乎总在源机器(72602-minipc)侧。
在 72602-minipc 执行:
export XDG_RUNTIME_DIR=/run/user/$(id -u)
# 1) 看 service
systemctl --user status reverse-tunnel-ecs-10021.service --no-pager
systemctl --user status reverse-tunnel-ecs-10022.service --no-pager
# 2) 一次只处理一条;先处理 backup,验证 listener/banner/监控恢复后再决定是否处理 primary
TUNNEL_SERVICE=reverse-tunnel-ecs-10022.service
systemctl --user restart "$TUNNEL_SERVICE"
# 3) 看日志
journalctl --user -u "$TUNNEL_SERVICE" --since "10 min ago" --no-pager
# 4) 验证到 ECS 的基础连通
ssh -o ConnectTimeout=5 root@47.110.67.161 echo ok场景 B:外网超时但 ECS 本机可通
问题在安全组或 ECS 防火墙,不在隧道本身。
ssh root@47.110.67.161 "ss -tlnp | grep -E '10021|10022'"
ssh root@47.110.67.161 "iptables -L INPUT -n | grep -E '10021|10022' || true"场景 C:重启后隧道没起来
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user is-enabled reverse-tunnel-ecs-10021.service
systemctl --user is-enabled reverse-tunnel-ecs-10022.service
loginctl show-user aaron | grep Linger六、常用运维命令
export XDG_RUNTIME_DIR=/run/user/$(id -u)
# 重载;重启时一次只选择一个入口,验证 listener/banner/监控后再处理另一个
systemctl --user daemon-reload
TUNNEL_SERVICE=reverse-tunnel-ecs-10021.service
systemctl --user restart "$TUNNEL_SERVICE"
# 停止(仅在明确授权的维护窗口内;一次只停止一个入口)
TUNNEL_SERVICE=reverse-tunnel-ecs-10021.service
systemctl --user stop "$TUNNEL_SERVICE"
# 日志实时跟踪
journalctl --user -u "$TUNNEL_SERVICE" -f七、和现网监控的关系(72602;2026-08-13 审计快照,动态状态需 live verify)
ECS 上原有巡检继续只监控 72602-minipc 的两个公开入口;其端口列表、脚本、 unit、timer 和状态文件保持不变。
当 72602-minipc 双入口上线后,只需确认:
- 安全组已放行
10021/10022 /etc/tunnel-healthcheck-ports.conf包含10021、10022/etc/tunnel-healthcheck.env企业应用参数可用(DINGTALK_CLIENT_ID/SECRET/AGENT_ID/USER_IDS)
按该审计快照记录的 72602 线上实现:
- 告警通道为钉钉企业应用 API(非 webhook)
- 告警消息为 Markdown 格式(标题:
🚨 ECS Tunnel Alert) - 连续失败 3 次才发送告警(防抖)
ZJLAB 的两个私有 loopback listener 不加入上述端口列表,而由独立的
check-only 巡检按 primary、backup 标签检查。该巡检要求每个标签恰有一个
loopback-only listener、唯一 owner 为 sshd、两个 owner 相互独立、短超时
SSH banner 正常且 owner 签名稳定;连续失败 3 次才告警。日志和告警不包含
endpoint、端口、账户、内网拓扑或签名值。巡检不会 restart/kill 隧道,也不会
修改 sshd、防火墙、安全组、DNS 或 endpoint。真实配置和恢复步骤只保存在
私有 SOPS inventory 与 root-only host 配置中。
八、Mailu 代理回滚
按以下顺序回滚,不恢复旧 HAProxy 配置,也不卸载 HAProxy:
# ECS
sudo systemctl stop haproxy
# 72602-minipc(使用实际保存的备份路径)
cp /home/aaron/Ops/ops-private/backups/reverse-tunnel-ecs-10022.service.<UTC>.before-haproxy \
~/.config/systemd/user/reverse-tunnel-ecs-10022.service
export XDG_RUNTIME_DIR=/run/user/$(id -u)
systemctl --user daemon-reload
systemctl --user restart reverse-tunnel-ecs-10022.service若还原 ECS 的 GatewayPorts 修正,恢复对应的
/var/backups/sshd_config.<UTC>.before-haproxy,执行 sshd -t 后 reload
sshd,再按上面步骤恢复并重启 10022。10021 不在本次回滚范围内。