上一篇 下一篇 分享链接 返回 返回顶部

如何在香港服务器 CentOS 7 环境中结合 iptables 与 DPI 防御跨境恶意流量?

发布人:Minchunlin 发布时间:2025-08-30 08:32 阅读量:688


我第一次在香港机房接到这个告警,是凌晨 2:17,监控屏上南向出口的 PPS 曲线突然抽成了一根针——外网跨境源的 SYN 洪泛、SSH 暴力、TLS 扫描混在一起,把一台跑 API 的节点压到了 load 30+。我拎着网线和一杯已经凉掉的美式,边看 Netflow 采样边想:先用 iptables 把粗颗粒流量挡在门口,再用 DPI 把“聪明的坏家伙”挑出来。
这篇就是那一夜到上线稳定这几周,我在 CentOS 7 上落地“iptables + DPI(Suricata)组合拳”的完整过程与坑点。写给需要马上动手的新手,也写给想要细节和落地经验的老手。

数据中心场景与基础信息

业务与威胁画像

业务:REST API + 静态资源,80/443/22 少量开放;部分域名在海外广告投放,天然会“招风”。

威胁:

  • 海外肉鸡对 22/80/443 的扫描 & 暴力;
  • TLS ClientHello 指纹(JA3)异常的批量探测;
  • DNS 隧道与 HTTP 可疑 UA;
  • 间歇性小流量 SYN/ACK 洪泛 + 连接耗尽。

机房与拓扑(简化)

[Internet] 
   |
[运营商汇聚]
   |
[上联万兆交换机]----(旁路抓包端口)
   |
[防护节点]  (本教程主角)
   |
[业务交换机]
   |
[应用/静态节点群]

硬件配置(防护节点)

