232 iptables:规则表与链的深度剖析

前言

上一篇文章我们深入理解了 Netfilter 的 HOOK 机制——那是整个框架的底层基础设施。如果把 Netfilter 看作一条高速公路的匝道系统,那么 iptables 就是这条匝道上的收费站的规则手册。它定义了”什么车可以过”、”什么车要收费”、”什么车直接拦下”。

iptables 是 Linux 中最经典、使用最广泛的防火墙配置工具。尽管近年来 nftables 正在逐步取代它,但理解 iptables 的设计理念,对于掌握 Linux 网络安全的核心逻辑依然不可或缺。

本文将从表链机制、匹配原理、规则处理流程、实用场景和 demo 五个维度全面剖析 iptables,并对比 Windows WFP 的规则配置模型。


1. 表与链的架构体系

1.1 为什么需要”表”和”链”?

Netfilter 只有 5 个 HOOK 点。如果直接在这 5 个 HOOK 上写全部规则,规则会非常混乱:

Bash
# 混杂的规则示例(不要这样做!)
-A INPUT -p tcp --dport 22 -j ACCEPT   # 这条是防火墙
-A INPUT -p tcp --dport 80 -j DNAT ... # 这条是 NAT
-A INPUT -p tcp --dport 80 -j ACCEPT   # 这条又是防火墙

iptables 引入表(Table)来区分不同功能域:filter 表专注”通过/拒绝”,nat 表专注”地址转换”,mangle 表专注”修改报文”。然后在每个表内部再分链(Chain)来对应不同的 HOOK 点。

Bash
┌─────────────────────────────────────────────────────────────────┐
│               iptables 表与链映射全景图                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   5 个 Netfilter HOOK 点 (纵向)  vs  4 个 iptables 表 (功能域)   │
│                                                                 │
│            PREROUTING   LOCAL_IN   FORWARD  LOCAL_OUT POSTROUTING │
│                 │          │         │        │         │      │
│                 ▼          ▼         ▼        ▼         ▼      │
│           ┌─────────────────────────────────────────────────┐   │
│           │                    raw 表                        │   │
│           │  (最高优先级,最早处理,用于NOTRACK跳过连接跟踪)    │   │
│           └────────┬────────┬────────┬────────┬────────────┘   │
│                    │        │        │        │                 │
│           ┌────────▼────────▼────────▼────────▼────────────┐   │
│           │                  mangle 表                      │   │
│           │    (修改报文内容:TTL/TOS/MARK/QOS)              │   │
│           └────────┬────────┬────────┬────────┬────────────┘   │
│                    │        │        │        │                 │
│           ┌────────▼────────▼────────▼────────▼────────────┐   │
│           │                    nat 表                       │   │
│           │  (网络地址转换:DNAT/SNAT/PAT)                   │   │
│           └────────┬────────┬────────┬────────┬────────────┘   │
│                    │        │        │        │                 │
│           ┌────────▼────────▼────────▼────────▼────────────┐   │
│           │                  filter 表                       │   │
│           │    (数据包过滤: ACCEPT/DROP/REJECT)             │   │
│           └─────────────────────────────────────────────────┘   │
│                                                                 │
│   默认策略链 (由用户创建): INPUT / OUTPUT / FORWARD             │
│   特殊内建链: PREROUTING / POSTROUTING (不可删除)               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.2 四张表详解

1.2.1 raw 表

raw 表的优先级最高,在连接跟踪之前处理。主要用途是 NOTRACK——告诉内核某些连接不要进入 conntrack 系统,从而跳过 NAT 和状态检测。

场景:高性能 DNS 服务器可以跳过连接跟踪来降低延迟:

Bash
# 放行 DNS 但不跟踪连接
iptables -t raw -A PREROUTING -p udp --dport 53 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --sport 53 -j NOTRACK

1.2.2 mangle 表

mangle 表用于修改数据包的 IP 头字段,不涉及地址转换:

Bash
# 修改 TOS 字段( DiffServ 服务类型)
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j TOS --set-tos 0x10  # Minimize-Delay

# 设置 MARK(配合 tc 限速)
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 100

# 修改 TTL
iptables -t mangle -A POSTROUTING -p ipv4 -j TTL --ttl-set 64

1.2.3 nat 表

