SSH ProxyJump:通过跳板机访问内网服务器
有些服务器不能从办公电脑直接访问,但可以先登录一台跳板机,再由跳板机访问内网目标服务器。
最直接的做法是先登录跳板机,再在跳板机上执行第二次 ssh。不过 SSH 自己就支持这条链路,使用 -J(ProxyJump)可以把两次连接写成一条命令。
假设网络关系如下:
当前电脑
└── SSH ──> 跳板机 192.0.2.10:22022
└── SSH ──> 目标服务器 198.51.100.20:22
本文中的账号和地址都是脱敏示例:跳板机使用 jump-user@192.0.2.10:22022,目标服务器使用 target-user@198.51.100.20。请替换为实际环境的值。
当前电脑不能直接访问 198.51.100.20,但跳板机可以访问它。连接目标服务器只需要:
ssh -J jump-user@192.0.2.10:22022 target-user@198.51.100.20
-J 到底做了什么
这条命令中有两个 SSH 地址:
ssh -J jump-user@192.0.2.10:22022 \
target-user@198.51.100.20
它们的角色不同:
-J jump-user@192.0.2.10:22022:先连接跳板机,用户名是jump-user,SSH 端口是22022target-user@198.51.100.20:最终要登录的目标服务器,未指定端口时使用默认的22
-J 的格式是:
-J [跳板机用户@]跳板机地址[:跳板机端口]
如果目标服务器的 SSH 端口不是 22,使用 -p 指定目标端口:
ssh -J jump-user@192.0.2.10:22022 \
-p 2222 \
target-user@198.51.100.20
这里的 -p 2222 属于目标服务器;跳板机的端口仍然写在 -J 后面。
连接过程
执行命令后,本地 SSH 客户端会做两件事:
- 先使用本地的密钥或密码认证跳板机
- 请求跳板机上的
sshd建立一条到198.51.100.20:22的 TCP 通道
随后,本地 SSH 客户端通过这条通道和目标服务器完成真正的 SSH 握手与认证。
完整链路可以画成:
本地 ssh 客户端
│
│ 连接跳板机 192.0.2.10:22022
▼
跳板机上的 sshd
│
│ 连接 198.51.100.20:22
▼
目标服务器上的 sshd
跳板机的作用是转发 TCP 字节流,不需要在跳板机上再启动一个交互式 shell。目标服务器的私钥认证也由当前电脑完成,不需要把目标服务器的私钥复制到跳板机上。
这和下面这种手动操作不同:
ssh -p 22022 jump-user@192.0.2.10
ssh target-user@198.51.100.20
手动操作需要先进入跳板机,再从跳板机发起第二次连接;-J 则把跳板机隐藏成目标连接的代理通道。
两台机器使用不同的账号和密钥
跳板机和目标服务器不一定使用同一个账号,也不一定使用同一把密钥。
例如跳板机使用 jump-key,目标服务器使用 target-key。更推荐写入 ~/.ssh/config,分别为两个主机配置认证信息。这样每把密钥的作用范围都很清楚。
Host my-jump
HostName 192.0.2.10
User jump-user
Port 22022
IdentityFile ~/.ssh/jump-key
Host my-target
HostName 198.51.100.20
User target-user
IdentityFile ~/.ssh/target-key
ProxyJump my-jump
之后直接执行:
ssh my-target
配置文件还有一个好处:目标服务器地址、跳板机端口和密钥路径只需要维护一次,后续执行 scp、sftp 或其他 SSH 工具时也可以复用。
例如使用 scp:
scp ./example.txt my-target:/tmp/
多级跳板机
如果目标服务器前面还有多层网络隔离,可以在 -J 中用逗号分隔多个跳板机:
ssh -J user1@jump-1:22022,user2@jump-2:22 \
user3@198.51.100.20
连接顺序是:
当前电脑 -> jump-1 -> jump-2 -> 198.51.100.20
也可以写成配置:
Host my-target
HostName 198.51.100.20
User user3
ProxyJump user1@jump-1:22022,user2@jump-2:22
每增加一层跳板,排查网络问题就会多一层依赖,因此实际使用时应该尽量保持链路简单。
连接失败时怎么排查
先确认跳板机本身可以登录:
ssh -p 22022 jump-user@192.0.2.10
再从跳板机检查它能否访问目标服务器的 SSH 端口:
nc -vz 198.51.100.20 22
如果跳板机没有 nc,也可以直接在跳板机上尝试:
ssh -v target-user@198.51.100.20
最后在本地打开详细日志:
ssh -vv \
-J jump-user@192.0.2.10:22022 \
target-user@198.51.100.20
通常可以根据日志快速区分问题发生在哪一段:
- 跳板机认证失败:检查跳板机用户名、端口和密钥
- 跳板机无法连接目标端口:检查目标服务器路由、防火墙和
sshd - 目标服务器认证失败:检查目标服务器的用户、密钥和
authorized_keys - 出现 host key 警告:确认目标 IP 没有被重新分配给另一台机器
ProxyJump 的目标连接仍然是端到端的,因此第一次连接目标服务器时,当前电脑仍然可能看到目标服务器的 host key 确认提示。
和端口转发有什么区别
-J 解决的是“登录目标服务器”:
本地电脑 -> 跳板机 -> 目标服务器
登录成功后,得到的是目标服务器上的 shell。
如果只是想访问目标服务器上的某个 HTTP、数据库或其他 TCP 服务,而不需要登录 shell,应该使用 -L 本地端口转发。另一篇文章中的用法就是让本地浏览器访问内网 Web 服务:
本地程序 -> 本地端口 -> SSH 隧道 -> 目标服务器上的服务
两者可以同时使用:-J 负责“怎么到达目标服务器”,-L 负责“到达目标服务器后再转发哪个服务端口”。
安全边界
跳板机不是认证绕过。它只改变了到达目标服务器的网络路径,目标服务器仍然会独立校验用户和密钥。
实际部署时需要注意:
- 跳板机必须允许连接目标服务器的 SSH 端口
- 目标服务器的 host key 应该在当前电脑上验证并保存
- 尽量使用密钥认证,避免把目标服务器私钥放到跳板机
- 只给跳板机开放必要的目标网段和端口
- 不要把跳板机当成无边界的通用代理
ProxyJump 的价值在于把“必须经过某台机器才能到达”的网络事实写进 SSH 命令或配置文件。命令本身很短,但它把多段连接、认证边界和最终目标区分得很清楚。
