232 iptables:规则表与链的深度剖析
前言
上一篇文章我们深入理解了 Netfilter 的 HOOK 机制——那是整个框架的底层基础设施。如果把 Netfilter 看作一条高速公路的匝道系统,那么 iptables 就是这条匝道上的收费站的规则手册。它定义了”什么车可以过”、”什么车要收费”、”什么车直接拦下”。
iptables 是 Linux 中最经典、使用最广泛的防火墙配置工具。尽管近年来 nftables 正在逐步取代它,但理解 iptables 的设计理念,对于掌握 Linux 网络安全的核心逻辑依然不可或缺。
本文将从表链机制、匹配原理、规则处理流程、实用场景和 demo 五个维度全面剖析 iptables,并对比 Windows WFP 的规则配置模型。
1. 表与链的架构体系
1.1 为什么需要”表”和”链”?
Netfilter 只有 5 个 HOOK 点。如果直接在这 5 个 HOOK 上写全部规则,规则会非常混乱:
# 混杂的规则示例(不要这样做!)
-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 点。
┌─────────────────────────────────────────────────────────────────┐
│ 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 服务器可以跳过连接跟踪来降低延迟:
# 放行 DNS 但不跟踪连接
iptables -t raw -A PREROUTING -p udp --dport 53 -j NOTRACK
iptables -t raw -A OUTPUT -p udp --sport 53 -j NOTRACK1.2.2 mangle 表
mangle 表用于修改数据包的 IP 头字段,不涉及地址转换:
# 修改 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 641.2.3 nat 表
NAT 表在连接跟踪之后处理(除了 raw 之上的连接),负责网络地址转换:
┌─────────────────────────────────────────────────────────────┐
│ NAT 处理流程(以 DNAT 为例) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 外部请求: 200.1.1.1:12345 → 100.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:12345 → 192.168.1.10:8080 │
│ │ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ POSTROUTING (SNAT) │ │
│ │ 源 IP 伪装为网关 IP │ │
│ └─────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘1.2.4 filter 表
filter 表是大多数人接触最多的表,负责基本的包过滤决策:
# 默认策略
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 # HTTP1.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 匹配流程
┌──────────────────────────────────────────────────────────────┐
│ iptables 规则匹配完整流程 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 数据包到达 HOOK 点(如 INPUT) │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 遍历链中第一条规则 │ ← 规则按添加顺序排列(A=Append) │
│ └────────┬────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ ▼ ▼ │
│ 匹配? 不匹配? │
│ │ │ │
│ ▼ ▼ │
│ 执行 Target 继续下一条 │
│ (ACCEPT/ 规则 │
│ DROP/ │ │
│ REJECT/ │ │
│ ... ) │ │
│ │ │ │
│ │ ┌─────┴──────┐ │
│ │ ▼ ▼ │
│ │ 还有规则? 没有更多规则 → 应用默认策略 (Policy) │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ 下一条规则 执行 Policy │
│ │ (继续匹配) (ACCEPT/DROP) │
│ │ │
│ └──────────┌───────────────┘ │
│ ▼ │
│ 返回 Verdict │
│ │
└──────────────────────────────────────────────────────────────┘2.2 通用匹配(generic match)
所有 iptables 规则都支持的基础匹配条件:
# 协议匹配
-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 通过扩展模块提供丰富的匹配能力:
# 连接状态扩展 (-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 REJECT2.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 流量重定向到本地代理服务器:
# 场景: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 MASQUERADE3.2 场景二:防 DDoS(连接状态+限流)
#!/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.v43.3 场景三:端口映射(公网访问内网服务)
#!/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 MASQUERADE3.4 综合 Demo:完整的防火墙脚本
#!/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,执行:
chmod +x firewall_setup.sh
sudo ./firewall_setup.sh
ping -c 5 127.0.0.1
sudo iptables -L -v -n4. iptables 规则查看与诊断
4.1 常用查看命令
# 查看 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.v44.2 常用调试技巧
# 查看哪些规则匹配了某个连接
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 防火墙配置通过以下方式:
- netsh advfirewall(Windows Vista+):Windows 防火墙的 CLI 接口
- Windows Firewall COM API:程序化配置
- 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 架构差异
┌─────────────────────────────────────────────────────────────┐
│ 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 状态下清空规则
这是新手最常犯的错误——清空规则时默认策略还没设置,会瞬间断连。
正确做法:
# 在配置完整规则之前,先设置默认策略
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”放在前面,后面的”放行”规则永远不会生效:
# 错误顺序示例
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 原子加载
避免逐条执行规则导致中间状态不一致:
# 将所有规则写入文件
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
🔗 原创内容,转载须获授权 | 禁止匿名转载
💬 留言区:你的防火墙规则有哪些”雷打不动”的配置?欢迎分享
评论