5月28日 00:53

什么是 SSH 隧道和跳板机?如何配置多级跳板连接?

SSH 隧道是通过 SSH 协议在客户端和服务器之间建立加密通道的技术,所有经过该通道的流量都受到加密保护。跳板机(Jump Host / Bastion Host)是部署在网络边界的唯一入口,外部用户必须先登录跳板机才能访问内网服务器。两者结合是企业运维中实现安全远程访问的标准方案——绝大多数生产环境都不允许直连内网服务器,必须经过跳板机中转。

SSH 隧道的三种转发模式

理解 SSH 隧道的核心在于区分三种端口转发方式,它们解决不同的问题,选错模式会导致连接不通或不必要的复杂性。

本地端口转发(-L)

将本地某个端口的流量,通过 SSH 连接转发到远端服务器的指定端口。典型场景:从本地机器访问远端内网的数据库、Redis 等服务。

bash
# 在本地 3306 端口访问内网 MySQL ssh -L 3306:db-server:3306 user@bastion -N # 连接后,本地执行 mysql -h 127.0.0.1 即可访问远程数据库

-L 格式为 本地端口:目标主机:目标端口,其中"目标主机"是从 SSH 服务端(跳板机)的视角解析的,不是从本地解析——这是最常踩的坑。

远程端口转发(-R)

将远端服务器的某个端口流量,通过 SSH 连接转发到本地指定端口。典型场景:让远端服务器访问你本地运行的服务,比如本地开发环境的 Web 服务需要被远端回调。

bash
# 让远端服务器通过 8080 端口访问你本地的 Web 服务 ssh -R 8080:localhost:3000 user@remote-server -N # 远端服务器上访问 localhost:8080 即可到达你本地的 3000 端口

注意:远程转发需要 SSH 服务端开启 GatewayPorts 选项,否则默认只绑定到远端的 localhost

动态端口转发(-D)

在本地创建一个 SOCKS5 代理,所有通过该代理的流量都会经由 SSH 连接转发。典型场景:需要通过跳板机访问内网多个服务,不想为每个服务单独配一条 -L 规则。

bash
# 在本地 1080 端口创建 SOCKS5 代理 ssh -D 1080 user@bastion -N # 配置浏览器或 curl 使用 socks5://127.0.0.1:1080 代理 curl --socks5 127.0.0.1:1080 http://internal-web:8080

选取原则:访问单个固定服务用 -L,需要暴露本地服务给远端用 -R,需要灵活访问多个内网服务用 -D-N 参数表示不打开远程 shell,仅做端口转发,生产环境推荐加上。

跳板机配置方式

ProxyJump(推荐)

OpenSSH 7.3+ 支持,是最简洁的配置方式。-J 参数直接指定跳板机,SSH 客户端自动处理中间的连接建立,流量端到端加密,中间跳板机无法解密。

bash
# 命令行 ssh -J jump-user@jump-host:22 target-user@target-host # 配置文件(~/.ssh/config) Host jump-host HostName jump.example.com User jump-user Host target-host HostName target.example.com User target-user ProxyJump jump-host

配置完成后,ssh target-host 即可自动经跳板机连接目标服务器,scp、rsync、sftp 等工具同样适用。

ProxyCommand(兼容旧版本)

适用于 OpenSSH 7.3 以下版本,通过 ssh -W 参数在跳板机上建立 TCP 转发通道。

bash
# 命令行 ssh -o ProxyCommand="ssh -W %h:%p jump-user@jump-host" target-user@target-host # 配置文件 Host target-host HostName target.example.com User target-user ProxyCommand ssh -W %h:%p jump-user@jump-host

%h%p 是占位符,分别代表最终目标的主机名和端口,SSH 客户端会自动替换。ProxyCommand 的优势是可以在命令中嵌入更复杂的逻辑,比如根据网络环境选择不同的跳板路径。

多级跳板配置

当网络拓扑存在多层隔离(如 DMZ → 应用区 → 数据区)时,需要串联多个跳板机。ProxyJump 天然支持链式配置。

bash
# 命令行:两级跳板,用逗号分隔 ssh -J jump1@host1,jump2@host2 target@final-host # 配置文件:逐层声明依赖关系 Host jump1 HostName host1.example.com User jump1 Host jump2 HostName host2.example.com User jump2 ProxyJump jump1 Host final HostName final.example.com User target ProxyJump jump2

关键在于配置文件中的"依赖链":final → jump2 → jump1。每层只需声明自己的上一跳,SSH 会递归建立连接。所有经过跳板机的流量都是端到端加密的,中间跳板机只能看到加密数据流。

典型使用场景

通过跳板机访问内网数据库

