Blog
SSH 密钥与 fail2ban:主机加固最小清单
公网 IP 上的 SSH,如果不做任何限制,auth.log 里全是扫密码的机器人。我不要「军事级堡垒」,只要一张最小清单:密钥登录、关密码、防火墙、fail2ban。下面用配置当主线,事故写在旁注里——因为真实世界里,加固步骤本身就会伤到操作者。
家庭实验室的威胁模型很朴素:不是国家队盯着你,而是全网自动脚本日夜试密码。目标是把「随手扫到就能撞库」变成「没有密钥别浪费时间」。做得过火,只会增加自己锁门的概率。
本地密钥:先生成,再谈服务器
笔记本上:
ssh-keygen -t ed25519 -a 100 -C "me@laptop"
# -a 100:KDF 轮数,拖慢暴力猜私钥口令的速度
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
旁注(事故苗头):ssh-copy-id 若因密码错失败,你却以为成功了,下一步关密码等于锁门丢钥匙。我养成习惯:copy 之后立刻 ssh -o PasswordAuthentication=no user@server 试一次,失败就停。
私钥权限必须是 600,目录 700。权限过宽时 ssh 会直接拒绝用这把钥匙,表现像「密钥坏了」,其实是自检。
sshd drop-in:我实际落盘的全文注解
确认新会话能进之后,再改服务端。我用 drop-in,不动主配置全文:
sudo tee /etc/ssh/sshd_config.d/99-hardening.conf <<'EOF'
PasswordAuthentication no
# 关密码:扫库机器人立刻失业;你自己也只能靠密钥
KbdInteractiveAuthentication no
# 键盘交互式登录一并关,避免绕回密码类流程
PermitRootLogin no
# root 不直连;需要时 su/sudo,审计更清晰
PubkeyAuthentication yes
X11Forwarding no
# 服务器很少需要 X11 转发,关了少一个面
AllowUsers myuser
# 白名单用户比「改端口藏起来」实在
# MaxAuthTries 3
# 可选:减少单连接尝试次数;与 fail2ban 互补
EOF
sudo sshd -t && sudo systemctl reload ssh
旁注(2025-03 某夜):sshd -t 我写过一次语法错误却跳过检查,reload 后新连接全灭。幸好旧会话还在,立刻把 drop-in 挪走 reload。从那天起,命令写成固定组合:sshd -t && systemctl reload ssh,前半失败绝不执行后半。
AllowUsers 比改端口实在。改端口只能减少噪声,挡不住定向扫。更干净的做法是:SSH 根本不监听公网,只绑 WireGuard 网卡。做得到的话,上面很多「防扫」会变成锦上添花。
fail2ban:jail.local 逐项说明
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
/etc/fail2ban/jail.local 里我只开 sshd:
[sshd]
enabled = true
maxretry = 4
# 10 分钟窗口内失败 4 次再 ban;太严容易误伤自己重试
findtime = 10m
bantime = 1h
# 一小时够让脚本换 IP;不必和整个互联网赌气永久封
旁注:有一次我在咖啡厅 NAT 后面连家,前面某人把 IP 刷脏了,我被 ban。解法是 fail2ban-client set sshd unbanip x.x.x.x,并接受「共享出口就有误伤」。家里出口固定时,可以直接 ufw 只放行自家 IP,fail2ban 变第二层。
看状态:
sudo fail2ban-client status sshd
sudo journalctl -u ssh -n 50 --no-pager
fail2ban 是减噪,不是万能盾。前面有 Cloudflare 或面板防护时,它仍然值得留——便宜,且对直连 IP 有效。
ufw:默认拒绝,再按来源开洞
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 10.66.0.0/24 to any port 22
# 只信任 WireGuard 网段访问 SSH
# 若必须公网:sudo ufw allow OpenSSH
sudo ufw enable
旁注(差点双杀):云安全组已放行 22,ufw 却只信任 10.66.0.0/24。某次 WG 挂了,我既进不去 SSH,也以为是密钥问题。其实是防火墙按设计拒绝。抢救靠厂商 VNC。两道闸都要会查:ufw status numbered 和云控制台规则列表。
能绑在 VPN 网段就不要 0.0.0.0/0:22。云厂商安全组与 ufw 是两道闸,别只信一边。
事故完整回顾:锁门外的四十分钟
时间线:
- 新机器上改好 drop-in,关密码。
- 没有新开窗口验证,直接关了旧 SSH。
- 笔记本公钥其实没进
authorized_keys(上次 copy 失败没看输出)。 - 重连拒绝;fail2ban 尚未 ban 我,是纯认证失败。
- 云主机 VNC 进系统,放公钥,
sshd -t && reload,新窗口验证。
抢救路径清单我现在贴在密码管理器置顶:
- 云主机:厂商 VNC / serial console
- 家里机器:接显示器键盘
- 若还有另一个 sudo 会话:立刻改回
PasswordAuthentication yes救急,修好再关
纪律只有一句:改 sshd 之前保留已认证会话;改完新开窗口验证。这条比任何扫描器签名都重要。
可选增强:按威胁模型加,不按清单堆
ssh-audit扫一遍算法套件- 只允许特定 IP(家里出口固定时)
- 跳板机 + 内网主机禁止公网 SSH
- 2FA(PAM):家庭场景我很少上,密钥保管更优先
日志日常扫:失败密码、非法用户名、短时间大量连接。有 fail2ban 后噪声会降,但突然成功的陌生 IP 比失败次数更值得警惕。
加固不是装越多软件越安全。最小清单做完、监听面收干净,比写十页安全制度更有效。配置能注解,事故能复盘,这套才算真正属于你。
若只能做一件事:先把密码登录关掉,并确认密钥与救援通道。fail2ban 和 ufw 可以第二天再补;锁门外的下午,通常发生在你最有信心的那次 reload。