适用场景:全新 VPS / 重装系统后的首批操作
环境:Debian 12 / Ubuntu 22.04+(其它 systemd 发行版同理)
核心目标:密码登录彻底关闭、暴力破解自动封禁、威胁情报联动封禁
—
0. 先看一张“加固前后对比”
| 指标 | 加固前 | 加固后 |
| SSH 端口扫描存活 | ✅ | ✅(端口不变,但只有证书能进) |
| 密码爆破成功率 | 取决于密码强度 | 0%(密码认证已禁用) |
| 证书私钥泄露风险 | N/A | 仅持有私钥的机器可登录,配合 ssh-agent 落地无痕 |
| 自动封禁维度 | 无 | IP 维度(fail2ban)+ 威胁情报维度(CrowdSec) |
| 运维成本 | 低 | 一次配置,终身受益 |
—
1. SSH 证书登录(彻底禁用密码)
1.1 生成 CA 与签发用户证书(仅在受信任管理机上做一次)
# 1) 生成 CA 密钥(妥善保管,离线最佳)
ssh-keygen -t ed25519 -f ~/.ssh/ca -C "vps-ca-$(date +%Y%m%d)"
# 生成:ca(私钥)、ca.pub(公钥)
# 2) 为每台 VPS 签发主机证书(主机证书用于客户端验证服务端,防中间人)
ssh-keygen -s ~/.ssh/ca -I "vps-host-$(hostname)" -h -n "$(hostname),$(curl -s ifconfig.me)" -V +365d /etc/ssh/ssh_host_ed25519_key.pub
# 生成:ssh_host_ed25519_key-cert.pub → 拷回 VPS /etc/ssh/
# 3) 为每位运维人员签发用户证书(每人一份,设定有效期)
ssh-keygen -s ~/.ssh/ca -I "admin-alice" -n "root,alice" -V +90d ~/.ssh/id_ed25519.pub
# 生成:id_ed25519-cert.pub → 发给 Alice,她放 ~/.ssh/ 同目录即可
关键点:-V +90d 限制有效期,到期自动失效,无需手动吊销;-n 指定允许登录的远程用户名。
1.2 VPS 端配置(/etc/ssh/sshd_config.d/99-hardening.conf)
# === 仅保留现代加密套件 ===
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
# === 证书认证 ===
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/ca.pub # 只需 CA 公钥,无需分发每个用户公钥
AuthenticationMethods publickey
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PermitRootLogin prohibit-password # root 仅证书
AuthorizedKeysFile none # 彻底忽略 ~/.ssh/authorized_keys
# === 其它收敛 ===
Port 22
PermitEmptyPasswords no
MaxAuthTries 3
LoginGraceTime 20
ClientAliveInterval 300
ClientAliveCountMax 2
DisableForwarding yes
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
PrintMotd no
# 部署 CA 公钥
sudo install -m 644 ca.pub /etc/ssh/ca.pub
sudo systemctl reload sshd
验证:ssh -vvv root@your.vps.ip 2>&1 | grep -E "Offering|Authenticated" 应显示 Authenticated with certificate。
—
2. fail2ban:把“试密码/试证书失败”的 IP 自动关小黑屋
2.1 安装与核心配置
sudo apt update && sudo apt install -y fail2ban
/etc/fail2ban/jail.d/sshd-hardening.local:
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
backend = systemd
maxretry = 3
findtime = 600
bantime = 86400
banaction = iptables-multiport
action = %(action_mwl)s
action_mwl = 封禁 + 发邮件通知(需配置 destemail/sender),不想收邮件改 %(action_)s。
2.2 启用并验证
sudo systemctl enable --now fail2ban
fail2ban-client status sshd
# 手动测试:故意输错 3 次密码/证书 → fail2ban-client status sshd 应显示 Banned IP
—
3. CrowdSec:把全网威胁情报变成你的防火墙规则
CrowdSec = “众包版 fail2ban”,社区共享恶意 IP,本地决策引擎自动拉取并封禁。
3.1 一键安装(官方脚本自动适配发行版)
curl -s https://install.crowdsec.net | sudo bash
# 安装完成后会提示注册控制台(可选,注册后能看仪表盘)
3.2 关键采集器 & 决策
# 采集 SSH / nginx / docker 日志
sudo cscli collections install crowdsecurity/sshd crowdsecurity/nginx crowdsecurity/docker
# 查看已加载的场景
cscli scenarios list
3.3 配置 bouncer(真正落地封禁的组件)
# 推荐 iptables bouncer(无依赖、内核级生效)
sudo apt install -y crowdsec-firewall-bouncer-iptables
# 或 nftables 版:crowdsec-firewall-bouncer-nftables
验证:
cscli decisions list
# 应显示来自社区的恶意 IP + 本地 fail2ban 触发的决策
联动:fail2ban 封本地日志里的坏人,CrowdSec 封全网已知的坏人,双重保险。
—
4. 一条命令自检(加完建议跑一次)
cat <<'EOF' | sudo bash
echo "=== SSH 仅证书登录 ===" && sshd -T | grep -E 'passwordauthentication|pubkeyauthentication|authenticationmethods'
echo -e "\n=== fail2ban 状态 ===" && fail2ban-client status sshd
echo -e "\n=== CrowdSec 决策 ===" && cscli decisions list -o table
echo -e "\n=== 当前防火墙规则 ===" && sudo iptables -L -n -v | grep -E 'f2b|crowdsec' | head -20
EOF
—
5. 常见坑 & 避坑指南
| 现象 | 原因 | 修正 |
证书登录提示 Permission denied (publickey) | 客户端没带 -i 或证书未放在同目录 | ssh -i ~/.ssh/id_ed25519 root@ip;证书文件名必须为 id_ed25519-cert.pub |
| fail2ban 不封禁 | 日志路径/后端不对 | journalctl -u sshd -f 确认日志格式,backend = systemd |
| CrowdSec 决策不生效 | bouncer 未启动 / 冲突 | systemctl status crowdsec-firewall-bouncer-iptables;检查 iptables -L 有无 crowdsec 链 |
| 误封自己 | 管理机 IP 变动 | cscli decisions delete -i <your-ip> 或加白名单 cscli decisions add --ip <your-ip> --type whitelist |
—
6. 维护清单(建议加入定期巡检)
# 每周
cscli hub update && cscli hub upgrade # 更新场景/采集器
fail2ban-client status # 确认服务存活
# 每月
cscli decisions list --since 30d | wc -l # 统计封禁量
ssh-keygen -Lf ~/.ssh/ca.pub # 检查 CA 有效期
—
7. 一键部署脚本(可选,放 GitHub Gist 供以后复用)
#!/usr/bin/env bash
# vps-harden.sh — 仅供参考,生产环境建议逐步执行并确认
set -euo pipefail
# ... 把上面所有步骤串成函数,按需调用