跳板机明明能连上,为什么内网服务器还是进不去?

周一早上接到发布窗口的告警时,跳板机 bastion-a 的 SSH 连接是绿色的,目标服务器 db-prod 却一直超时。很多人第一反应是再试一次密码,或者把目标端口从 22 改成一个"看起来更像数据库"的端口。这样做只会让日志变得更乱,因为此时还没有证明跳板机能替你访问目标地址。这就是典型的 SSH 跳板机连接失败现场,症状在目标,原因可能在任意一段。

多跳连接最容易制造一种错觉:本地到跳板机通了,于是整条路径应该都通。实际上,连接至少分成三段,本地到跳板机、跳板机到下一跳、最后一跳到目标。任意一段的 DNS、路由、认证或转发权限出问题,客户端都可能只给你一个笼统的超时。

我排这类问题时,会把"登录跳板机"和"从跳板机抵达目标"当成两个独立动作。这个顺序看起来慢一点,却能快速缩小范围,也能避免把生产凭据反复填进错误的连接配置。

先确认你连到的真是跳板机

本地先单独连接 bastion-a,不要一上来就叠加 -J

bash 复制代码
ssh -vvv ops@bastion-a

看到 shell 提示符后,先确认主机名、网卡地址和当前用户:

bash 复制代码
hostname
ip addr
whoami

如果这里都不稳定,目标服务器还没有进入排查范围。跳板机可能被 DNS 轮询到不同节点,也可能刚完成重装导致主机密钥变化。OpenSSH 10.3 起,命令行传入的 -J / ProxyJump 用户名和主机名会做更严格的校验;这能减少不可信输入带来的问题,但不会替你验证网络拓扑是否符合预期。此时先确认 SSH 跳板机本身的地址、用户和认证,再看下一跳。

图形工具可以把跳板连接保存起来,但我仍会在终端里做一次最小确认。连接名叫"生产跳板"不等于远端真的就是生产跳板,尤其是复制配置、切换 VPN 或调整 DNS 之后。

如果公司有多个出口或多个堡垒机集群,我还会记录本地实际使用的出口地址。相同的跳板主机名可能根据网络区域解析到不同节点,节点之间的安全策略和可达网段未必完全一致。把"我在办公室能连"直接推断成"家里 VPN 也能连",往往会把环境差异误认为目标故障。

"跳板机可达"不代表它能看见目标

确认第一段后,直接在跳板机上测试目标地址和端口。目标是判断网络层,不是登录目标:

bash 复制代码
getent hosts db-prod
nc -vz -w 3 10.30.8.12 22

如果系统没有 nc,可以用 Bash 的 TCP 探测做一个临时验证:

bash 复制代码
timeout 3 bash -c '</dev/tcp/10.30.8.12/22' && echo open || echo closed

这里的结果只说明端口是否能建立 TCP 连接,不代表 SSH 认证一定成功。若跳板机解析出的 db-prod 地址与工单记录不同,先处理 DNS 或 hosts 文件;若地址正确但端口不通,再看安全组、路由表和目标机防火墙。不要在本地反复执行 ssh -J,那样无法告诉你失败发生在哪一跳。

测试时最好保留一次成功和一次失败的完整输出。成功输出能证明某个时间点的路径和权限,失败输出则提供对照,尤其是 DNS 解析结果、连接目标地址和超时发生的秒数。只贴一句"连接超时"给网络同事,信息量远不够;把两段输出和目标端口一起交过去,定位速度会快很多。

ProxyJump 配置,顺序比选项更多重要

官方文档把多跳路径画成"本地电脑 -> 跳板机 -> 目标服务器",并支持继续串联第二台跳板机。配置时,跳板机要先作为普通 SSH 连接保存,再在目标连接里选择它。这样做的价值不只是界面整齐,而是每一跳可以使用各自的用户名、密钥和交互认证方式。

一个最小的 OpenSSH 配置可以这样写:

sshconfig 复制代码
Host bastion-a
    HostName bastion.example.net
    User ops
    IdentityFile ~/.ssh/ops_ed25519

Host db-prod
    HostName 10.30.8.12
    User dba
    ProxyJump bastion-a
    IdentityFile ~/.ssh/dba_ed25519

