2.11 SSH 深入——隧道、跳板机与配置管理

预计阅读时间:15 分钟

📖 目录

学习目标

学完本章后,你将能够:

  • 编写结构化的 ~/.ssh/config 文件管理多台服务器
  • 使用 -L-R-D 实现本地/远程/动态端口转发
  • 通过 ProxyJump 穿透多层网络访问内网服务器
  • 理解 SSH 代理转发的原理与安全风险并正确使用
  • 搭建 SSH 证书认证体系管理大规模服务器集群

核心知识

  • SSH 配置文件~/.ssh/config)——按 Host 别名分组,支持通配符,集中管理所有 SSH 连接参数
  • 本地端口转发-L)——将远程端口映射到本机,用于访问内网服务
  • 远程端口转发-R)——将本机端口暴露到远程,用于穿透 NAT
  • 动态端口转发-D)——创建 SOCKS5 代理隧道,加密所有应用层流量
  • ProxyJump-J)——通过跳板机透明连接目标主机,私钥不离开本机
  • 代理转发(Agent Forwarding)——在跳板机上借用本地 SSH Agent 完成二次认证
  • 证书认证(Cert Auth)——由 CA 统一签发用户证书,替代分散的 authorized_keys 管理
  • 多路复用(ControlMaster)——复用同一 TCP 连接减少握手开销

知识关联

原理讲解

SSH 协议从建立连接到数据传输经历三个阶段:

阶段协议层作用使用的加密技术
传输层握手TCP → SSH协商密钥交换算法、主机密钥认证、建立加密隧道Diffie-Hellman(密钥交换)、RSA/Ed25519(主机身份)
用户认证SSH 内部验证用户身份(密码、公钥、证书、GSSAPI 等)RSA/Ed25519(签名验证)
连接复用SSH 通道在同一加密隧道上承载多个 channel(shell、exec、forwarded connections)对称加密(AES/ChaCha20)

端口转发的工作原理:SSH 服务器在内部维护一个转发表,监听指定端口并将进入的数据包通过已建立的加密通道转发到目标地址。本地转发(-L)在客户端监听,远程转发(-R)在服务器端监听,动态转发(-D)在客户端创建一个 SOCKS 代理,由 SOCKS 协议告知目标地址。

ProxyJump 与 ProxyCommand 的对比:ProxyJump(SSH 7.3+)是内置的简化写法,本质上是让 SSH 先连到跳板机,再通过 -W 标志将 stdin/stdout 代理到目标主机。与传统的 ssh -W %h:%p jumper ProxyCommand 效果相同,但更简洁且支持多跳。

SSH Agent 的工作原理ssh-agent 是一个长期运行的后台进程,加载私钥后通过 Unix socket(通常是 $SSH_AUTH_SOCK)对外提供签名服务。Agent Forwarding 将本地的 socket 文件描述符转发到远程,让远程 SSH 客户端可以请求本地 agent 签名——密钥本身从不离开本机。

SSH 握手完整流程与 known_hosts

一次 SSH 连接从 TCP 建立到登录成功,要经历以下步骤:

  1. TCP 三次握手连接 22 端口;
  2. 双方交换版本字符串(如 SSH-2.0-OpenSSH_9.6);
  3. 算法协商:客户端发送支持的 KEX 算法、主机密钥类型、加密算法、MAC、压缩算法列表,服务器选双方交集,得出一组会话算法;
  4. 密钥交换:通过 Curve25519-sha256 等 ECDH 算法协商出会话密钥,服务器用主机私钥对交换材料签名,证明自己持有对应的主机公钥;
  5. 主机验证:客户端校验服务器主机公钥——首次连接时提示指纹,之后与 ~/.ssh/known_hosts 比对,不一致则警告"REMOTE HOST IDENTIFICATION HAS CHANGED"(疑似中间人攻击);
  6. 用户认证(公钥 / 密码 / 证书 / OTP);
  7. 建立 channel,进入交互会话。

known_hosts 条目格式为 主机名 算法 公钥,启用 HashKnownHosts yes 后以 |1|... 哈希形式存储,防止主机列表泄露。验证指纹的命令:ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub 生成的指纹应与首次连接提示一致——这是防中间人攻击的第一道闸门。StrictHostKeyChecking=no 会静默接受任何主机密钥,等于放弃这道防线,仅建议在一次性容器环境使用。

SSH 配置指令速查

