云服务器安全加固深度对比评测:fail2ban、ipset+nftables、安全组谁才是真防线?

一、先说结论:别再做“登录失败就 ban IP”的幼儿园加固

几乎所有云服务器教程都会让你开 fail2ban,但现实是:攻击者早已学会分布式换 IP、慢速爆破、伪造内网源地址。你还在用单机日志解析 + iptables 链逐个匹配,安全没用上,反而先把 CPU 打满了。

本文不讨论 SELinux/AppArmor 这种基础合规项,直接拿三个真实可用的封禁手段做深度对比:fail2banipset + nftables云平台安全组。全程给命令、给配置、给排错案例,你看完能直接抄。

二、方案一:fail2ban——廉颇老矣,尚能饭否

2.1 经典配置背后的性能暗礁

# /etc/fail2ban/jail.local
[sshd]
enabled = true
backend  = systemd
maxretry = 3
findtime = 300
bantime  = 3600
banaction = nftables-multiport

这条配置看似没问题,但注意 banaction。默认的 iptables 方案每封一个 IP 就往 INPUT 链里插一条规则,规则量上万后,内核线性遍历链表的代价直接体现在每个数据包的延时上。更隐蔽的是,fail2ban 的 findtime 窗口内每一条日志触发一次数据库写入,攻击频率稍高就会出现数据库锁等待,日志事件堆积——你还在等它 ban 人,它自己先崩了。

2.2 实测数据

  • 单机封禁 5000 个 IP 后,iptables 规则链匹配时间增加约 2.3ms/包。
  • 日志轮转时 fail2ban 的 filter 正则未适配压缩包格式,导致漏判,封禁失效。
  • 重启后 iptables 规则丢失,没有持久化机制,开机空窗期再次被爆破。

适用场景:临时应急、小流量个人服务器、业务低峰期。不推荐作为生产环境主力防线。

三、方案二:ipset + nftables——真正的内核级“内存指纹库”

3.1 为什么它碾压 fail2ban

ipset 将封禁 IP 存储在内存哈希表中,nftables 在 netfilter 钩子处查集合,时间复杂度 O(1)。封禁一万个 IP,对转发性能的影响趋近于零。更重要的是,它支持动态增删条目,配合一个轻量脚本就能实现“秒级封禁、秒级解封”,不像 fail2ban 那样要重启插件。

3.2 nftables 集合配置

# /etc/nftables.conf
table inet blackhole {
    set bad_ips {
        type ipv4_addr
        flags interval
    }
    chain input {
        type filter hook input priority filter; policy accept;
        ip saddr @bad_ips drop
    }
}

加载规则:

nft -f /etc/nftables.conf

手动封禁测试:

nft add element inet blackhole bad_ips { 203.0.113.5 }
nft list set inet blackhole bad_ips

3.3 自动封禁脚本:从日志到 ipset 的通路

用 systemd timer 每 30 秒扫描 auth.log,提取失败 IP 超过阈值就加入集合。关键点:使用 awk + sort + uniq -c 做聚合,避免每次重复读取大日志。

#!/bin/bash
# /usr/local/bin/auto-ban.sh
THRESHOLD=5
SSH_LOG="/var/log/auth.log"

# 只取最近 5 分钟的新失败条目,防止误杀历史IP
awk -v since="$(date -d '5 minutes ago' '+%b %e %H:%M:%S')" '
  $0 ~ /Failed password/ && $0 >= since {
    for(i=1;i<=NF;i++) if($i=="from") print $(i+1)
  }' "$SSH_LOG" | sort | uniq -c | awk -v t="$THRESHOLD" '$1>=t{print $2}' | while read ip; do
    # 非空才添加,且忽略私网/保留地址
    [[ "$ip" =~ ^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|127\.) ]] && continue
    nft add element inet blackhole bad_ips { "$ip" } 2>/dev/null && \
      echo "$(date) BAN $ip" >> /var/log/auto-ban.log
done

解封入口更简单,直接 nft delete element,不用翻日志。

3.4 持久化陷阱

千万记住:nft list ruleset 导出后,nft -f 恢复时如果集合里已有同名集合,会报错。标准做法是用 nft flush set 清空后重新加载。另外,systemd 启动顺序一定要让 nftables 先于任何 docker 或 bridge 网络服务,否则 Docker 的 iptables 链会绕过 nftables(经典坑,别问怎么知道的)。