这里有两个容易漏掉的事实:目标服务器的 User 不会自动继承跳板机的用户,目标私钥也不一定和跳板机相同。若跳板机使用 2FA,认证对话框会先出现;完成第一跳认证后,客户端才会继续到目标。把验证码填错窗口,常常会被误判成"目标服务器拒绝登录"。

第二跳还可能使用不同的主机密钥校验文件。自动化环境里,如果 ProxyJump 由专用用户运行,读取到的 known_hosts、代理套接字和个人电脑不同;本地测试通过并不能证明流水线也通过。部署前我会用和任务相同的用户、相同的配置文件做一次只读连接,确认"人工路径"和"自动路径"没有悄悄分叉。

多跳链路变慢时,先算路径再谈性能

每增加一台跳板机,就增加一段网络延迟和一个可能断开的点。官方文档给出的近似关系是:总延迟约等于本地到 A、A 到 B、B 到目标的延迟之和。它不是精确性能模型,却足够帮助判断"偶尔卡住"是不是链路变长导致的。

我会给每一跳单独测一次:

bash 复制代码
ping -c 3 bastion-a
ssh bastion-a 'ping -c 3 10.30.8.12'

第二条命令依赖跳板机允许执行 ping,失败时不要直接结论为网络不通,可能只是 ICMP 被禁用。可以改成在跳板机上运行 TCP 探测。对真正的 SSH 连接,再加 -oConnectTimeout=5 让失败更快暴露,避免一个超时把整个发布窗口拖住。

超时值也不宜无限缩短。跨地域链路或需要人工输入 2FA 的跳板,连接建立本来就可能比内网直连慢。我的做法是把"快速失败"只用于网络探测,把正式登录的超时交给链路实际延迟决定,并在发布脚本里记录每一跳耗时。这样不会因为一个过于激进的 3 秒阈值,把可用但稍慢的路径误报成故障。

认证失败,先看是哪一把钥匙在说话

多跳场景里的"Permission denied"不一定来自目标服务器。第一跳密钥错误、第二跳要求密码、目标只接受另一把公钥,都会产生相似的终端提示。详细日志会显示当前尝试了哪一个身份文件:

bash 复制代码
ssh -vvv -J bastion-a dba@10.30.8.12

如果配置了多个 IdentityFile,客户端可能按顺序尝试多把钥匙,直到触发服务端的认证次数限制。可以用 IdentitiesOnly yes 把范围收窄:

sshconfig 复制代码
Host db-prod
    IdentitiesOnly yes
    IdentityFile ~/.ssh/dba_ed25519

收窄身份范围是为了让"哪一把钥匙失败"变得可观察。确认原因后,再决定是否需要 ssh-agent、FIDO 密钥或第二跳的独立凭据。不要为了绕过限制,把目标私钥复制到跳板机磁盘上;那会改变密钥的暴露边界。

使用 ssh-agent 时还要确认转发是否真的被请求。没有转发,目标服务器看不到本地代理里的钥匙;开启转发,又会把代理能力带到跳板机上。团队通常会根据堡垒机策略决定是否允许这件事,我不会把 ForwardAgent yes 当成连接失败的通用修复。需要临时转发时,应该绑定到单个主机块,并在窗口结束后关闭。

转发权限被关掉时,客户端再聪明也没用

ProxyJump 依赖跳板机为下一段连接提供转发通道。若服务端限制了 TCP 转发,或者安全策略只允许跳板机访问一小段网段,本地配置再正确也会失败。此时应让负责跳板机的人确认 AllowTcpForwarding、目标网段 ACL 和审计策略,而不是直接打开所有转发。

如果组织使用了堡垒机审计,跳板机可能只允许固定目标、固定端口或固定时间段。把 db-prod 的地址改成另一个内网 IP 进行"试试看",反而会触发更严格的拦截。排查时保留原始目标和工单编号,方便把日志中的拒绝事件对上具体操作。

在 Xterminal 这类连接管理工具中,可以先保存跳板机,再在目标连接的跳板机选项里按顺序添加它们。工具能够减少手工输入和重复配置,但转发权限仍由服务器策略决定。图形界面显示"已连接"通常只对应当前会话,不代表后台的每个目标端口都已放行。