指令作用常用值
Host匹配段,支持通配符 * ?prod-*10.*
HostName实际连接地址IP 或域名
User / Port默认用户 / 端口deploy / 2222
IdentityFile指定私钥路径~/.ssh/id_ed25519
ProxyJump跳板机链(可逗号分隔多跳)bastionuser@host:port
ServerAliveInterval保活探测间隔(秒)60
ConnectTimeout连接超时(秒)10
ControlMaster / ControlPersist连接复用开关与保持时间auto / 10m
ForwardAgent / ForwardX11代理转发 / X11 图形转发no / yes
StrictHostKeyChecking主机密钥校验策略yes / accept-new / no
Compression链路压缩(低带宽友好)yes

配置匹配规则:先出现的段落优先生效,所以 Host * 通配段通常放最底部做默认值。调试时用 ssh -G 别名 查看某个别名最终生效的完整参数,避免"配置没生效"的困惑。

示例代码

~/.ssh/config 结构化配置

# ~/.ssh/config — 按环境分组的配置示例

# 全局默认
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    Compression yes
    StrictHostKeyChecking accept-new

# 跳板机
Host bastion
    HostName bastion.example.com
    User admin
    Port 2222
    IdentityFile ~/.ssh/bastion_ed25519

# 生产环境
Host prod-*
    User deploy
    ProxyJump bastion
    ForwardAgent no

# 内网机器通配
Host 10.*
    User ops
    IdentityFile ~/.ssh/ops_ed25519

# 别名示例
Host web01
    HostName 192.168.1.10
    User deploy
    ProxyJump bastion
ssh web01
# 输出:
# Last login: Thu Jul 30 09:15:22 2026 from 10.0.0.1
# [deploy@web01 ~]$

本地端口转发(-L)

# 将远程 MySQL (127.0.0.1:3306) 映射到本地 3307
ssh -L 3307:127.0.0.1:3306 user@db-server

# 另一个终端连接
mysql -h 127.0.0.1 -P 3307 -u root -p

远程端口转发(-R)

# 将本地开发服务 (:80) 暴露到公网服务器 8080 端口
ssh -R 8080:127.0.0.1:80 user@public-server

# 公网用户访问
curl http://public-server:8080

动态转发(-D)SOCKS 代理

# 创建 SOCKS5 代理隧道
ssh -D 1080 user@remote-server

# 浏览器配置
firefox --proxy-server='socks5://127.0.0.1:1080'
curl --socks5 127.0.0.1:1080 https://checkip.amazonaws.com
# 输出:
# 

ProxyJump 多跳连接

# 单跳
ssh -J user@bastion:2222 deploy@10.0.1.50

# 多跳
ssh -J user@bastion,user@internal-gw deploy@10.0.2.100

# 等价 config 配置
Host internal-*
    ProxyJump bastion
    User deploy

SSH 证书认证

# 1. 创建 CA(安全机器上操作)
ssh-keygen -t ed25519 -f ~/ssh-ca -C "SSH CA for ops team"

# 2. 服务器信任 CA
echo "TrustedUserCAKeys /etc/ssh/ca.pub" >> /etc/ssh/sshd_config
cp ~/ssh-ca.pub /etc/ssh/ca.pub
systemctl restart sshd

# 3. 签发用户证书
ssh-keygen -s ~/ssh-ca -I "alice@ops" -n "deploy,admin" -V "+52w" ~/alice.pub

# 4. 用户将 alice-cert.pub 放入 ~/.ssh/ 即可登录
ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub
# 输出:
# ssh-ed25519-cert-v01@openssh.com
#     Type: ssh-ed25519-cert-v01@openssh.com
#     Key ID: "alice@ops"
#     Principals: deploy, admin
#     Valid from: 2026-07-30T00:00:00
#     Valid to:   2027-07-29T00:00:00

SSH 安全加固

# /etc/ssh/sshd_config — 生产环境推荐配置
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin no
AllowUsers deploy admin
ClientAliveInterval 300
ClientAliveCountMax 0
AllowTcpForwarding yes
AllowAgentForwarding no

Match User backup
    ForceCommand /usr/local/bin/rsync --server
sshd -t && systemctl reload sshd
# 输出: (无输出表示语法正确)

隧道模式选型对照

模式监听位置方向典型场景命令
本地转发 -L客户端拉取远程内网服务访问被防火墙挡住的 MySQL / Redis / 管理面板ssh -L 3307:内网DB:3306 jump
远程转发 -R服务器暴露本地服务到公网内网开发机让公网同事访问、NAT 穿透ssh -R 8080:127.0.0.1:80 vps
动态转发 -D客户端全流量 SOCKS5加密上网、临时代理、绕过地域限制ssh -D 1080 vps