结合本地端口转发和 ProxyJump,将内网数据库映射到本地端口,用本地客户端工具直连。

bash
ssh -L 3306:db-server:3306 -J bastion@bastion.example.com user@bastion.example.com -N

连接后,本地 mysql -h 127.0.0.1 -P 3306 直接访问远程数据库,对本地客户端完全透明。

通过跳板机传输文件

scp 和 rsync 原生支持 ProxyJump,无需额外配置。

bash
# scp 传输 scp -o ProxyJump="bastion@bastion.example.com" local-file admin@internal-server:/path/ # rsync 同步 rsync -avz -e "ssh -J bastion@bastion.example.com" ./src/ admin@internal-server:/path/

用 SOCKS 代理浏览内网 Web 服务

当你需要通过浏览器访问内网多个 Web 应用时,动态转发比逐一配置本地端口更方便。

bash
ssh -D 1080 -J bastion@bastion.example.com user@bastion.example.com -N

在浏览器代理设置中配置 SOCKS5:127.0.0.1:1080,即可访问所有内网 Web 服务。Chrome 可配合 SwitchyOmega 插件实现按规则自动代理。

用配置文件简化日常操作

将常用服务器和跳板关系写入 ~/.ssh/config,日常操作只需 ssh <别名>

bash
Host bastion HostName bastion.example.com User bastion IdentityFile ~/.ssh/bastion_key Host web-server HostName 192.168.1.100 User webadmin ProxyJump bastion IdentityFile ~/.ssh/internal_key Host db-server HostName 192.168.1.200 User dbadmin ProxyJump bastion IdentityFile ~/.ssh/internal_key

这样 ssh web-serverssh db-server 都会自动经 bastion 跳转,VS Code Remote-SSH 也会自动读取该配置。

密钥转发与安全要点

SSH Agent 转发

当跳板机不允许存放私钥时,可以通过 Agent 转发在跳板机上使用本地的 SSH 密钥,私钥始终留在本地。

bash
ssh -A -J bastion@bastion.example.com target@internal-server

安全提醒:Agent 转发存在风险——如果跳板机被入侵,攻击者可以利用转发的 Agent 连接你能访问的其他服务器。仅在可信跳板机上启用,用完及时关闭。

跳板机加固配置

bash
# /etc/ssh/sshd_config PasswordAuthentication no # 禁用密码登录 PermitRootLogin no # 禁止 root 登录 AllowUsers bastion # 限制可登录用户 MaxAuthTries 3 # 限制认证尝试次数 ClientAliveInterval 300 # 空闲连接超时检测

更完善的方案是部署堡垒机(如 Teleport、JumpServer),提供会话录制、访问审计和多因素认证。

密钥管理实践

  • 跳板机和目标服务器使用不同的密钥对,单密钥泄露不会影响全局
  • 私钥必须设置密码短语(passphrase),用 ssh-keygen -p 添加
  • 定期轮换密钥,使用 ssh-keygen -R 清理旧授权
  • ssh-add -l 检查 Agent 中加载的密钥,避免残留

连接复用与性能优化

每次建立 SSH 连接都要完成 TCP 握手和密钥交换,频繁连接时开销明显。SSH 支持连接复用(Multiplexing),复用已有连接的通道建立新会话,后续连接几乎瞬时完成。

bash
# ~/.ssh/config Host * ControlMaster auto ControlPath ~/.ssh/cm-%r@%h:%p ControlPersist 600

ControlPersist 600 让最后一个会话关闭后连接保持 600 秒,避免频繁重建。其他优化手段:-C 启用压缩(低带宽高延迟网络有效),ServerAliveInterval 60 保持心跳防止连接断开。

常见问题排查

连接失败时,-v / -vv / -vvv 是第一工具,逐级增加调试信息量。

bash
ssh -vvv -J jump@jump-host target@target-host

常见错误及对策

  • Permission denied (publickey):密钥未正确配置。检查 ssh-add -l 是否加载了对应密钥,目标服务器的 ~/.ssh/authorized_keys 是否包含公钥,文件权限是否正确(目录 700,authorized_keys 600)
  • Connection refused:目标 SSH 服务未运行或防火墙阻止。检查 systemctl status sshd 和端口开放情况
  • open failed: connect failed:端口转发目标不可达。从跳板机上 telnet 目标 端口 确认能否连通
  • Broken pipe:连接空闲超时断开。配置 ServerAliveInterval 60ServerAliveCountMax 3 保持心跳
  • 配置文件不生效~/.ssh/config 的缩进必须用空格不能用 Tab;~/.ssh/ 目录权限 700;config 文件权限 644
标签:SSH