如果采用图形连接中心,我会给每一跳写清楚区域、用途和允许的目标,例如"华东生产跳板 -> 数据库网段",而不是只写一个模糊的"跳板"。名称越具体,批量操作时越不容易误选。连接分组可以帮助管理配置,却不能替代网络团队对可达网段的授权记录。

反例:把目标端口改成 22,问题反而被藏起来

有些团队为了让探测命令"看起来成功",会把数据库端口临时写成 SSH 的 22。这样确实可能连到跳板机上的 SSH 服务,却完全没有验证目标数据库是否可达。过几分钟再把端口改回去,现场只剩下一串互相矛盾的日志。

更好的做法是明确记录每一段的目的:跳板机到目标的 22 端口用于 SSH,目标数据库的 3306 或 5432 另行测试。端口对应服务,测试结果才有解释空间。若必须通过隧道访问数据库,也先证明隧道的两端都建立,再测试本地映射端口:

bash 复制代码
ssh -N -L 13306:10.30.8.12:3306 bastion-a
nc -vz 127.0.0.1 13306

这条命令只建立本地转发,不会替你验证数据库账号。账号失败和隧道失败要分开记录。

本地端口也要避免和已有服务冲突。13306 只是示例,如果本机已经有进程监听它,SSH 可能在建立转发时直接失败,或者你连到的是另一个应用。先用 lsof -nP -iTCP:13306 -sTCP:LISTEN 检查监听者,再选择一个临时端口,并把端口映射写进发布记录。

我会在什么时候停止继续加跳板机

一条链路里,跳板机不应无限增加。官方文档建议跳板机数量控制在较少范围,并提醒任一跳板机断开都会导致整体连接中断。我的停止条件是:已经能在每一跳证明下一跳地址和端口可达;认证使用明确的用户和密钥;转发权限有负责人确认;再增加一跳不会带来新的网络隔离价值。

如果第一跳能连、第二跳端口不通,先修路由或 ACL;如果端口通但认证失败,检查目标用户和密钥;如果两者都通过却在长时间运行中断开,才去看心跳、连接复用和中间设备超时。按这个顺序,通常不需要把整个链路拆掉重建。每一段的结果都应写入发布记录,形成可回看的内网服务器连接证据。

当故障已经定位到目标服务器内部,例如 sshd 没有监听预期端口或本地防火墙拒绝连接,跳板配置就暂时冻结,不要继续改动。先恢复目标服务,再重新验证从跳板机到目标的单段路径。一次只改一个变量,才能知道是哪项变更让连接重新变绿。

回到开头的 bastion-adb-prod,绿色的第一段从来没有承诺第二段也会通。把路径拆开、把每个端口对应到具体服务、把每把密钥绑定到正确用户,才是多跳 SSH 真正可复用的排查方法。等每一段都能说清楚,再把配置放回连接中心,发布窗口才不会被一次模糊超时反复打断。

相关推荐
不剪发的Tony老师2 天前
Navop:一款工具搞定数据库、SSH、SFTP、远程桌面、AI Agent
运维·数据库·ssh
与自己和解7413 天前
使用ssh实现 VScode 直连 ubuntu
vscode·ubuntu·ssh
lingran__5 天前
Git 完全指南(三):远程仓库与标签管理
开发语言·git·gitee·ssh·团队协作·远程仓库·分布式版本控制
Tassel_YUE5 天前
非 ansible 主机批量执行命令方法:psssh 和 pscp(随手记)
linux·网络·ssh·scp·ansible
JZZC25 天前
1.1 基础的SSH配置
计算机网络·ssh
深念Y7 天前
Windows 11 工具链部署与 SSH 踩坑笔记
windows·笔记·ssh
mooooooooooye8 天前
2026 年跨平台 SSH 客户端怎么选?Xterminal、Termius、MobaXterm 谁更合适
服务器·网络·ssh
小王C语言8 天前
Windows 无法使用 ssh 连接虚拟机(ubuntu),网络没问题的情况下
运维·ssh
mooooooooooye9 天前
SSH 工具越用越乱,Xterminal 先清掉这三类失效连接
运维·ssh·github
梓沂10 天前
SSH 公钥认证配置指南:从生成密钥到配置 SFTP 服务器
ssh