提示:-L / -R 是"点对点映射",只代理指定端口;-D 是"整机代理",浏览器、git、curl 等应用配合 SOCKS5 即可透明使用。隧道与连接共用一个 SSH 会话,断开即失效——需要长期驻留时用 autossh -M 0 自动重连。

ssh-agent 使用

# 启动 agent 并加载私钥
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -l              # 列出已加载密钥
ssh-add -t 8h ~/.ssh/id_ed25519   # 8 小时后自动忘记密钥

# 免密码批量执行
ssh web01 'hostname'
ssh web02 'hostname'

# 临时使用指定密钥(不污染 agent)
ssh -i ~/.ssh/ci_key user@server

# systemd 用户会话自动启动 agent
# ~/.config/systemd/user/ssh-agent.service
# [Unit]
# Description=SSH key agent
# [Service]
# ExecStart=/usr/bin/ssh-agent -a %t/ssh-agent.socket
# Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
# [Install]
# WantedBy=default.target
systemctl --user enable --now ssh-agent.service

# 安全提示:Agent Forwarding(ssh -A)只在信任的中间机上开启

scp / sftp / rsync 对比与选择

工具传输方向增量 / 断点压缩典型场景
scp单文件 / 目录(双向)不支持手动 -C一次性小文件拷贝
sftp交互式(双向)不支持需要浏览远端目录树的交互操作
rsync单向同步支持(差异传输 + 断点续传)-z备份、大批量同步、增量部署
# scp 基础用法
scp -P 2222 backup.tar.gz user@server:/data/
scp -r ./configs/ user@server:/etc/myapp/
scp user@server:/var/log/app.log .

# sftp 交互会话
sftp user@server
# sftp> ls /data
# sftp> get /data/app.log
# sftp> put backup.tar.gz /data/
# sftp> bye

# rsync 是主力:增量 + 压缩 + 保持属性 + 断点续传
rsync -avzP --delete ./web/ deploy@server:/var/www/
# -a 归档(权限/时间/链接) -v 详细 -z 压缩 -P 进度+断点续传
# --delete 删除远端多余文件(务必先 --dry-run 演练)

# rsync 走 SSH 跳板
rsync -avz -e "ssh -J bastion" ./data/ user@internal:/data/

# 只打印将传输的文件,不实际执行
rsync -av --dry-run ./src/ user@server:/src/

SSH 加固进阶:fail2ban 联动

# 安装 fail2ban
sudo apt install fail2ban

# /etc/fail2ban/jail.local
# [sshd]
# enabled  = true
# port     = ssh
# maxretry = 3          # 5 分钟内失败 3 次
# bantime  = 3600       # 封禁 1 小时
# findtime = 300        # 统计窗口 5 分钟

sudo systemctl enable --now fail2ban

# 查看封禁状态
sudo fail2ban-client status sshd
# 输出: Banned IP list: 203.0.113.7

# 误封解封
sudo fail2ban-client set sshd unbanip 203.0.113.7

# 手动封禁
sudo fail2ban-client set sshd banip 198.51.100.9

# Debian 12+ 默认 nftables 后端,可见规则:
# nft list ruleset | grep fail2ban

多因素认证:TOTP + SSH

# 服务端安装 google-authenticator
sudo apt install libpam-google-authenticator
google-authenticator        # 生成密钥,按提示扫描二维码
# 输出:
# Your new secret key is: XXXXXXXXXXXXXXXXXXXX
# 二维码 / emergency scratch codes(务必离线保存)

# /etc/pam.d/sshd 文件顶部加入:
# auth required pam_google_authenticator.so nullok

# sshd_config 中启用键盘交互并组合因子:
# KbdInteractiveAuthentication yes
# PasswordAuthentication no
# AuthenticationMethods publickey,keyboard-interactive
sudo systemctl reload sshd

# 效果:公钥 + 6 位动态码双因子,密码认证保持关闭
# 验证:ssh user@server 先验证密钥,再提示输入 6 位验证码

# 多服务器共享同一密钥:把 ~/.google_authenticator 分发到各服务器
# 注意:密钥文件权限必须为 600,属主为登录用户

常见错误