NAT 表在连接跟踪之后处理(除了 raw 之上的连接),负责网络地址转换:

Bash
┌─────────────────────────────────────────────────────────────┐
│              NAT 处理流程(以 DNAT 为例)                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   外部请求:  200.1.1.1:12345100.1.1.100:80               │
│                 │                                           │
│                 ▼                                           │
│   ┌─────────────────────────┐                               │
│   │  PREROUTING (DNAT)     │                               │
│   │  -p tcp --dport 80     │                               │
│   │  -j DNAT --to-dest     │                               │
│   │    192.168.1.10:8080   │                               │
│   └────────────┬───────────┘                               │
│                │                                           │
│                ▼                                           │
│   路由决策: 目标 IP = 192.168.1.10                         │
│                │                                           │
│                ▼                                           │
│   ┌─────────────────────────┐                               │
│   │  FORWARD / INPUT        │                               │
│   │  (filter 规则检查)      │                               │
│   └────────────┬───────────┘                               │
│                │                                           │
│                ▼                                           │
│   内部转发: 192.168.1.1:12345192.168.1.10:8080          │
│                │                                           │
│                ▼                                           │
│   ┌─────────────────────────┐                               │
│   │  POSTROUTING (SNAT)     │                               │
│   │  源 IP 伪装为网关 IP     │                               │
│   └─────────────────────────┘                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2.4 filter 表

filter 表是大多数人接触最多的表,负责基本的包过滤决策:

Bash
# 默认策略
iptables -P INPUT DROP      # 默认拒绝入站
iptables -P FORWARD DROP    # 默认拒绝转发
iptables -P OUTPUT ACCEPT   # 默认放行出站

# 基础放行规则
iptables -A INPUT -i lo -j ACCEPT            # 放行本地回环
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT  # 放行已建立连接
iptables -A INPUT -p tcp --dport 22 -j ACCEPT  # SSH
iptables -A INPUT -p tcp --dport 80 -j ACCEPT  # HTTP

1.3 五条内建链

每个 Netfilter HOOK 点对应的默认链:

HOOK 点 内建链 所属表(默认) 主要用途
PRE_ROUTING PREROUTING raw, mangle, nat DNAT、端口映射
LOCAL_IN INPUT raw, mangle, filter, nat 防火墙入站
FORWARD FORWARD mangle, filter 防火墙转发
LOCAL_OUT OUTPUT raw, mangle, nat, filter 防火墙出站、SNAT
POST_ROUTING POSTROUTING mangle, nat SNAT/MASQUERADE

注意:INPUT 和 OUTPUT 链默认属于多个表,按优先级顺序遍历。


2. 规则匹配原理

2.1 匹配流程

Bash
┌──────────────────────────────────────────────────────────────┐
│           iptables 规则匹配完整流程                           │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│  数据包到达 HOOK 点(如 INPUT)                               │
│         │                                                    │
│         ▼                                                    │
│  ┌─────────────────┐                                         │
│  │ 遍历链中第一条规则 │ ← 规则按添加顺序排列(A=Append)      │
│  └────────┬────────┘                                         │
│           │                                                  │
│     ┌─────┴─────┐                                           │
│     ▼           ▼                                           │
│  匹配?      不匹配?                                           │
│     │           │                                           │
│     ▼           ▼                                           │
│  执行 Target   继续下一条                                     │
│  (ACCEPT/     规则                                            │
│   DROP/       │                                               │
│   REJECT/     │                                               │
│   ... )       │                                               │
│     │           │                                            │
│     │     ┌─────┴──────┐                                     │
│     │     ▼            ▼                                    │
│     │   还有规则?     没有更多规则   →  应用默认策略 (Policy) │
│     │     │              │                                   │
│     │     ▼              ▼                                   │
│     │   下一条规则      执行 Policy                          │
│     │   (继续匹配)      (ACCEPT/DROP)                        │
│     │                                                    │
│     └──────────┌───────────────┘                             │
│                ▼                                            │
│           返回 Verdict                                       │
│                                                              │
└──────────────────────────────────────────────────────────────┘

2.2 通用匹配(generic match)

所有 iptables 规则都支持的基础匹配条件:

Bash
# 协议匹配
-p tcp / -p udp / -p icmp / -p all