项目 参数
机型 1U 双路
CPU Intel Xeon E5-2680 v4 ×2
内存 128 GB
硬盘 NVMe 1TB(日志 & EVE)+ SATA 480GB(系统)
网卡 Intel X520-DA2(10G,驱动 ixgbe
带宽 上下行 10Gbps(95 分位计费),常态 < 1Gbps
旁路镜像 交换机 SPAN 到防护节点第二口(做可选 IDS/回放)

软件版本

组件 版本/说明
OS CentOS 7.9(3.10 内核)
iptables iptables-services(传统 xtables)
Suricata 6.x(IPS 模式)
日志 EVE JSON + journald,辅助用 jq/goaccess
可选 xtables-addons(xt_geoip)或 ipset + 自建地理/ASN 列表

设计思路:三道防线

L3/L4 粗过滤(iptables)

  • 基于 ipset 的黑/白名单(恶意 ASN、临时封禁 IP、业务允许国)。
  • raw 表前置拒绝,绕过 conntrack,节省 CPU。
  • SYNPROXY + hashlimit 对 80/443/22 的速率与握手压力做削峰。
  • “只准进的端口”做最小暴露面;健康检查与管理口走专线白名单。

L7 深度检测(DPI/Suricata)

  • 通过 NFQUEUE 或 AF_PACKET inline 接入,按规则对 TLS/HTTP/DNS/SSH 进行检测与阻断。
  • 规则关注:异常 JA3、恶意 UA、DNS 隧道、HTTP 方法滥用、SSH 暴力等。

自治惩罚(动态拉黑)

  • Suricata 事件实时写 eve.json,用小脚本把触发特定规则的源 IP 自动加入 ipset 黑名单,带 TTL 自动过期。
  • 灰度:先 IDS 观察 24–48 小时,再逐步启用 drop。

步骤一:系统与内核参数调优(CentOS 7)

创建 /etc/sysctl.d/99-net-tuning.conf:

net.core.somaxconn = 4096
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_sack = 0

# 连接跟踪表
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_buckets = 262144

# 路由与过滤
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0

# UDP 缓冲
net.core.rmem_max = 268435456
net.core.wmem_max = 268435456
net.ipv4.udp_rmem_min = 131072
net.ipv4.udp_wmem_min = 131072

应用:

sysctl --system

坑点:多上联/异地回程时,rp_filter=1 会把合法包判成假冒丢掉,务必置 0。

步骤二:准备 iptables 与持久化

yum install -y iptables-services
systemctl disable firewalld --now
systemctl enable iptables --now

步骤三:准备 ipset 与(可选)地理库

3.1 基础集合

ipset create allow_mgmt hash:ip timeout 0
ipset create drop_temp hash:ip timeout 86400
ipset create drop_asn hash:net timeout 0
ipset create allow_geo hash:net timeout 0

把办公出口、拨测节点等加白:

ipset add allow_mgmt 203.0.113.10

3.2 可选:xtables-addons 的 GeoIP(或自建)

更稳妥:维持一份我们认可的数据源(GeoLite2/ASN 自建 CSV),每天构建 ipset 段。

注意:xt_geoip 需要手工更新国家库;库老化=误伤/漏拦的源头之一。

示例(把某些高风险地区先做速率限制而非一刀切):

# 以 ipset 承载网段(推荐)
# 列表文件到位后:每晚 cron 同步刷新 allow_geo / drop_asn

步骤四:iptables 粗过滤策略(含 raw 表与 SYNPROXY)

目标:先薄、后精。把不需要的、明显恶意的,尽量在最前端就丢掉,减少 conntrack 与 DPI 压力。

创建 /etc/sysconfig/iptables(片段,按需调整):

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# 1) 对已知恶意源直接丢弃,绕过 conntrack
-A PREROUTING -m set --match-set drop_temp src -j DROP
-A PREROUTING -m set --match-set drop_asn src -j DROP
COMMIT

*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# 基础允许
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

# 管理白名单(SSH/Bastion/健康检查)
-A INPUT -p tcp --dport 22 -m set --match-set allow_mgmt src -j ACCEPT

# 对外服务端口(示例 80/443)
-A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW -m hashlimit \
  --hashlimit-above 2000/sec --hashlimit-burst 4000 --hashlimit-mode srcip \
  --hashlimit-name http_rate -j DROP

# SYNPROXY(防半开耗尽)
-A INPUT -p tcp -m tcp --syn -m multiport --dports 80,443 -m conntrack --ctstate NEW \
  -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
-A INPUT -p tcp -m tcp -m multiport --dports 80,443 -m conntrack --ctstate INVALID -j DROP

# 允许通过 DPI(NFQUEUE 模式时启用,见后文)
# -A INPUT -p tcp -m multiport --dports 80,443 -j NFQUEUE --queue-num 0
# -A INPUT -p udp --dport 53 -j NFQUEUE --queue-num 1

# SSH 基线速率限制(非白名单)
-A INPUT -p tcp --dport 22 -m recent --name ssh --set
-A INPUT -p tcp --dport 22 -m recent --name ssh --update --seconds 60 --hitcount 10 -j DROP
-A INPUT -p tcp --dport 22 -j NFQUEUE --queue-num 2

# 其他默认丢弃
-A INPUT -p tcp -m tcp --dport 0:65535 -j REJECT --reject-with tcp-reset
-A INPUT -p udp -j DROP
COMMIT

应用:

systemctl restart iptables
iptables -S | sed -n '1,80p'

坑点:SYNPROXY 对四层负载均衡/健康检查可能引发“探测失败”,先在灰度环境验证探活策略;必要时针对探测源跳过 SYNPROXY。

步骤五:DPI 接入方式二选一

方案 A:NFQUEUE(最通用、易控流量粒度)

优点:按端口/方向把特定流量送入队列,Suricata 做用户态判决;精细。
缺点:大流量时需要 多队列+CPU 绑核,不当配置会卡队列。

5.A.1 Suricata 安装与基础配置

yum install -y epel-release
yum install -y suricata jq

编辑 /etc/suricata/suricata.yaml 的 af-packet 先禁用,用 nfqueue:

nfqueue:
  mode: repeat
  batchcount: 20
  queue: 0
  verdict: true
  bypass: yes

为了多核并行,可开多队列:

nfqueue:
  mode: repeat
  batchcount: 40
  queue-balance: [0,5]   # 使用 0..5 共 6 个队列

iptables 规则对齐(替换前面注释的行):

iptables -I INPUT -p tcp -m multiport --dports 80,443 -j NFQUEUE --queue-balance 0:5
iptables -I INPUT -p udp --dport 53 -j NFQUEUE --queue-balance 0:5

队列长度与内核参数(避免丢包/阻塞):

sysctl -w net.netfilter.nf_queue_qmax=4096

5.A.2 绑核运行

# 假设 CPU 0-11,为 Suricata 预留 4-8 个核
taskset -c 4-11 systemctl start suricata
systemctl enable suricata

方案 B:AF_PACKET Inline(内核态更稳、吞吐高)

优点:更接近线速,减少用户态队列瓶颈;
缺点:需要对网卡/Offload 细节把握,配置偏“硬核”。

suricata.yaml(核心片段):

af-packet:
  - interface: eth0
    cluster-id: 99
    cluster-type: cluster_flow
    defrag: yes
    tpacket-v3: yes
    use-mmap: yes
    ring-size: 300000
    block-size: 65536
    buffer-size: 64512
    copy-mode: ips
    copy-iface: eth0
    checksum-checks: auto
    # inline 模式:
    # IPS: setting "inline: yes" in default-rule-path or run with --af-packet

outputs:
  - eve-log:
      enabled: yes
      filetype: regular
      filename: /var/log/suricata/eve.json

Offload 注意(否则出现校验伪告警/掉包):

ethtool -K eth0 gro off lro off gso off tso off

启动:

suricata --af-packet -c /etc/suricata/suricata.yaml -D

步骤六:DPI 规则与示例(HTTP/TLS/DNS/SSH)

规则可用 ET/Open Rules 打底,结合业务定制;此处给几个“跨境恶意流量”高命中示例。

6.1 TLS/JA3 异常(常见扫描器)

/etc/suricata/rules/local.rules:

# 某些已知扫描器 JA3 指纹(示例哈希)
drop tls any any -> $HOME_NET any (msg:"TLS JA3 known scanner"; \
 ja3_hash; content:"76972c4f4b1a..."; startswith; sid:900001; rev:1;)

# 没有 SNI 的 HTTPS 握手(大规模探测时常见,但谨慎启用)
alert tls any any -> $HOME_NET 443 (msg:"TLS no SNI"; tls_sni; content:""; \
 isdataat:!1,relative; threshold:type both, track by_src, count 10, seconds 60; \
 sid:900002; rev:1;)

6.2 HTTP 可疑 UA/方法滥用

drop http any any -> $HOME_NET 80 (msg:"HTTP suspicious UA masscan/zgrab"; \
 http.user_agent; pcre:"/masscan|zgrab|curl\/7\.(2[0-9]|3[0-9])/Ri"; \
 classtype:bad-unknown; sid:900010; rev:2;)

6.3 DNS 隧道/ANY 滥用

drop dns any any -> $HOME_NET 53 (msg:"DNS long label suspicious"; \
 dns.query; pcre:"/([A-Za-z0-9]{40,})/"; sid:900020; rev:1;)

alert dns any any -> $HOME_NET 53 (msg:"DNS ANY flood"; \
 dns.qtype; content:"ANY"; threshold:type both, track by_src, count 20, seconds 60; \
 sid:900021; rev:1;)

6.4 SSH 暴力(借 Suricata 计数 + 动态封禁)

alert ssh any any -> $HOME_NET 22 (msg:"SSH brute force"; flow:to_server; \
 threshold:type both, track by_src, count 15, seconds 120; sid:900030; rev:1;)

步骤七:事件联动 —— 自动加入 ipset 黑名单

创建 /usr/local/bin/eve2ipset.sh:

#!/usr/bin/env bash
EVE=/var/log/suricata/eve.json
SET=drop_temp
TAIL=/usr/bin/tail
JQ=/bin/jq

$TAIL -Fn0 $EVE | while read line; do
  # 针对我们定义的 sid 列表做动作
  echo "$line" | $JQ -r 'select(.alert.sid|inside([900001,900010,900020,900030])) 
     | .src_ip' | while read ip; do
       # 加入黑名单 24 小时自动过期
       ipset add $SET $ip timeout 86400 2>/dev/null && \
         logger -t eve2ipset "blocked $ip by DPI"
     done
done

服务化:

cat >/etc/systemd/system/eve2ipset.service <<EOF
[Unit]
Description=EVE to IPSet autoblocker
After=suricata.service

[Service]
ExecStart=/usr/local/bin/eve2ipset.sh
Restart=always

[Install]
WantedBy=multi-user.target
EOF

chmod +x /usr/local/bin/eve2ipset.sh
systemctl daemon-reload
systemctl enable --now eve2ipset

步骤八:灰度策略与回滚

  • 第 1~2 天:Suricata 只 alert,不 drop,观察 eve.json 中业务误报;
  • 第 3 天:对某一端口(如 22)启用 drop,并开启 eve2ipset;
  • 第 5 天:扩大到 80/443,保持 hashlimit/SYNPROXY;

随时回滚:

  • 停 Suricata:systemctl stop suricata;
  • 移除 NFQUEUE 规则:iptables -D INPUT ... NFQUEUE ...;
  • 清空临时黑名单:ipset flush drop_temp。

验证与基准结果(我们的实测样本)

指标 灰度前(仅 L4) 组合拳上线后(L4+L7)
PPS 峰值可承载(不丢包) ~ 600k ~ 900k
80/443 新建连接 95 分位握手时延 12.3 ms 10.8 ms
CPU(单机总) 42–68% 38–55%
误封率(按 7 天解除) < 0.05%
SSH 暴力成功率 低但存在 近 0(秒级封禁)

注:NFQUEUE 模式下,把队列扩展到 0..5 并进行绑核后,抖动明显下降;AF_PACKET inline 模式下关闭 LRO/GRO 后恢复正常。

运行与排障清单(我踩过的坑)

NFQUEUE 堵塞:

  • 症状:suricata 日志出现 packet queue drop,延迟拉高;
  • 处理:提高 net.netfilter.nf_queue_qmax,queue-balance 多队列 + Suricata runmode: workers,并绑核。

校验和/Offload 干扰:

  • 症状:DPI 报告 checksum invalid;
  • 处理:ethtool -K eth0 gro/lro/gso/tso off。

SYNPROXY 与探活冲突:

  • 对健康检查源(如 100.64.0.0/10)在 raw 或 filter 前置 ACCEPT,绕开。

conntrack 表爆:

  • 现象:dmesg 出现 nf_conntrack: table full, dropping packet;
  • 处理:提升 nf_conntrack_max/buckets,并用 raw 表尽早丢弃无需跟踪的流。

地理库老化/ASN 变更:

  • 误拦合法用户或漏拦恶意;
  • 处理:不要把地理过滤当银弹。用 hashlimit/SYNPROXY 限流 + DPI 精准识别,地理库只作“权重”。

Suricata 规则误报:

  • 调整为 threshold 或改为 alert 观察期,再升级到 drop;
  • 给每条本地规则明确 sid,便于联动脚本精确匹配。

双网口与路由策略:

  • 旁路抓包与业务口混用时注意 rp_filter 与多路由表,避免反向路径校验误封。

运维日常与可视化

eve.json 快速视图:

jq -r 'select(.alert) | "\(.timestamp)\t\(.src_ip)\t\(.alert.signature)"' /var/log/suricata/eve.json | tail

统计 top 源:

jq -r 'select(.alert) | .src_ip' /var/log/suricata/eve.json | sort | uniq -c | sort -nr | head

Nginx 访问日志与 Suricata 告警按 IP 关联,确认是否误封。

安全上线 checklist(可打印)

  1.  sysctl 生效,rp_filter=0;
  2.  iptables 规则含:raw 前置丢弃、SYNPROXY、hashlimit、NFQUEUE/或 AF_PACKET;
  3.  ipset 集合及 TTL 封禁脚本运行;
  4.  Suricata 先 IDS 再 IPS,本地规则 sid 管理清晰;
  5.  Offload 关闭,PPS 压测通过;
  6.  回滚路径可一键执行(stop Suricata + 删除 NFQUEUE + flush ipset)。

参考脚本:一键部署骨架(可按需裁剪)

#!/usr/bin/env bash
set -e

echo "[1/6] Install deps"
yum install -y epel-release iptables-services suricata jq ipset

echo "[2/6] Sysctl tuning"
cat >/etc/sysctl.d/99-net-tuning.conf <<'EOF'
# (同上内容略)
EOF
sysctl --system

echo "[3/6] ipset sets"
for s in allow_mgmt drop_temp drop_asn allow_geo; do
  ipset create $s hash:ip timeout 0 2>/dev/null || true
done
ipset flush drop_temp

echo "[4/6] iptables rules"
cat >/etc/sysconfig/iptables <<'EOF'
# (同上规则片段,按需粘贴完整版)
EOF
systemctl enable --now iptables

echo "[5/6] Suricata + NFQUEUE"
# 这里默认 NFQUEUE,如果用 AF_PACKET 改 suricata.yaml 即可
sed -i 's/^# *- local.rules/- local.rules/' /etc/suricata/suricata.yaml
systemctl enable --now suricata

echo "[6/6] eve2ipset autoblocker"
cat >/usr/local/bin/eve2ipset.sh <<'EOS'
# (同上脚本)
EOS
chmod +x /usr/local/bin/eve2ipset.sh
cat >/etc/systemd/system/eve2ipset.service <<'EOS'
# (同上 service)
EOS
systemctl daemon-reload
systemctl enable --now eve2ipset

echo "Done."

成本与收益

成本:一次性的规则与联动脚本投入;NFQUEUE 调优时间较长;AF_PACKET 对网卡/内核有认知门槛。

收益:把“脏流量”大头挡在 L3/L4,L7 精确识别再做自治惩罚,在不依赖昂贵硬件清洗的前提下,把稳定性和安全性拉到一个新台阶。

组合拳上线的第二周,一个跨境扫描的波峰又来了。
这回大屏上的红线很安静,Suricata 的 eve.json 一条条写着“JA3 known scanner dropped”,drop_temp 集合的计数在嗒嗒往上跳。负载稳在 0.x,NOC 小伙打了个哈欠问我:“还用不着清洗厂商吧?”
我把最后一口冷咖啡干掉,笑了:“今晚让软件先顶着。”
对我来说,这套在 CentOS 7 上打磨出来的 iptables + DPI 防御,不是某种完美主义,而是业务在风口浪尖活下来的务实主义。你也可以很快把它复刻到你的香港节点上,然后按你的业务去打磨属于你自己的本地规则集。

附:常用命令速查

# 查看队列统计
watch -n1 "nfnl_osf -l 2>/dev/null || cat /proc/net/netfilter/nfnetlink_queue"

# Suricata 运行态检查
suricata -T -c /etc/suricata/suricata.yaml   # 测试配置
journalctl -u suricata -f

# ipset / iptables
ipset list drop_temp | head
iptables -L -n -v --line-numbers

# 快速回滚
systemctl stop suricata
iptables -S | grep NFQUEUE | sed 's/^-A /-D /' | bash
ipset flush drop_temp
目录结构
全文