速读#
ufw 和 fail2ban 是两层:ufw 管哪些端口该开,fail2ban 管哪些反复试探的 IP 该封。这篇不堆安全命令,重点是两条纪律:启用前先放行 SSH,阈值宁可少报也别滥报。
为什么两层#
SSH 加固守的是「敲门之后认不认你」。门外还有两类威胁,同一件工具挡不干净。
| 层 | 挡什么 | 管什么 |
|---|---|---|
| ufw | 不该来的端口 | 结构:门开几道缝 |
| fail2ban | 反复试的 IP | 行为:砸门的记下来封掉 |
加上 SSH 密钥管的「认不认你」,一共三个层面,各管一段:ufw 决定门开在哪,fail2ban 盯着谁在砸门,密钥决定进门认证。任何一层单拿出来都有说得通的绕过场景,叠起来才是面。
ufw:封装赢过裸 iptables#
ufw default deny incomingufw default allow outgoingufw allow 22222/tcp # SSH 端口(先于 enable)ufw allow 80/tcpufw allow 443/tcpufw enableufw status verbose # 启用后核对:默认策略 + 放行列表iptables 强,但规则语法反人类,写错不易察觉,还可能把自己锁外。ufw 用人能读懂的命令封装它,代价是少一部分灵活性——我这个规模不需要那部分。如今 Debian 的底层已经换成 nftables,ufw 照样封装得住:用可读性换灵活性这笔取舍,不随底层实现变。
又是自由 vs 定型:小规模防护,定型赢。
两条 default 是这套配置的骨架:入站默认全拒,出站默认全放,然后只把需要的缝一条条开出来。 白名单思维——漏放行顶多自己连不上,漏封堵则是留了个不知道的洞。两种错的代价不对称,所以默认值要偏向前者。
启用前先放行 SSH#
启用防火墙前,务必先放行你的 SSH 端口。否则
ufw enable瞬间把自己关在门外。这个错通常只会犯一次。
和 SSH 篇「改配置别关当前会话」同一类纪律:半破坏性网络操作前,先确保自己有活路。
fail2ban:让反复试有代价#
关了密码登录,日志里仍有海量试探。fail2ban 让「反复试」有代价:
[sshd]enabled = trueport = 22222maxretry = 4bantime = 1hfindtime = 10m10 分钟内失败 4 次,封 1 小时。它的工作方式很朴素:盯日志、数失败次数、超过阈值就往防火墙插一条临时封禁规则,到期自动撤销。
正因为一切建立在「读日志」上,日志源不对,它就整个失明。一个容易踩的点:fail2ban 传统上读 /var/log/auth.log,而 Debian 12 最小安装可能没带 rsyslog,这个文件根本不存在——jail 看着在跑,实际一条日志都没收到。这种情况在 jail 里加 backend = systemd,让它直接读 journal。
所以装完别只确认进程活着,要确认 jail 真的在工作:
fail2ban-client status sshd # 看 Currently failed / Total banned 是否在动systemctl restart fail2ban # 改完 jail 要重启才生效阈值:宁可少报,别滥报#
| 太敏感 | 太宽松 |
|---|---|
| 手滑输错密钥就被封 | 等于没挡 |
宁可少报,别滥报。 和监控告警「不狼来了」同源:任何自动响应,误触发多了人就会整体静音,机制就废了。
maxretry = 4 也是这个逻辑下选的:正常人输错两三次会停下来检查,只有脚本才会不知疲倦地连错四次以上。阈值卡在「人会犯的错」和「脚本才有的行为」之间。
收紧线一直到 Tunnel#
这里「只放行 SSH、80、443」是防护的一个状态。把 Web 流量改走 Cloudflare Tunnel 之后,80/443 入站也可以归零——Web 走出站隧道,防火墙上连这两道缝都不用留。
防护面是动态的:开一堆端口 → 只开必要的 → 零入站。这条收紧线,是 VPS 到 Tunnel 一路的主旋律。