错误表现根因正确做法
Bad owner or permissions on ~/.ssh/config~/.ssh/config~/.ssh/ 目录权限过大chmod 600 ~/.ssh/config && chmod 700 ~/.ssh
端口转发成功但连接被拒绝转发目标地址写成了 localhost,在远端语境下指向了跳板机自身确认目标服务监听地址,转发地址使用实际监听地址而非 127.0.0.1
Permission denied (publickey) 但密钥已上传公钥未追加到 authorized_keys,或 ~/.ssh 权限不对使用 ssh-copy-id 自动处理,或手动检查 authorized_keys 权限为 600
ProxyJump 报 unknown option客户端 SSH 版本低于 7.3,不支持 -J 参数升级 OpenSSH 或改用 -o ProxyCommand="ssh -W %h:%p jump"
Agent Forwarding 在跳板机上报 ssh-agent: no such file or directory跳板机未启动 ssh-agent,或 $SSH_AUTH_SOCK 未正确传递跳板机上 eval $(ssh-agent) 启动 agent;客户端用 ssh -A 明确开启转发

最佳实践

建议原理示例
始终使用 Ed25519 密钥性能比 RSA 快 3 倍,安全性更高,密钥更短ssh-keygen -t ed25519
用 ProxyJump 代替 Agent Forwarding私钥不离开本机,减少被窃取的风险Host target\n ProxyJump bastion
配置文件按环境分组 + 通配符减少重复配置,新加主机只需改一行Host prod-*; Host 10.*; Host *.internal
启用 ControlMaster 复用连接避免重复 TCP 握手,批量操作速度提升 5-10 倍ControlMaster auto; ControlPersist 10m
修改 sshd_config 后先开逃生终端再 reload防止配置错误导致自己被锁在门外第二个窗口 ssh -o ConnectTimeout=5 localhost 确认可登录

练习题

  1. (概念)SSH 本地转发(-L)和远程转发(-R)的核心区别是什么?分别适用于什么场景?
  2. (概念)ProxyJump 相比于传统的 Agent Forwarding 有什么安全优势?
  3. (实操)编写一个 ~/.ssh/config,包含:全局保活间隔 60 秒;跳板机别名 jump 指向 jump.example.com:2222,用户 admin;内网 10.*.*.* 均通过 jump 跳转,用户 ops,密钥 ~/.ssh/ops_ed25519
  4. (实操)通过一条 SSH 命令在本地开启 1080 端口的 SOCKS5 代理,并用 curl 测试是否正常工作。
  5. (🔍 挑战)某公司有 200 台 Linux 服务器,之前用 ssh-copy-id 逐台分发公钥。现在需要实现:集中证书管理、用户证书有效期 90 天自动失效、支持吊销离职员工证书。请描述完整的 SSH 证书认证实施方案。
点击查看答案
  1. 本地转发-L 本地端口:目标地址:目标端口 跳板)在客户端监听,把远程服务映射到本地,适用于访问内网服务。远程转发-R 远程端口:目标地址:目标端口 跳板)在服务器端监听,把本地服务暴露到远程,适用于穿透 NAT。
  2. ProxyJump 在客户端完成身份认证,私钥和 agent socket 始终留在本地。Agent Forwarding 将 $SSH_AUTH_SOCK 传递到跳板机,跳板机上的 root 用户可以通过该 socket 冒充你连接其他服务器。
  3. 参考本章「示例代码」中 ~/.ssh/config 一节的配置。
  4. ssh -D 1080 user@remote-server,然后 curl --socks5 127.0.0.1:1080 https://checkip.amazonaws.com,输出应为 remote-server 的公网 IP。
  5. 在一台安全机器上创建 CA 密钥对;在所有服务器 /etc/ssh/sshd_config 中添加 TrustedUserCAKeys /etc/ssh/ca.pub 并重启 sshd;为每个员工签发带 -V "+12w"(90 天)有效期的用户证书;员工离职时在 CA 侧用 -z 递增序列号签发同一公钥的 -V "-1s" 使之前证书失效。

学习检查点

学完本章后,请检验自己是否掌握以下内容:

检查项自测问题验证方法
概念理解能用自己的话解释本地转发(-L)、远程转发(-R)、动态转发(-D)的区别尝试向他人讲解
命令操作能不查文档配置 ~/.ssh/config 实现多台服务器的便捷管理在终端实际执行
原理掌握能说出 SSH 握手流程和 known_hosts 防中间人攻击的原理画出握手流程图
故障排查能独立排查 SSH 连接超时、密钥认证失败等常见问题模拟故障并修复
最佳实践能说明为什么 ProxyJump 比手动嵌套 SSH 更安全可靠对比不同跳板方式

本章总结

