Linux 如何限制单 IP 连接数?

11次阅读

iptables 用 connlimit 模块可轻量限制单 IP 并发连接数,需正确指定链(如 inputdocker-USER)、加 –syn 限制新连接,并注意 nf_conntrack 依赖及 IPv4/ipv6 掩码差异。

Linux 如何限制单 IP 连接数?

iptables 如何用 connlimit 限制单 IP 并发连接数

直接用 iptablesconnlimit 模块最轻量、最常用,不需要额外服务或用户态程序。它工作在内核 netFilter 层,对性能影响极小。

常见错误是写成匹配「已建立连接」却忘了加 --syn 或放错链(比如误配到 FORWARD 而非 INPUT),导致规则不生效。

  • 限制 ssh端口 22)单 IP 最多 3 个并发连接:
    iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j REJECT --reject-with tcp-reset
  • 限制所有 TCP 连接(非特定端口):
    iptables -A INPUT -p tcp -m connlimit --connlimit-above 10 -j DROP
  • 只限制新连接(避免误伤已有长连接):必须加 --syn,否则会匹配所有包(包括 ACK、数据包等):
    iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 50 -j REJECT

为什么 connlimit 默认统计的是 ESTABLISHED + SYN_RECV 状态

connlimit 统计的是当前被内核连接跟踪子系统(nf_conntrack)记录的连接条目,主要包含 ESTABLISHEDSYN_RECV 状态。这意味着:

  • 它不区分「是否完成三次握手」——SYN_RECV 算一个连接,哪怕只是半开连接;
  • 它依赖 nf_conntrack 模块启用,若系统关闭了连接跟踪(如某些高性能转发场景),connlimit 将完全失效;
  • 如果 nf_conntrack_max 设置过小(默认常为 65536),高并发下可能因哈希表溢出导致新建连接失败,此时限制逻辑本身也会异常;
  • IPv4 和 IPv6 各自独立计数,--connlimit-mask 32(IPv4)和 --connlimit-mask 128(IPv6)需分别配置。

替代方案:fail2ban 适合按请求频率封禁,不是连接数限制

很多人混淆「连接数限制」和「请求频率封禁」。fail2ban 是基于日志分析的,它看到 nginx 日志里某 IP 1 分钟内发了 100 次 /login 请求,才触发封禁——这跟 TCP 连接数无关。

如果你的真实需求是防暴力破解或爬虫扫端口,fail2ban 更合适;但如果是防连接耗尽(如 SYN Flood、连接池打满),connlimit 是唯一合理选择。

  • fail2ban 无法感知未记录日志的连接(如直接 telnet 扫描 22 端口);
  • 它有延迟(至少几十秒),而 connlimit 是实时拦截;
  • 它需要配置 jail、filter、action,复杂度远高于一条 iptables 命令。

注意 connlimit 在 Docker / kubernetes 环境下的失效点

Docker 默认使用 iptables 规则做 NAT 和端口映射,且会在 DOCKER-USER 链插入规则。如果你把 connlimit 规则加在 INPUT 链,它可能根本匹配不到容器暴露的端口流量——因为包先被 DOCKER-USERFORWARD 链处理了。

  • 正确做法:把规则加到 DOCKER-USER 链(Docker 官方推荐的用户自定义链),并指定目标容器端口和协议:
    iptables -I DOCKER-USER -p tcp --dport 8080 -m connlimit --connlimit-above 20 -j REJECT
  • Kubernetes 中,connlimit 对 Service ClusterIP 流量无效(走的是 iptables kube-proxy 规则,不进 INPUT);只有 nodePort 或 HostNetwork 暴露的端口才适用;
  • 容器内应用若自己监听 0.0.0.0,且宿主机没开防火墙connlimit 必须在宿主机上配,容器内配无效。

真正难的不是写那条命令,而是想清楚你要控的是哪一层的连接:是宿主机原生服务?Docker 映射端口?还是 Kubernetes 的入口流量?不同场景下,connlimit 的链位置、掩码值、甚至是否可用,全都不一样。

text=ZqhQzanResources