# 源/目标 IP 匹配
-s 192.168.1.0/24 -d 10.0.0.1

# 接口匹配(流入/流出)
-i eth0 -o eth1

# 片段分包
-f  # 匹配分片包的后续片段

2.3 专用匹配(match extensions)

iptables 通过扩展模块提供丰富的匹配能力:

Bash
# 连接状态扩展 (-m state)
-m state --state NEW,ESTABLISHED,RELATED,INVALID

# 端口扩展 (-m multiport)
-m multiport --dports 22,80,443

# IP 地址范围扩展 (-m iprange)
-m iprange --src-range 192.168.1.1-192.168.1.100

# 连接限流扩展 (-m limit)
-m limit --limit 100/minute --limit-burst 200

# 连接跟踪模块 (-m conntrack)
-m conntrack --ctstate ESTABLISHED -j ACCEPT

# 字符串匹配 (-m string)
-m string --string "malware" --algo bm

# 时间范围 (-m time)
-m time --timestart 09:00 --timestop 18:00 --weekdays Mon Fri

# 连接数限制 (-m connlimit)
-m connlimit --connlimit-above 10 -j REJECT

2.4 Target 判决

规则匹配后的处理动作:

Target 效果 说明
ACCEPT 放行 数据包通过,当前链终止
DROP 丢弃 无响应(适合外部流量)
REJECT 拒绝 返回 ICMP/TCP RST(适合配置错误反馈)
DNAT 改目标 NAT 表 PREROUTING
SNAT 改源 NAT 表 POSTROUTING
MASQUERADE 伪装 SNAT 的动态版本
LOG 日志 记录到内核日志
MARK 标记 打标记配合 tc
RETURN 返回 停止当前链,继续上一级链
REDIRECT 重定向 端口转发(本地)

3. 实用场景与 Demo

3.1 场景一:透明代理(NAT 重定向)

将内网所有 HTTP 流量重定向到本地代理服务器:

Bash
# 场景:Squid 代理监听 3128
# 将内网 192.168.1.0/24 的 HTTP 流量重定向到本地 3128

# 1. 开启 IP 转发
echo 1 > /proc/sys/net/ipv4/ip_forward

# 2. DNAT:在 PREROUTING 将 HTTP 流量 DNAT 到本地代理
iptables -t nat -A PREROUTING -i eth0 -s 192.168.1.0/24 
    -p tcp --dport 80 -j REDIRECT --to-port 3128

# 3. 如果代理服务器不在本机
iptables -t nat -A PREROUTING -i eth0 -s 192.168.1.0/24 
    -p tcp --dport 80 -j DNAT --to-destination 192.168.1.200:3128

# 4. POSTROUTING:源地址伪装,使返回流量正确路由
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j MASQUERADE

3.2 场景二:防 DDoS(连接状态+限流)

Bash
#!/bin/bash
# 防 DDoS 基础脚本

# 清理旧规则
iptables -F
iptables -X
iptables -t nat -F

# 默认策略
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# 放行本地回环
iptables -A INPUT -i lo -j ACCEPT

# 放行已建立连接和 RELATED(允许返回)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 放行 SSH(防锁死)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 放行 HTTP/HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# ICMP 限流(防 Ping Flood)
iptables -A INPUT -p icmp --icmp-type echo-request 
    -m limit --limit 1/s --limit-burst 4 -j ACCEPT

# 每 IP 最大 100 个并发连接
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 100 -j DROP

# 每分钟最多 60 个新 HTTP 连接
iptables -A INPUT -p tcp --dport 80 -m state --state NEW 
    -m limit --limit 60/minute --limit-burst 10 -j ACCEPT

# 日志 DDoS 告警
iptables -A INPUT -p tcp --dport 80 -m state --state NEW 
    -m recent --set --name HTTP_DOS
iptables -A INPUT -p tcp --dport 80 -m state --state NEW 
    -m recent --update --seconds 60 --hitcount 100 --name HTTP_DOS 
    -j LOG --log-prefix "DDoS DETECTED: "

# 保存
iptables-save > /etc/iptables/rules.v4

3.3 场景三:端口映射(公网访问内网服务)

Bash
#!/bin/bash
# 将公网 IP:8080 映射到内网 192.168.1.100:80

