约 8 分钟阅读

SSH 跳板机端口穿透:当 ProxyJump、ssh -L 和 SOCKS5 都不可用时

记录一种在服务器禁止 SSH TCP forwarding 时仍可通过跳板机访问目标服务的实用方案:ProxyCommand + nc 登录,socat + SSH ControlMaster 做固定端口穿透。

SSH 跳板机端口穿透:当 ProxyJump、ssh -L 和 SOCKS5 都不可用时

在内网运维场景里,经常会遇到这种链路:

本机 -> 跳板机 -> 目标机

本机不能直接访问目标机,只能先登录跳板机,再从跳板机访问目标机。通常我们会优先使用 ProxyJumpssh -Lssh -D SOCKS5 代理。但如果跳板机或目标机的 sshd 禁止 TCP forwarding,这些标准方案会直接失败。

这篇文章记录一个更朴素但实用的方案:用 ProxyCommand + nc 完成 SSH 登录,再用 socat + ssh + nc 穿透少量固定端口。它不需要修改服务器 sshd 配置,适合权限受限但可以正常 SSH 登录的环境。

示例里的 IP、用户和别名均为脱敏占位,不对应真实环境。


1. 基础 SSH 链路

先给跳板机配置普通 SSH Host:

Host bastion-demo
    HostName 192.0.2.10
    User bastion-user
    IdentityFile ~/.ssh/id_ed25519

目标机通过跳板机上的 nc 建立 TCP 连接:

Host target-demo
    HostName 198.51.100.20
    User app-user
    IdentityFile ~/.ssh/id_ed25519
    ProxyCommand ssh bastion-demo nc %h %p

验证登录:

ssh target-demo

这条链路的关键点是:ProxyCommand 不走 SSH 的 direct-tcpip channel,而是在跳板机上执行 nc <target-host> <target-port>。因此它可以绕过一部分禁止 ProxyJump 的环境。

要求很简单:

  • 本机私钥能登录跳板机。
  • 本机私钥或本机公钥授权能登录目标机。
  • 跳板机上存在 ncncatsocat 之一。
  • SSH Host 别名建议只用 ASCII。

2. 常见失败现象

2.1 中文或特殊字符别名导致 hostname 报错

如果执行:

ssh 某个中文别名

可能会报:

hostname contains invalid characters

处理方式很直接:SSH Host 别名使用 ASCII,中文写到注释或文档里。

project-bastion
project-target

2.2 ProxyJump 被服务端拒绝

ProxyJump 失败时常见报错:

channel 0: open failed: administratively prohibited: open failed
stdio forwarding failed

原因是 ProxyJump 依赖 SSH direct-tcpip channel。如果跳板机 sshd 禁止 TCP forwarding,就会被服务端拒绝。

替代配置:

ProxyCommand ssh bastion-demo nc %h %p

2.3 SOCKS5 或 ssh -L 被目标机拒绝

如果尝试:

ssh -N -D 127.0.0.1:1080 target-demo

或:

ssh -N -L 18080:127.0.0.1:8080 target-demo

遇到:

channel 1: open failed: administratively prohibited: open failed

通常是目标机 sshd 禁止 TCP forwarding。

可以在目标机检查:

sshd -T 2>/dev/null | egrep '^(allowtcpforwarding|disableforwarding|permitopen|permitlisten)'

如果看到:

allowtcpforwarding no
disableforwarding no
permitopen any
permitlisten any

那么 ssh -D SOCKS5 和 ssh -L 基本都不能用。


3. 固定端口穿透方案

当 SSH forwarding 被禁时,可以在本机用 socat 监听一个本地端口。每次有浏览器连接进来,就执行一次:

ssh target-demo nc 127.0.0.1 <remote-port>

通用模板:

socat TCP-LISTEN:<local-port>,bind=127.0.0.1,reuseaddr,fork EXEC:'ssh <target-alias> nc <remote-host> <remote-port>'

安装 socat

brew install socat

示例:把本机 18080 转到目标机本地 8080

socat TCP-LISTEN:18080,bind=127.0.0.1,reuseaddr,fork EXEC:'ssh target-demo nc 127.0.0.1 8080'

浏览器访问:

http://127.0.0.1:18080

如果目标服务监听的是内网 IP 而不是 127.0.0.1,把命令里的 127.0.0.1 换成对应地址即可。


4. 稳定性增强:复用 SSH 连接

上面的原始命令有一个问题:浏览器的每个 TCP 连接都会新建一次 SSH 登录。页面资源多时,连接会明显抖动。

可以先建立 SSH master 连接:

mkdir -p ~/.ssh/cm
chmod 700 ~/.ssh/cm

ssh -MNf \
  -S ~/.ssh/cm/target-demo \
  -o ControlMaster=yes \
  -o ControlPersist=2h \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  target-demo

再让 socat 复用这个 master:

socat TCP-LISTEN:18080,bind=127.0.0.1,reuseaddr,fork,keepalive EXEC:'ssh -T -S ~/.ssh/cm/target-demo -o BatchMode=yes -o LogLevel=ERROR target-demo nc 127.0.0.1 8080'

检查 master 状态:

ssh -S ~/.ssh/cm/target-demo -O check target-demo

关闭 master:

ssh -S ~/.ssh/cm/target-demo -O exit target-demo

这个版本仍然不是完整 VPN,也不是 SOCKS5,但对少量固定 Web 服务已经足够稳定。


5. 什么时候不该用这个方案

这个方案适合:

  • 只需要访问少量固定端口。
  • 不能修改 sshd 配置。
  • ProxyJumpssh -Lssh -D 被服务端策略禁止。
  • 跳板机能访问目标机,且跳板机上有 nc

不适合:

  • 需要访问大量动态端口。
  • 需要全系统透明代理。
  • 需要长期高并发访问。
  • 能让管理员开启 AllowTcpForwarding yes

如果管理员允许 TCP forwarding,优先使用标准方案:

ssh -N -L 18080:127.0.0.1:8080 target-demo

或者:

ssh -N -D 127.0.0.1:1080 target-demo

标准方案更简单,也更稳定。


6. 总结

当 SSH TCP forwarding 被禁时,不必一上来就改服务器配置或搭 VPN。对少量固定端口,可以用:

ProxyCommand + nc
socat + ssh + nc
ControlMaster 复用连接

这套组合足够小,也足够实用。它的边界也很清楚:适合临时访问固定端口,不适合替代正式 VPN 或标准 SSH forwarding。

💬 评论

主题
字体
密度
语言