四、方案三:云平台安全组——物理隔离,但远水救不了近火

4.1 优点是真的香

以轻云互联的云服务器为例,安全组规则实现在虚拟化层,数据包根本到达不了 vCPU。这意味着哪怕你的系统内核已经卡死或正在被流量打满,安全组依然能丢弃恶意 IP。而且它没有单机 CPU 开销,规则数量扩容到几百条也不影响五元组转发性能。

4.2 缺点才是关键

  • 无法实时联动:安全组规则变更需要调用 OpenStack/API,从发起封禁到完全生效通常有 3~10 秒延迟,不适合秒级爆破。
  • 操作频率受限:云平台 API 有 rate limit,一分钟内改 30 次,很容易触发限流,封禁一小撮 IP 就触顶。
  • 日志缺失:安全组没有 drop 日志,你想回溯哪个 IP 被拒,对不起,查不到。

所以安全组的定位是“大范围基础过滤”,比如封禁某个地区 IP 段、封禁竞争对手的已知资产,不适合做动态精细拦截。

五、三份方案硬核对比

  • 封禁容量:fail2ban 千级,ipset 百万级,安全组看云平台配额。
  • 每数据包额外性能损耗:fail2ban 随规则数量线性增长,ipset 基本恒定 < 1%,安全组为零。
  • 响应精度:fail2ban 依赖日志轮转时效,可能滞后;ipset 脚本可控 30 秒内;安全组 API 延迟最高。
  • 误封恢复:fail2ban 需要解除 jail 中的 IP 并重启;ipset 单条删除毫秒级;安全组需要 API 调用并等生效。
  • 抗绕过:fail2ban 和 ipset 均只看到来源 IP,若攻击者用伪造源 IP(反射型),可能误封无辜;安全组有云平台侧五元组校验,更严格。

六、避坑与排错——你的加固为什么失效了

6.1 案例一:fail2ban 失效,因为 journald 截断了日志

使用 backend=systemd 时,如果 journald 设置了 RateLimitIntervalSec,高频 SSH 爆破产生的日志会被 journald 自己丢弃。fail2ban 根本看不到攻击记录,当然不行动。

排查:

journalctl -u sshd --since "1 hour ago" | grep "Failed password" | wc -l
# 如果这个数字远小于实际攻击次数,说明日志被限速了
# 修改 /etc/systemd/journald.conf 的 RateLimitIntervalSec=0

6.2 案例二:ipset 重启后集合消失

很多人用 systemctl enable nftables 但忘了保存规则。正确做法:nft list ruleset > /etc/nftables.conf。但如果集合是空集,保存时可能不带 type ipv4_addr 声明,恢复直接报错。检查恢复脚本时务必看这行:

grep -n "bad_ips" /etc/nftables.conf

6.3 案例三:Docker 容器绕过 nftables 封禁

Docker 默认通过 br_netfilter 转发流量,nftables 的 input 链对经过 FORWARD 链的包不生效。要么在 nftables 里再挂 FORWARD 链,要么直接把 docker0 网桥流量全部丢给 nftables 处理:

nft add chain inet blackhole forward { type filter hook forward priority filter; policy accept; }
nft add rule inet blackhole forward ip saddr @bad_ips drop

七、推荐架构:双保险,但别让逻辑冲突

高防云服务器安全加固,我现在的标准做法是:

  1. 云平台安全组在最外层,封禁止业务地区大网段和云平台黑名单(比如轻云互联的云服务器控制台里直接加一条拒绝对话,免费用)。
  2. 主机内跑 ipset + nftables 做动态封禁,脚本每 30 秒一次,只针对 SSH、Web 管理端口。
  3. 彻底卸载 fail2ban,别让它在 /var/log/messages 里刷屏造成混淆。

这套组合的落地成本极低:nftables 规则五行,脚本三十行,systemd timer 一个单元文件。但它能让云服务器在真实攻击流量下保持稳定——因为你的 CPU 从未为“封禁”这个动作多烧一个 tps。如果你在用的是轻量云服务器,记住安全组别做太多变动,高频改动交给 ipset,CPU 占用几乎为零,IO 也被降到最低。这才是生产环境该有的姿态。