# DNAT:外部访问公网 IP:8080 → 内网 192.168.1.100:80
iptables -t nat -A PREROUTING -p tcp -d $(curl -s ifconfig.me) 
    --dport 8080 -j DNAT --to-destination 192.168.1.100:80

# FORWARD 放行该转发流量
iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 80 -j ACCEPT

# SNAT/MASQUERADE 保证返回流量
iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.100 
    --dport 80 -j MASQUERADE

3.4 综合 Demo:完整的防火墙脚本

Bash
#!/bin/bash
# iptables 全局配置 demo — 适合作为服务器初始安全加固

set -e

echo "=== 开始 iptables 防火墙配置 ==="

# ========== 基本清理 ==========
iptables -F
iptables -X
iptables -t nat -F
iptables -t mangle -F

# ========== 默认策略 ==========
iptables -P INPUT   DROP
iptables -P FORWARD DROP
iptables -P OUTPUT  ACCEPT

# ========== 放行 loopback ==========
iptables -A INPUT -i lo -j ACCEPT

# ========== 允许已建立连接 ==========
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# ========== SSH (保守限制来源) ==========
# 生产环境请替换 0.0.0.0/0 为具体 IP 段
iptables -A INPUT -p tcp -s 0.0.0.0/0 --dport 22 -j ACCEPT

# ========== HTTP/HTTPS ==========
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# ========== ICMP (Ping) 限流 ==========
iptables -A INPUT -p icmp --icmp-type echo-request 
    -m limit --limit 3/s --limit-burst 10 -j ACCEPT

# ========== DNS ==========
iptables -A INPUT -p udp --dport 53 -j ACCEPT

# ========== NTP ==========
iptables -A INPUT -p udp --dport 123 -j ACCEPT

# ========== 日志 dropped 的包 ==========
iptables -A INPUT -m limit --limit 5/min -j LOG 
    --log-prefix "IPTables-Dropped: " --log-ip-options

# ========== 查看结果 ==========
echo ""
echo "=== 当前规则 ==="
iptables -L -v -n --line-numbers

echo ""
echo "=== NAT 规则 ==="
iptables -t nat -L -v -n

echo ""
echo "=== 包计数 ==="
iptables -Z
echo "请运行: ping -c 5 127.0.0.1 && iptables -L -v -n 检查包计数变化"

保存为 firewall_setup.sh,执行:

Bash
chmod +x firewall_setup.sh
sudo ./firewall_setup.sh
ping -c 5 127.0.0.1
sudo iptables -L -v -n

4. iptables 规则查看与诊断

4.1 常用查看命令

Bash
# 查看 filter 表(默认表)
iptables -L -n -v

# 查看 nat 表
iptables -t nat -L -n -v

# 查看 mangle 表
iptables -t mangle -L -n -v

# 查看 raw 表
iptables -t raw -L -n -v

# 带行号查看(方便删除指定规则)
iptables -L INPUT -n --line-numbers
iptables -L OUTPUT -n --line-numbers

# 实时查看包计数变化
watch -n1 'iptables -L -n -v -Z'

# 保存当前规则
iptables-save > /etc/iptables/rules.v4

# 从文件恢复规则
iptables-restore < /etc/iptables/rules.v4

4.2 常用调试技巧

Bash
# 查看哪些规则匹配了某个连接
iptables -L -n -v | grep "192.168.1.50"

# 追踪特定连接(需要 raw 表 TRACE target)
iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE
iptables -t raw -A OUTPUT -p tcp --dport 80 -j TRACE
dmesg -w | grep -i "TRACE"

# 查看 conntrack 条目
cat /proc/net/nf_conntrack

# 查看 conntrack 统计
cat /proc/net/stat/nf_conntrack

# conntrack 工具(需要 install conntrack)
conntrack -L -p tcp --dport 80
conntrack -F  # 清空 conntrack 表

5. Windows 实现差距

5.1 Windows 的等效方案

Windows 上没有直接等价于 iptables 的命令行工具(直到 Windows Server 2025 才开始引入 netsh advfirewall 作为更现代的替代)。传统上,Windows 防火墙配置通过以下方式:

  1. netsh advfirewall(Windows Vista+):Windows 防火墙的 CLI 接口
  2. Windows Firewall COM API:程序化配置
  3. WFP (Winsock Filtering Platform):底层过滤框架

