主题模式
Are you an LLM? You can read better optimized documentation at /vps/settings/ssh-security-hardening.md for this page in Markdown format
SSH 安全加固与密钥管理实战:从入门到企业级防护
SSH 是服务器管理的核心通道,也是黑客攻击的首要目标。互联网上每秒都有数以万计的自动化脚本在暴力破解 SSH 密码。仅靠 基础防火墙 和 Fail2ban 还不够,本文将从密钥管理、认证加固、网络隔离三个维度,构建完整的 SSH 安全防护体系。
🎯 威胁模型分析
SSH 面临的主要威胁:
| 威胁类型 | 攻击方式 | 危害级别 | 防护手段 |
|---|---|---|---|
| 暴力破解 | 自动化脚本尝试密码 | 🔴 高 | 密钥认证 + Fail2ban |
| 中间人攻击 | 劫持 SSH 连接窃取凭据 | 🔴 高 | 主机密钥验证 |
| 密钥泄露 | 私钥被盗或意外公开 | 🔴 高 | 密钥密码 + 硬件密钥 |
| 横向移动 | 利用 SSH Agent 转发渗透内网 | 🟡 中 | 限制转发 + 跳板机 |
| 端口扫描 | 发现 SSH 端口后针对性攻击 | 🟡 中 | 端口更换 + 端口敲门 |
| 凭证填充 | 用泄露的密码字典尝试登录 | 🟡 中 | 禁用密码认证 |
🔑 密钥对认证
为什么用密钥而非密码
| 对比维度 | 密码认证 | 密钥认证 |
|---|---|---|
| 安全性 | 8 位密码 ~95bit | Ed25519 ~128bit+ |
| 暴力破解 | 可被穷举 | 计算上不可行 |
| 网络传输 | 密码经过加密通道 | 私钥永不离开本地 |
| 管理成本 | 需定期更换 | 一次生成,长期使用 |
| 审计追踪 | 无法区分来源 | 每把密钥可绑定身份 |
生成 Ed25519 密钥(推荐)
Ed25519 是目前最推荐的密钥算法,比 RSA 更安全、更快速、密钥更短:
bash
# 生成 Ed25519 密钥对
ssh-keygen -t ed25519 -C "ygjc@workstation-2026"
# 交互式设置:
# 1. 密钥保存路径(默认 ~/.ssh/id_ed25519)
# 2. 密钥密码(强烈建议设置)
# 3. 确认密码为什么选择 Ed25519?
- 更安全:基于椭圆曲线密码学,抗量子计算能力更强
- 更快速:签名和验证速度比 RSA-4096 快数倍
- 密钥更短:公钥仅 68 字符,RSA-4096 公钥约 715 字符
- OpenSSH 6.5+ 原生支持,兼容性已不是问题
生成 RSA 密钥(兼容性场景)
某些旧系统不支持 Ed25519,可使用 RSA:
bash
# 生成 RSA 4096 位密钥(不要用 2048 位)
ssh-keygen -t rsa -b 4096 -C "ygjc@workstation-2026"密钥管理最佳实践
一机一密原则
# ❌ 错误:所有服务器用同一把密钥
~/.ssh/id_ed25519 → 所有服务器
# ✅ 正确:每台服务器/环境使用独立密钥
~/.ssh/id_ed25519_personal → 个人服务器
~/.ssh/id_ed25519_work → 公司服务器
~/.ssh/id_ed25519_github → GitHub
~/.ssh/id_ed25519_bastion → 跳板机为不同用途生成密钥
bash
# 服务器登录密钥
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_server -C "server-access"
# Git 签名密钥
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_git -C "git-signing"
# 跳板机密钥
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_bastion -C "bastion"配置 SSH Config 管理多密钥
bash
# ~/.ssh/config
# 个人服务器
Host personal-vps
HostName 203.0.113.10
User root
Port 22
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
# 公司跳板机
Host bastion
HostName 10.0.0.1
User admin
Port 2222
IdentityFile ~/.ssh/id_ed25519_bastion
IdentitiesOnly yes
# 通过跳板机连接内网服务器
Host internal-server
HostName 192.168.1.100
User deploy
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
# GitHub
Host github.com
User git
IdentityFile ~/.ssh/id_ed25519_github
IdentitiesOnly yesIdentitiesOnly yes
这个选项确保 SSH 只使用指定的密钥文件尝试认证,避免用所有密钥逐一尝试(可能触发服务器的 MaxAuthTries 限制)。
部署公钥到服务器
bash
# 方式一:ssh-copy-id(推荐)
ssh-copy-id -i ~/.ssh/id_ed25519_personal.pub -p 22 [email protected]
# 方式二:手动部署
cat ~/.ssh/id_ed25519_personal.pub | ssh [email protected] "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"🛡️ SSH 服务端加固
sshd_config 安全配置
bash
sudo nano /etc/ssh/sshd_configini
# ===== 基础安全 =====
# 修改默认端口(避免自动扫描)
Port 2222
# 禁止 root 直接登录(先创建普通用户)
PermitRootLogin no
# 禁用密码认证(仅允许密钥登录)
PasswordAuthentication no
KbdInteractiveAuthentication no
# 禁用空密码
PermitEmptyPasswords no
# 禁用 X11 转发(如不需要)
X11Forwarding no
# ===== 认证控制 =====
# 最大认证尝试次数
MaxAuthTries 3
# 登录宽限时间(秒)
LoginGraceTime 30
# 认证超时
ClientAliveInterval 300
ClientAliveCountMax 2
# ===== 用户限制 =====
# 仅允许指定用户/组登录
AllowUsers deploy admin
# 或
AllowGroups ssh-users
# 禁止指定用户登录
DenyUsers nobody www-data
# ===== 密钥安全 =====
# 限制 authorized_keys 文件路径
AuthorizedKeysFile .ssh/authorized_keys
# 禁止密钥中的环境变量设置
PermitUserEnvironment no
# ===== 网络限制 =====
# 监听地址(仅内网)
# ListenAddress 192.168.1.100
# 禁用 TCP 转发(如不需要)
AllowTcpForwarding no
# 禁用 Agent 转发
AllowAgentForwarding no
# ===== 协议安全 =====
# 使用强加密算法
Ciphers [email protected],[email protected],aes256-ctr
MACs [email protected],[email protected]
KexAlgorithms [email protected],diffie-hellman-group16-sha512
# 禁用不安全算法
# (OpenSSH 9+ 默认已禁用 DSA、RSA-SHA1 等)
# ===== 日志 =====
# 详细日志
LogLevel VERBOSE
# 记录密钥指纹
PubkeyAuthentication yes修改端口注意事项
修改 SSH 端口后,确保防火墙放行新端口,否则会被锁在外面:
bash
sudo ufw allow 2222/tcp应用配置
bash
# 测试配置语法(不会实际重载)
sudo sshd -t
# 重载 SSH 服务
sudo systemctl reload sshd
# 或重启(更彻底)
sudo systemctl restart sshd不要直接重启
修改 sshd_config 后,保持当前 SSH 会话不要断开,另开一个终端测试新配置能否正常连接。确认无误后再关闭旧会话。
🔐 SSH Agent 与密钥密码
SSH Agent 简介
SSH Agent 是一个密钥管理守护进程,将解密后的私钥保存在内存中,避免每次连接都输入密钥密码:
bash
# 启动 SSH Agent
eval "$(ssh-agent -s)"
# 添加密钥到 Agent
ssh-add ~/.ssh/id_ed25519_personal
# 查看已添加的密钥
ssh-add -l
# 查看公钥
ssh-add -L
# 删除指定密钥
ssh-add -d ~/.ssh/id_ed25519_personal
# 删除所有密钥
ssh-add -DmacOS Keychain 集成
macOS 可以将 SSH 密钥密码存储在 Keychain 中:
bash
# ~/.ssh/config
Host *
UseKeychain yes
AddKeysToAgent yesbash
# 添加密钥并存储密码到 Keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519_personalAgent 转发的安全风险
Agent 转发允许你从本地通过中间服务器转发认证请求,但存在安全风险:
bash
# ❌ 不安全的 Agent 转发
ssh -A user@server # 如果 server 被攻破,攻击者可使用你的密钥
# ✅ 安全做法:使用 ProxyJump 代替 Agent 转发
ssh -J bastion internal-serverAgent 转发风险
如果启用了 Agent 转发(ForwardAgent yes),中间服务器被攻破后,攻击者可以通过 Unix Socket 冒充你进行认证。只有在完全信任中间服务器时才启用。
🚪 端口敲门(Port Knocking)
端口敲门是一种隐藏 SSH 端口的技术:SSH 端口默认关闭,只有按特定顺序"敲门"后才会打开。
安装 knockd
bash
sudo apt install knockd配置 knockd
bash
sudo nano /etc/knockd.confini
[options]
UseSyslog
Interface = eth0
[openSSH]
sequence = 7000,8000,9000
seq_timeout = 15
tcpflags = syn
command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
stop_timeout = 60
[closeSSH]
sequence = 9000,8000,7000
seq_timeout = 15
tcpflags = syn
command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT启动 knockd
bash
sudo systemctl enable knockd
sudo systemctl start knockd客户端使用
bash
# 安装 knock 客户端
sudo apt install knockd
# 敲门打开 SSH 端口
knock 203.0.113.10 7000 8000 9000
# 连接 SSH
ssh -p 2222 [email protected]
# 用完后关门
knock 203.0.113.10 9000 8000 7000端口敲门 vs 换端口
- 换端口:简单但可被全端口扫描发现
- 端口敲门:更隐蔽,即使扫描也看不到 SSH 端口
- 两者结合:先敲门再连接修改后的端口,安全性最高
📊 Fail2ban 进阶配置
在 基础 Fail2ban 之上,进行进阶配置:
SSH 专用 jail
bash
sudo nano /etc/fail2ban/jail.d/sshd.localini
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 300
bantime = 3600
findtime = 300
# 递增封禁:每次被封时间翻倍
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 604800
# 忽略白名单 IP
ignoreip = 127.0.0.1/8 ::1 192.168.1.0/24参数详解
| 参数 | 含义 | 推荐值 |
|---|---|---|
maxretry | 最大失败次数 | 3 |
findtime | 检测时间窗口(秒) | 300(5分钟) |
bantime | 初始封禁时长(秒) | 3600(1小时) |
bantime.increment | 递增封禁 | true |
bantime.factor | 递增倍数 | 2 |
bantime.maxtime | 最大封禁时长(秒) | 604800(7天) |
ignoreip | 白名单 IP | 内网 + 本机 |
查看封禁状态
bash
# 查看所有 jail 状态
sudo fail2ban-client status
# 查看 SSH jail 详情
sudo fail2ban-client status sshd
# 手动解封 IP
sudo fail2ban-client set sshd unbanip 1.2.3.4
# 手动封禁 IP
sudo fail2ban-client set sshd banip 1.2.3.4🏗️ 跳板机架构
跳板机工作原理
开发者电脑 → 跳板机(唯一 SSH 入口)→ 内网服务器群
┌──────────┐
│ 跳板机 │ ← 唯一对外暴露的 SSH 端口
└────┬─────┘
│ 内网 SSH
┌──────────────┼──────────────┐
│ │ │
┌─────┴────┐ ┌─────┴────┐ ┌─────┴────┐
│ Server A │ │ Server B │ │ Server C │ ← 内网 SSH,不对外暴露
└──────────┘ └──────────┘ └──────────┘跳板机配置
ini
# /etc/ssh/sshd_config (跳板机)
Port 2222
PermitRootLogin no
PasswordAuthentication no
AllowUsers jump-user
# 禁用端口转发(跳板机不应转发流量)
AllowTcpForwarding no
AllowAgentForwarding no
X11Forwarding no
# 限制会话时长
ClientAliveInterval 300
ClientAliveCountMax 0客户端使用 ProxyJump
bash
# ~/.ssh/config
# 跳板机
Host bastion
HostName 203.0.113.10
User jump-user
Port 2222
IdentityFile ~/.ssh/id_ed25519_bastion
# 内网服务器(通过跳板机连接)
Host server-a
HostName 192.168.1.101
User deploy
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519_work
# 直接使用
ssh server-a # 自动经过跳板机跳板机审计
bash
# 记录所有 SSH 会话操作(script 命令)
sudo apt install bsdutils
# 在 /etc/profile 中添加
if [ -n "$SSH_CONNECTION" ]; then
LOG_DIR="/var/log/ssh-sessions"
mkdir -p "$LOG_DIR"
script -qf "$LOG_DIR/$(whoami)-$(date +%Y%m%d-%H%M%S).log"
fi🔄 双因素认证(2FA)
安装 Google Authenticator PAM 模块
bash
sudo apt install libpam-google-authenticator为用户配置 2FA
bash
# 切换到目标用户
su - deploy
# 生成 2FA 密钥
google-authenticator
# 交互式配置:
# 1. 显示二维码,用 Google Authenticator/Authy 扫描
# 2. 保存紧急备用码
# 3. 是否基于时间生成验证码?(y)
# 4. 是否限制同一 token 使用一次?(y)
# 5. 是否延长时间窗口?(n)
# 6. 是否启用速率限制?(y)配置 SSH 使用 2FA
bash
# 编辑 PAM 配置
sudo nano /etc/pam.d/sshd
# 在文件顶部添加
auth required pam_google_authenticator.sobash
# 编辑 sshd_config
sudo nano /etc/ssh/sshd_config
# 启用挑战-响应认证
ChallengeResponseAuthentication yes
KbdInteractiveAuthentication yes
# 配置认证方式(密钥 + 2FA)
AuthenticationMethods publickey,keyboard-interactivebash
sudo systemctl restart sshd密钥 + 2FA 双重认证
配置后,连接 SSH 需要同时提供密钥和动态验证码。即使密钥泄露,没有 2FA 也无法登录。
客户端连接
bash
ssh deploy@server
# 输入密钥密码(如有)
# 输入 2FA 验证码(从手机 App 获取)📋 安全检查清单
| 检查项 | 状态 | 说明 |
|---|---|---|
| 禁用密码认证 | ☐ | PasswordAuthentication no |
| 禁用 root 登录 | ☐ | PermitRootLogin no |
| 使用 Ed25519 密钥 | ☐ | ssh-keygen -t ed25519 |
| 密钥设置密码 | ☐ | 私钥文件加密 |
| 修改默认端口 | ☐ | Port 2222 |
| 配置 Fail2ban | ☐ | 自动封禁暴力破解 IP |
| 限制登录用户 | ☐ | AllowUsers / AllowGroups |
| 禁用不必要的转发 | ☐ | AllowTcpForwarding no |
| 配置 SSH Config | ☐ | 多密钥管理 |
| 启用 2FA(高安全场景) | ☐ | Google Authenticator |
| 配置跳板机(多服务器场景) | ☐ | ProxyJump |
| 定期检查 auth.log | ☐ | 监控异常登录 |
| 备份密钥 | ☐ | 离线存储 |
📊 安全等级对照
| 等级 | 适用场景 | 配置项 |
|---|---|---|
| 基础 | 个人 VPS | 密钥认证 + 禁用密码 + Fail2ban |
| 标准 | 生产服务器 | 基础 + 修改端口 + 禁用 root + 限制用户 |
| 进阶 | 敏感数据服务器 | 标准 + 端口敲门 + 禁用转发 |
| 企业级 | 核心基础设施 | 进阶 + 2FA + 跳板机 + 会话审计 |
❓ 常见问题
Q: 忘记密钥密码怎么办?
无法找回。密钥密码没有后门,只能重新生成密钥对并重新部署公钥。建议将密钥密码存储在密码管理器(如 1Password、Bitwarden)中。
Q: 修改端口后被锁在外面怎么办?
通过 VPS 提供商的 VNC 控制台 或 救援模式 登录,恢复 sshd_config 配置。
Q: 2FA 手机丢了怎么办?
使用 google-authenticator 生成的紧急备用码。如果备用码也丢失,需要通过 VNC 控制台登录并重新配置。
Q: 多台服务器如何统一管理密钥?
推荐使用跳板机架构:所有服务器只允许跳板机 SSH 连接,管理员通过跳板机访问。配合 SSH Config 管理连接配置。
Q: SSH 连接经常断开怎么办?
bash
# ~/.ssh/config
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
TCPKeepAlive yes或在服务端配置 ClientAliveInterval 60。
🔗 相关文章
- VPS 安全加固指南 — 防火墙与 Fail2ban 基础
- 开启防火墙端口 — 端口管理
- SSL/TLS 证书管理 — HTTPS 安全
- 网络诊断工具箱 — 网络排查
- Tailscale 组网 — 替代 SSH 的内网访问方案