SSH 远不止一条 ssh user@host 命令。本章从 ~/.ssh/config 的配置管理切入,深入讲解了三种端口转发的使用场景与原理,再到 ProxyJump 多层穿透、证书认证等企业级能力。掌握这些技术后,你将能从「能用 SSH 登录」进阶为「用 SSH 构建安全的远程网络」。

速查表

功能命令 / 配置速记口诀
配置别名~/.ssh/configHost + HostName配置写好,输入减少
本地转发ssh -L 本地端口:目标:端口 跳板远程拉到本地
远程转发ssh -R 远程端口:本地:端口 跳板本地推到远程
SOCKS 代理ssh -D 1080 服务器一条隧道,全流量加密
跳板机ssh -J 跳板 目标ProxyJump一步穿透,密钥留本地
代理转发ssh -AForwardAgent yes借用本地 Agent
证书认证ssh-keygen -s CA -I "id" -n "user" -V "+52w" key.pubCA 统一签发,到期自动失效
多路复用ControlMaster auto; ControlPersist 10m复用连接,提速 N 倍

SSHFP DNS 记录

SSHFP 记录将 SSH 服务器公钥指纹发布到 DNS,客户端首次连接时可通过 DNS 验证服务器身份,防止中间人攻击:

# 生成 SSHFP 记录
ssh-keygen -r myserver.example.com

# 输出示例(添加到 DNS 区域文件)
myserver.example.com IN SSHFP 4 2 123456789abcdef...

# 客户端启用 SSHFP 验证(~/.ssh/config)
Host myserver
    VerifyHostKeyDNS yes

# 或全局启用
# /etc/ssh/ssh_config 中添加:
# VerifyHostKeyDNS yes

Mosh——移动 Shell 替代方案

Mosh(Mobile Shell)基于 UDP 传输,解决 SSH 在高延迟/不稳定网络下的卡顿问题:

# 安装
sudo apt install mosh          # Ubuntu
brew install mosh              # macOS

# 连接(使用 UDP 60000-61000 端口)
mosh user@server

# 指定端口
mosh -p 2222 user@server

# 使用 SSH 密钥认证
mosh --ssh="ssh -i ~/.ssh/id_ed25519" user@server
SSHMosh
传输层TCPUDP(SSP 协议)
漫游断线需重连自动重连,IP 变化无感
延迟体验每按键等 RTT本地预测渲染,即时反馈
端口转发支持不支持
Agent 转发支持不支持
使用场景 Mosh 适合不稳定网络(咖啡厅、移动热点)下的日常终端操作。需要端口转发、Agent 转发或 SCP 时仍使用 SSH。

学习路径

延伸阅读

  • 官方文档man ssh_configman sshd_configman ssh-keygen — OpenSSH 手册页是最权威的参考
  • RFC 4251:SSH Protocol Architecture — 协议设计的完整技术细节
  • 推荐章节5.1:Linux 安全加固 Linux 安全加固、5.10:WireGuard VPN WireGuard VPN、4.1:Ansible 自动化 Ansible 自动化运维
  • 推荐书籍:《SSH Mastery》by Michael W. Lucas — 最全面的 SSH 实践指南

常见问题

SSH 端口转发有什么实际应用?
三种转发对应不同场景:本地转发(-L)将本地端口映射到远程主机,用于访问内网服务。远程转发(-R)将远程端口映射到本地,用于外网访问内网(如 ngrok 替代方案)。动态转发(-D)创建 SOCKS5 代理,浏览器配置后用 SSH 做加密通道。日常排障中最常用的是本地转发访问数据库。
ProxyJump 和 ProxyCommand 有什么区别?
ProxyJump(-J)是 OpenSSH 7.3+ 引入的简洁语法:ssh -J jump@堡垒机 user@目标主机。ProxyCommand 是传统的灵活方式:ssh -o ProxyCommand="ssh -W %h:%p jump@堡垒机" user@目标主机。推荐使用 ProxyJump,配置更清晰。在 ~/.ssh/config 中配置 ProxyJump 跳板机。
SSH 证书认证比密钥认证好在哪里?
SSH 证书(CA 签名)的核心优势:① 集中管理:CA 服务器签名用户公钥,无需在每台服务器上部署 authorized_keys;② 有效期控制:证书自带过期时间;③ 撤销能力:吊销列表(KRL)可撤销单个证书;④ 免交互部署:新服务器上线时自动信任 CA 公钥即可。适合 10+ 台服务器的环境。
↑ 回到顶部