5.2 命令对照表

iptables Windows netsh advfirewall
iptables -P INPUT DROP netsh advfirewall set allprofiles default Inbound block
iptables -A INPUT -p tcp --dport 22 -j ACCEPT netsh advfirewall firewall add rule name="SSH" dir=in action=allow protocol=TCP localport=22
iptables -A INPUT -i lo -j ACCEPT 自动处理(Windows loopback 是白名单)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT netsh advfirewall set allprofiles settings InboundExceptions enable
iptables -A FORWARD ... -j DNAT netsh interface portproxy add v4tov4 listenport=8080 connectaddress=192.168.1.100 connectport=80
iptables-save netsh advfirewall export "C:fw.wfw"
iptables-restore netsh advfirewall import "C:fw.wfw"

5.3 架构差异

Bash
┌─────────────────────────────────────────────────────────────┐
│            iptables vs Windows Firewall 架构对比            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Linux: iptables → netfilter → kernel network stack         │
│         (用户态规则)    (HOOK 框架)   (数据包处理)            │
│                                                             │
│  Windows: netsh advfirewall → Windows Firewall Service      │
│           (CLI 配置)        (用户态服务)                    │
│                    ↓                                       │
│           WFP Callout Drivers → NDIS → NIC                  │
│           (内核过滤)     (网络驱动)                         │
│                                                             │
│  关键差异:                                                   │
│  1. Linux 的 iptables 和 netfilter 完全在内核实现            │
│     Windows 的防火墙规则经过用户态服务中转                   │
│  2. Linux 的 iptables 可以精细控制每个 HOOK 点               │
│     Windows advfirewall 只能按 "入站/出站/专用/公用" 分组    │
│  3. Linux 的 NAT 功能集成在 netfilter/iptables              │
│     Windows 的 NAT 独立于防火墙,是 Routing and Remote Access │
│                                                             │
└─────────────────────────────────────────────────────────────┘

6. 常见陷阱与最佳实践

6.1 不要在远程 SSH 状态下清空规则

这是新手最常犯的错误——清空规则时默认策略还没设置,会瞬间断连。

正确做法:

Bash
# 在配置完整规则之前,先设置默认策略
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# 然后添加 SSH 规则
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 确认后再清空并重新配置

6.2 注意规则顺序

iptables 按顺序匹配,第一条匹配就执行。如果把”通用 DROP”放在前面,后面的”放行”规则永远不会生效:

Bash
# 错误顺序示例
iptables -A INPUT -j DROP                  # 全部丢弃(应该放最后)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT  # 这条永远不会生效

# 正确顺序
iptables -A INPUT -p tcp --dport 22 -j ACCEPT  # 放行 SSH 在前
iptables -A INPUT -j DROP                     # 拒绝其他的在后

6.3 使用 iptables-restore 原子加载

避免逐条执行规则导致中间状态不一致:

Bash
# 将所有规则写入文件
cat > /etc/iptables/rules.v4 <<'EOF'
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp --dport 22 -j ACCEPT
-A INPUT -p tcp --dport 80 -j ACCEPT
COMMIT
EOF

# 原子加载
iptables-restore < /etc/iptables/rules.v4

结语

iptables 的设计体现了 Linux 的哲学——小工具组合,通过表和链的层次结构,把看似复杂的防火墙逻辑分解为清晰可管理的单元。5 个内建链、4 张表、数百种匹配条件和 Target,构成了一个表达能力极强却又相对容易掌握的网络安全配置系统。

理解表链的优先级顺序、各表的作用域、规则的匹配顺序和默认策略,是掌握 Linux 防火墙的必经之路。

下一篇文章,我们将深入连接跟踪(conntrack)系统——这是 iptables 状态检测的幕后功臣,也是 NAT 能够正常工作的关键技术基础。


📢 公众号:Kernel Hacker
🔍 系列:操作系统内核深度探索 | 第 232 篇
📅 发布日期:2025-07-16
🔗 原创内容,转载须获授权 | 禁止匿名转载
💬 留言区:你的防火墙规则有哪些”雷打不动”的配置?欢迎分享

最后修改: 2024年4月8日

作者

评论

发表评论

您的邮箱地址不会被公开。