4. iptables 限速实战:hashlimit、connlimit、recent 模块
限速这件事,说白了就是跟攻击流量抢时间。你想想看,DDoS 打过来的时候,带宽和连接数都是有限的,谁先占住资源谁就赢。iptables 里这三个模块——hashlimit、connlimit、recent——就是我手头最常用的三板斧。
我个人习惯把它们分成两类:连接数控制和速率控制。connlimit 管连接数,hashlimit 和 recent 管速率。今天咱们一个一个拆开讲。
4.1 connlimit:限制单 IP 并发连接数
connlimit 模块干的事很简单——限制单个源 IP 能建立的并发连接数。我在项目中遇到过不少 SYN Flood 变种,攻击者用大量伪造 IP 打满服务器连接表,这时候 connlimit 就是第一道防线。
4.1.1 基本用法
# 限制每个 IP 最多 20 个并发 SSH 连接
iptables -A INPUT -p tcp --dport 22 -m connlimit \
--connlimit-above 20 -j DROP
# 限制每个 IP 最多 100 个并发 HTTP 连接
iptables -A INPUT -p tcp --dport 80 -m connlimit \
--connlimit-above 100 -j DROP
这里要注意,--connlimit-above 的意思是“超过这个数就丢包”。所以 20 表示允许 20 个,第 21 个开始被拒绝。
4.1.2 掩码匹配
有时候攻击者用同一个 C 段 IP 打你,一个一个限制太麻烦。connlimit 支持掩码匹配:
# 限制 /24 网段最多 200 个并发连接
iptables -A INPUT -p tcp --dport 80 -m connlimit \
--connlimit-above 200 --connlimit-mask 24 -j DROP
4.2 hashlimit:精细化速率控制
hashlimit 是我最喜欢的一个模块。它用哈希表来跟踪每个 IP 的流量速率,能做到非常精细的控制。说白了,就是给每个 IP 单独记一笔账,超了就限速。
4.2.1 核心参数
| 参数 | 作用 | 示例 |
|---|---|---|
--hashlimit-above |
触发限速的阈值 | 10/minute |
--hashlimit-burst |
允许的突发量 | 20 |
--hashlimit-mode |
匹配维度 | srcip, dstip, srcport, dstport |
--hashlimit-name |
哈希表名称 | httplimit |
--hashlimit-htable-size |
哈希表大小 | 100000 |
4.2.2 实战配置
我记得有一次客户被 HTTP Flood 打得服务器 CPU 飙到 100%,我上去配了这么一条规则:
# 每个源 IP 每分钟最多 30 个 HTTP 请求,允许突发 50 个
iptables -A INPUT -p tcp --dport 80 -m hashlimit \
--hashlimit-above 30/minute \
--hashlimit-burst 50 \
--hashlimit-mode srcip \
--hashlimit-name http_limit \
-j DROP
为什么要设 burst?因为正常用户偶尔会快速刷新页面,burst 就是给这种正常行为留的缓冲。我曾经见过有人把 burst 设成 0,结果正常用户刷新一下就断了,投诉电话被打爆。
4.2.3 多维度匹配
hashlimit 可以同时匹配多个维度,比如按源 IP + 目标端口:
# 每个源 IP 对每个目标端口,每分钟最多 10 个连接
iptables -A INPUT -p tcp -m hashlimit \
--hashlimit-above 10/minute \
--hashlimit-mode srcip,dstport \
--hashlimit-name port_limit \
-j DROP
--hashlimit-htable-size 设得太小,结果哈希冲突导致正常用户被误杀。建议至少设成 100000,如果服务器内存够大,可以设到 500000。
4.3 recent:动态黑名单机制
recent 模块跟前面两个不太一样。它更像一个动态的黑名单系统——记录每个 IP 最近的活动,然后根据历史行为做判断。
4.3.1 工作原理
recent 维护一个 IP 地址列表,你可以往里面加 IP、查 IP、删 IP。每次匹配到规则,它就会更新这个列表。我常用它来做“三次犯规就封禁”的逻辑。
# 第一步:记录 SSH 登录失败的 IP
iptables -A INPUT -p tcp --dport 22 -m state --state NEW \
-m recent --name ssh_brute --set
# 第二步:如果这个 IP 在 60 秒内出现超过 4 次,就封禁 300 秒
iptables -A INPUT -p tcp --dport 22 -m state --state NEW \
-m recent --name ssh_brute --update \
--seconds 60 --hitcount 4 -j DROP \
-m comment --comment "SSH brute force protection"
4.3.2 常用参数
| 参数 | 说明 |
|---|---|
--set |
将源 IP 加入列表 |
--update |
更新列表中的时间戳 |
--seconds |
统计时间窗口 |
--hitcount |
触发阈值 |
--name |
列表名称 |
--rcheck |
只检查不更新 |
--remove |
从列表中移除 |
4.3.3 实战:防暴力破解
嗯,这里要注意顺序。recent 的规则必须两条配合:第一条负责记录,第二条负责判断。我曾经见过有人只配了第二条,结果 IP 从来没被记录过,规则形同虚设。
# 更完整的防暴力破解方案
iptables -N SSH_CHECK
# 记录新连接
iptables -A SSH_CHECK -m recent --name ssh_brute --set
# 检查是否超限
iptables -A SSH_CHECK -m recent --name ssh_brute \
--rcheck --seconds 60 --hitcount 4 -j LOG \
--log-prefix "SSH_BRUTE: "
iptables -A SSH_CHECK -m recent --name ssh_brute \
--rcheck --seconds 60 --hitcount 4 -j DROP
iptables -A INPUT -p tcp --dport 22 -m state --state NEW \
-j SSH_CHECK
4.4 三模块对比与选型
| 模块 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| connlimit | 连接数型攻击 | 配置简单,资源消耗低 | 无法控制请求速率 |
| hashlimit | 请求速率型攻击 | 精细化控制,支持突发 | 内存消耗较大 |
| recent | 暴力破解、慢速攻击 | 动态封禁,灵活性强 | 需要两条规则配合 |
4.5 组合实战:三层防御
你想想看,真正的攻击不会只打你一层。所以我一般会把这仨模块叠起来用:
# 第一层:connlimit 限制并发
iptables -A INPUT -p tcp --dport 80 -m connlimit \
--connlimit-above 100 -j DROP
# 第二层:hashlimit 限制速率
iptables -A INPUT -p tcp --dport 80 -m hashlimit \
--hashlimit-above 30/minute \
--hashlimit-burst 50 \
--hashlimit-mode srcip \
--hashlimit-name http_rate \
-j DROP
# 第三层:recent 动态封禁
iptables -A INPUT -p tcp --dport 80 -m recent \
--name http_abuse --set
iptables -A INPUT -p tcp --dport 80 -m recent \
--name http_abuse --rcheck \
--seconds 120 --hitcount 10 -j DROP
好了,这三个模块的用法和实战配置就讲到这里。记住一点:限速不是目的,让正常用户不受影响才是。配置的时候多想想正常用户的行为模式,别把自己人拦在外面。