一、一次 SSH 爆破引发的血案
去年某客户的测试服务器被勒索病毒加密,排查日志发现攻击者用的不是什么 0day,就是最朴素的 SSH 密码爆破——22 端口暴露在公网,root 密码是 Admin@123,fail2ban 也没装。/var/log/secure 里 8 小时内 12 万次失败登录尝试,最后一次成功。
那次之后我把 SSH 安全总结为一句话:端口不是护城河,认证方式才是。 但两者缺一不可——端口改了能挡掉 90% 的脚本小子,密钥认证能挡掉剩下 10% 的暴力破解,审计录屏则保证万一出了事,你能追溯每一个命令。
二、SSH 加固四层防御模型
外部攻击者
│
┌────────▼────────┐
│ 第一层:网络层 │ ← 防火墙白名单 / 安全组
│ 只允许跳板机IP │
└────────┬────────┘
│
┌────────▼────────┐
│ 第二层:认证层 │ ← 密钥登录 + 禁密码
│ ed25519 密钥 │
└────────┬────────┘
│
┌────────▼────────┐
│ 第三层:协议层 │ ← 改端口 + 限root + 超时
│ sshd 精细化配置 │
└────────┬────────┘
│
┌────────▼────────┐
│ 第四层:审计层 │ ← 录屏 + 命令日志
│ Teleport/tlog │
└─────────────────┘
经验法则:四层防御不是"选其中几层做",而是层层叠加。任何一层被突破,后面还有兜底。
三、第一层:网络层 —— 把门先关上
3.1 防火墙白名单
最安全的端口是不开放的端口。生产服务器 SSH 应该只允许跳板机(或运维 VPN 网段)访问:
# iptables:只允许 10.10.4.0/24 网段访问 2222 端口
iptables -A INPUT -p tcp --dport 2222 -s 10.10.4.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 2222 -j DROP
# firewalld 写法
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.10.4.0/24 port port=2222 protocol=tcp accept'
firewall-cmd --permanent --remove-port=22/tcp
firewall-cmd --reload
3.2 安全组(云服务器)
如果是阿里云 / AWS 等云服务器,在安全组里配置更可靠——在内核防火墙之前就把流量挡掉了,不消耗服务器 CPU:
入方向规则:
协议: TCP
端口: 2222
源IP: <跳板机公网IP>/32
策略: 允许
(删除 22 端口的 0.0.0.0/0 规则)
四、第二层:认证层 —— 密钥替代密码
4.1 生成 ed25519 密钥
# 客户端生成密钥对(ed25519 比 RSA 更短更快更安全)
ssh-keygen -t ed25519 -C "ops@scrcx-2026" -f ~/.ssh/id_ed25519_scrcx
# 推荐加 passphrase,防止私钥泄露后直接可用
# 生成后会有两个文件:
# ~/.ssh/id_ed25519_scrcx (私钥,绝对不能传出去)
# ~/.ssh/id_ed25519_scrcx.pub (公钥,放到服务器)
4.2 推送公钥到服务器
# 方法一:ssh-copy-id(交互式,适合初次配置)
ssh-copy-id -i ~/.ssh/id_ed25519_scrcx.pub -p 2222 ops@10.10.4.17
# 方法二:手动写入(适合批量部署)
cat ~/.ssh/id_ed25519_scrcx.pub | ssh -p 2222 ops@10.10.4.17 \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
4.3 密钥权限铁律
# 服务器端权限必须精确,否则 sshd 拒绝读取
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R ops:ops ~/.ssh
# 客户端私钥权限
chmod 600 ~/.ssh/id_ed25519_scrcx
踩坑提示:
authorized_keys权限是 644 时,某些 sshd 版本会静默忽略密钥,回退到密码认证——你以为密钥配好了,实际上一直在用密码。用ssh -vvv排错时关注Authentications that can continue: publickey这行。
4.4 SSH Agent 转发(跳板机场景必备)
# 客户端启动 agent 并加载密钥
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519_scrcx
# 通过跳板机连接目标机时启用转发
ssh -A -J jump@jump.scrcx.cn -p 2222 ops@10.10.4.20
Agent 转发让你在跳板机上不需要存放任何私钥,就能跳到第三台机器——私钥永远只在你的笔记本上。
五、第三层:协议层 —— sshd 精细化配置
5.1 生产级 sshd_config
# /etc/ssh/sshd_config —— 生产环境推荐配置
# === 端口与监听 ===
Port 2222 # 改掉默认 22 端口
ListenAddress 10.10.4.17 # 只监听内网(公网走跳板机)
# === 协议版本 ===
Protocol 2 # 禁用不安全的 Protocol 1
# === 认证 ===
PermitRootLogin no # 禁止 root 直接登录
PasswordAuthentication no # 禁止密码认证(密钥登录后再关)
PubkeyAuthentication yes # 启用密钥认证
PermitEmptyPasswords no # 禁止空密码
MaxAuthTries 3 # 最多尝试 3 次
LoginGraceTime 30 # 30 秒内未认证则断开
# === 会话控制 ===
ClientAliveInterval 300 # 5 分钟无操作发心跳
ClientAliveCountMax 2 # 2 次心跳无响应则断开(=10分钟超时)
MaxSessions 10 # 最大并发会话
MaxStartups 10:30:60 # 防并发爆破(第10个起30%丢弃,60个硬上限)
# === 转发控制 ===
AllowTcpForwarding yes # 跳板机需要,普通服务器可关
X11Forwarding no # 生产环境不需要图形转发
AllowAgentForwarding yes # 跳板机需要 agent 转发
# === 日志 ===
SyslogFacility AUTH
LogLevel VERBOSE # 记录密钥指纹,方便审计
# 注意:不要用 DEBUG3,会记录敏感信息
# === 用户白名单 ===
AllowUsers ops deploy # 只允许这两个用户 SSH 登录
5.2 应用配置并验证
# 先测试配置语法(不会重启 sshd,避免把自己锁外面)
sshd -t
# 关键:先保留一个已登录的 SSH 会话,再重启
systemctl restart sshd
# 新开一个终端测试密钥登录(确认能连上再关旧会话)
ssh -i ~/.ssh/id_ed25519_scrcx -p 2222 ops@10.10.4.17
血泪教训:改 sshd_config 后永远先
sshd -t,然后保留旧会话开新窗口验证。 我见过太多人在远程服务器上改完配置systemctl restart sshd然后把自己锁在外面,只能跑机房插显示器。
5.3 用 Match 做条件配置
# 跳板机允许特定网段密码登录(临时排障用),其他强制密钥
Match Address 10.10.4.0/24
PasswordAuthentication no
PermitRootLogin no
# CI/CD 机器人专用配置
Match User deploy
ForceCommand internal-sftp # 只允许 SFTP,不能拿 shell
ChrootDirectory /data/deploy # 锁定根目录
AllowTcpForwarding no
六、Fail2ban:自动封禁爆破 IP
6.1 安装与配置
# RHEL/CentOS
yum install -y fail2ban
# Debian/Ubuntu
apt install -y fail2ban
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600 # 封禁 1 小时
findtime = 600 # 10 分钟内
maxretry = 3 # 3 次失败即封
banaction = firewallcmd-ipset # CentOS 用 firewalld
# banaction = iptables-multiport # Debian 用 iptables
[sshd]
enabled = true
port = 2222
logpath = /var/log/secure
maxretry = 3
bantime = 86400 # SSH 爆破直接封 24 小时
6.2 启动与验证
systemctl enable --now fail2ban
# 查看当前被封 IP
fail2ban-client status sshd
# 手动解封某个 IP
fail2ban-client set sshd unbanip 1.2.3.4
# 模拟失败登录测试(故意输错密码 3 次后检查是否被封)
6.3 白名单保护
# /etc/fail2ban/jail.local
[DEFAULT]
ignoreip = 127.0.0.1/8 10.10.4.0/24 60.205.129.97
# 上述 IP 永远不会被封,防止跳板机自己被封
七、跳板机搭建:所有入口收口
7.1 架构设计
运维人员 外部审计
│ │
│ SSH (密钥+MFA) │ 只读日志
▼ ▼
┌────────────────────────────────────┐
│ 跳板机 (jump.scrcx.cn) │
│ ┌──────────┐ ┌────────────────┐ │
│ │ sshd │ │ tlog/audit │ │
│ │ (端口2222)│ │ (录屏+命令日志) │ │
│ └──────────┘ └────────────────┘ │
└───────────────┬────────────────────┘
│ SSH (内网密钥)
┌────────────┼────────────┐
▼ ▼ ▼
生产机A 生产机B 数据库机
10.10.4.17 10.10.4.19 10.10.4.20
核心原则:生产服务器只允许跳板机 IP 访问 SSH,所有人必须先登录跳板机再跳转。
7.2 跳板机 SSH 限制
# /etc/ssh/sshd_config (跳板机)
Port 2222
PasswordAuthentication no
AllowTcpForwarding yes # 必须开启,用于跳转
AllowAgentForwarding yes # 必须开启,用于密钥转发
# 限制用户只能跳到指定机器
Match User ops
PermitOpen 10.10.4.17:2222 10.10.4.19:2222 10.10.4.20:2222
7.3 ~/.ssh/config 客户端配置
# ~/.ssh/config —— 运维人员笔记本上配置
Host jump
HostName jump.scrcx.cn
Port 2222
User ops
IdentityFile ~/.ssh/id_ed25519_scrcx
ForwardAgent yes
Host web-01
HostName 10.10.4.17
Port 2222
User ops
ProxyJump jump # 自动通过跳板机
IdentityFile ~/.ssh/id_ed25519_scrcx
Host db-01
HostName 10.10.4.20
Port 2222
User oracle
ProxyJump jump
IdentityFile ~/.ssh/id_ed25519_scrcx
配好后直接 ssh web-01 就能一跳到位,不用记端口和 IP。
八、审计层:操作录屏与命令日志
8.1 方案一:tlog(轻量级,RHEL 原生)
yum install -y tlog
# /etc/tlog/tlog-rec-session.conf
{
"verify": true,
"write": {
"file": {
"path": "/var/log/tlog/tlog.log"
}
}
}
# 在 /etc/profile.d/ 加入自动录制
cat > /etc/profile.d/tlog.sh << 'EOF'
if [ -n "$SSH_TTY" ] && [ "$USER" != "root" ]; then
TLOG_REC=yes
export TLOG_REC
fi
EOF
回放操作录屏:
# 查看录屏列表
tlog-rec --list
# 回放某次会话
tlog-play -r file -i <session-id>
8.2 方案二:bash history 增强(零依赖方案)
# /etc/profile.d/audit.sh —— 不装任何软件,纯 bash 方案
export HISTTIMEFORMAT="%F %T `who am i | awk '{print $5}'` "
export HISTSIZE=10000
export HISTFILESIZE=10000
export PROMPT_COMMAND='history -a; logger -t ssh-audit "$(history 1)"'
# 关键:PROMPT_COMMAND 把每条命令实时写入 syslog
# /var/log/messages 里会出现:
# ssh-audit: 2026-07-13 10:23:45 (10.10.4.100) rm -rf /tmp/old_data
8.3 方案三:Teleport(企业级,推荐规模较大时用)
# Teleport 自带 MFA、RBAC、录屏、审计,适合 10+ 台服务器管理
# 安装略,核心配置示意
# /etc/teleport.yaml
auth_service:
enabled: yes
listen_addr: 0.0.0.0:3025
# 用户通过 Web MFA 登录 → 自动获得临时证书 → SSH 到目标机
# 所有操作录屏存到 S3 / OSS,可随时回放审计
九、双因素认证(MFA):密钥之上再加一层
9.1 Google Authenticator(PAM 模块)
yum install -y google-authenticator
# 每个用户在自己环境下执行(生成 TOTP 密钥)
google-authenticator
# 选择:
# - 时间_based: y
# - 更新 .google_authenticator: y
# - 禁止多次使用同一令牌: y
# - 时间窗口: n
# - 限速: y
# /etc/pam.d/sshd 添加
auth required pam_google_authenticator.so
# /etc/ssh/sshd_config
ChallengeResponseAuthentication yes
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
配置后登录流程:密钥认证 → 输入手机上的 6 位动态码 → 登录成功。
9.2 证书签名(大规模环境)
# CA 服务器签发用户证书(有效期 8 小时)
ssh-keygen -s ~/.ssh/ca_user_key \
-I "ops-xuwang-20260713" \
-V +8h \
-n ops,deploy \
~/.ssh/id_ed25519_scrcx.pub
# 生成 id_ed25519_scrcx-cert.pub
# 服务器信任 CA 公钥
echo "@cert-authority * $(cat ~/.ssh/ca_user_key.pub)" >> /etc/ssh/sshd_config
# TrustedUserCAKeys /etc/ssh/ca_user_key.pub
# 用户登录时自动使用证书
ssh -i ~/.ssh/id_ed25519_scrcx -p 2222 ops@10.10.4.17
证书方案的优势:不需要在每台服务器上分发公钥,只要服务器信任 CA,签发的证书就能用。人员离职时吊销证书即可,不用逐台删 authorized_keys。
十、监控与告警
10.1 关键监控指标
# SSH 登录成功事件
journalctl -u sshd | grep "Accepted"
# SSH 登录失败事件(突然暴增 = 爆破攻击)
journalctl -u sshd | grep "Failed password" | wc -l
# 当前在线用户
who | wc -l
# Fail2ban 封禁数量
fail2ban-client status sshd | grep "Banned IP"
10.2 Promtail 采集 SSH 日志(接入 Grafana)
# /etc/promtail/promtail-config.yml
scrape_configs:
- job_name: ssh-audit
static_configs:
- targets: [localhost]
labels:
job: sshd
__path__: /var/log/secure
pipeline_stages:
- regex:
expression: '^(?P<time>\S+ \S+) (?P<host>\S+) sshd\[\d+\]: (?P<event>Accepted|Failed) (?P<method>\S+) for (?P<user>\S+) from (?P<ip>\S+)'
- labels:
event:
user:
ip:
十一、避坑清单
| # | 坑 | 现象 | 解决 |
|---|---|---|---|
| 1 | 改配置后自锁 | SSH 连不上了 | 永远保留旧会话,sshd -t 后再重启 |
| 2 | authorized_keys 权限错误 | 密钥被静默忽略 | chmod 600,目录 chmod 700 |
| 3 | 密钥有 passphrase 脚本卡住 | cron/CI 任务挂起 | 用 ssh-agent 或 expect 自动输入 |
| 4 | Agent 转发被中间人利用 | 跳板机管理员可借用私钥 | 只信任跳板机,跳板机不存私钥 |
| 5 | fail2ban 封了自己 | 跳板机 IP 被误封 | ignoreip 加白名单 |
| 6 | known_hosts 冲突 | 服务器重装后连不上 | ssh-keygen -R <host> 清旧指纹 |
| 7 | SSH 超时断开 | 长任务跑到一半断了 | tmux/screen 保活,或 ServerAliveInterval |
| 8 | ProxyJump 不生效 | 直连超时 | 检查跳板机 AllowTcpForwarding yes |
| 9 | 证书过期无感知 | 突然全部连不上 | 监控证书有效期,提前续签 |
| 10 | SELinux 阻止非标端口 | 改端口后 sshd 起不来 | semanage port -a -t ssh_port_t -p tcp 2222 |
十二、小结
SSH 安全的核心不是某一条配置,而是纵深防御:网络层白名单挡住大部分流量,密钥认证挡住密码爆破,协议层配置限制攻击面,审计层兜底追溯。四层叠加后,即使某一层被突破,攻击者仍然无法直接拿到 shell。
跳板机则解决了另一个工程问题:入口收口。所有运维流量都经过一个可控的入口,既方便统一加固和审计,又避免了"每台服务器都要暴露在公网"的灾难。当你第一次通过 ssh web-01 一键穿透跳板机直达目标机,同时知道自己的每一条命令都在录屏,你就理解了"安全与便捷不矛盾"这句话。

