iptables 完全指南:四表五链、策略配置、场景实战与排障
iptables 是 Linux 内核 netfilter 框架的用户态配置工具,所有 Linux 网络防火墙的根基。理解了四表(raw/mangle/nat/filter)五链(PREROUTING/INPUT/FORWARD/OUTPUT/POSTROUTING)的匹配顺序,不管是 CentOS 6 的 iptables 还是 RHEL 9 的 nftables 后端,还是 Docker/K8s 的网络规则,都能看懂、改对、排障。本文覆盖四表五链数据流图、filter 表策略(默认 DROP vs ACCEPT、白名单思路)、nat 表(SNAT/DNAT/MASQUERADE/端口转发)、mangle 表(QoS 标记/TTL)、state 模块状态追踪、常用匹配条件 30 条、6 个生产场景配置(Web 服务器/网关路由/Docker 适配/端口转发/防 CC/限速)、save/restore 持久化、firewalld 与 iptables 关系、以及 12 个高频排障场景。
一、iptables 是什么
netfilter 与 iptables 的关系
flowchart TB
subgraph K["Linux 内核"]
N["netfilter 框架(内核态,5 个钩子点)<br/>在网络栈的关键位置插入 hook<br/>数据包经过时触发对应的 chain"]
end
T["iptables / nftables(用户态工具)<br/>把人类写的规则翻译成 netfilter 规则"]
T -. 配置下发 .-> N- netfilter:Linux 内核里的数据包过滤框架,2.4 起内置。在内核网络栈的 5 个位置插了钩子(hook),数据包路过时会被规则检查。
- iptables:用户态命令行工具,把规则下发到 netfilter。
- nftables:iptables 的继任者(0.9+),语法更统一,但 RHEL 8/9 上
iptables命令仍然可用(底层翻译成 nftables)。
iptables vs firewalld vs ufw
| 工具 | 定位 | 适用 | 说明 |
|---|---|---|---|
| iptables | 底层规则引擎 | 所有 Linux | 最灵活,最底层,规则文件可读 |
| firewalld | 高层抽象(zone + service) | RHEL/CentOS 7+ | 动态管理,运行时修改不丢会话 |
| ufw | 简化前端 | Ubuntu | 最简语法,适合个人机器 |
| nftables | iptables 继任 | RHEL 8+ / Debian 10+ | 统一语法,性能更好 |
结论:服务器运维必须会 iptables。firewalld 和 ufw 底层最终还是翻译成 iptables/nftables 规则。Docker 的网络规则直接用 iptables 写,不经过 firewalld。
二、四表五链
1.1 五条链(Chain)
netfilter 在网络栈的 5 个位置插了钩子,每个钩子对应一条 chain:
flowchart TB
IN(["入方向"]) --> PRE["PREROUTING<br/>(路由判断前)<br/>raw / mangle / nat"]
PRE --> R{"路由判断:<br/>目标是本机?"}
R -- 是 --> INP["INPUT<br/>(发给本机的包)<br/>mangle / filter"]
R -- 否 --> FWD["FORWARD<br/>(转发的包)<br/>mangle / filter"]
INP --> LOCAL["本机进程"]
LOCAL --> OUT["OUTPUT<br/>(本机发出的包)<br/>raw / mangle / nat / filter"]
FWD --> OUT
OUT --> POST["POSTROUTING<br/>(离开网卡前)<br/>mangle / nat"]
POST --> OUTD(["出方向"])| 链 | 触发时机 | 常见表 |
|---|---|---|
| PREROUTING | 数据包进入网卡后、路由判断前 | raw, mangle, nat |
| INPUT | 目标是本机的数据包、交给本机进程前 | mangle, filter |
| FORWARD | 目标不是本机、需要转发的数据包 | mangle, filter |
| OUTPUT | 本机进程产生的数据包、发出前 | raw, mangle, nat, filter |
| POSTROUTING | 数据包发出网卡前 | mangle, nat |
1.2 四张表(Table)
表决定了规则什么时候匹配和能做什么动作:
| 表 | 优先级 | 功能 | 支持的链 |
|---|---|---|---|
| raw | 1(最高) | 在连接追踪之前处理,可标记”不追踪” | PREROUTING, OUTPUT |
| mangle | 2 | 修改数据包的 TOS/TTL/MARK 等 | 全部 5 链 |
| nat | 3 | 地址转换(SNAT/DNAT/MASQUERADE) | PREROUTING, OUTPUT, POSTROUTING |
| filter | 4(最低) | 过滤(ACCEPT/DROP/REJECT) | INPUT, FORWARD, OUTPUT |
优先级:当一个数据包经过一个链时,按 raw → mangle → nat → filter 的顺序依次检查每张表里的规则。
1.3 数据包完整流程图
flowchart TB
subgraph INBOUND["入站包(目标本机)"]
direction TB
I1["网卡"] --> I2["raw:PREROUTING"] --> I3["mangle:PREROUTING"] --> I4["nat:PREROUTING<br/>DNAT"] --> I5["路由判断<br/>(目标是本机)"] --> I6["mangle:INPUT"] --> I7["filter:INPUT"] --> I8["本机进程"]
end
subgraph FORWARDING["转发包(目标非本机)"]
direction TB
F1["网卡"] --> F2["raw:PREROUTING"] --> F3["mangle:PREROUTING"] --> F4["nat:PREROUTING<br/>DNAT"] --> F5["路由判断<br/>(目标非本机)"] --> F6["mangle:FORWARD"] --> F7["filter:FORWARD"] --> F8["mangle:POSTROUTING"] --> F9["nat:POSTROUTING<br/>SNAT"] --> F10["网卡发出"]
end
subgraph OUTBOUND["出站包(本机产生)"]
direction TB
O1["本机进程"] --> O2["raw:OUTPUT"] --> O3["mangle:OUTPUT"] --> O4["nat:OUTPUT"] --> O5["filter:OUTPUT"] --> O6["mangle:POSTROUTING"] --> O7["nat:POSTROUTING<br/>SNAT"] --> O8["网卡发出"]
end三、filter 表:过滤策略
2.1 三个默认链的策略(Policy)
每条链有一个默认策略(Policy),当所有规则都没匹配时执行。 注意:iptables -P 只接受 ACCEPT / DROP 两个值(不能设为 REJECT)。REJECT 只能用作具体规则的 target。
| 类别 | 值 | 含义 | 用法 |
|---|---|---|---|
| 默认策略 (Policy) | ACCEPT | 允许通过 | iptables -P INPUT ACCEPT |
| 默认策略 (Policy) | DROP | 丢弃(不回应,对端超时) | iptables -P INPUT DROP |
| 规则动作 (Target) | REJECT | 拒绝(回 ICMP port-unreachable,对端立即知道被拒) | iptables -A INPUT -p tcp --dport 23 -j REJECT --reject-with icmp-port-unreachable |
# 查看当前规则和策略
iptables -L -n -v --line-numbers
# 设置默认策略
iptables -P INPUT DROP # 默认拒绝所有入站
iptables -P FORWARD DROP # 默认拒绝所有转发
iptables -P OUTPUT ACCEPT # 默认允许所有出站2.2 白名单 vs 黑名单
| 策略模式 | 默认 Policy | 规则思路 | 安全性 | 适用 |
|---|---|---|---|---|
| 白名单(推荐) | DROP | 只放行明确允许的 | 高 | 服务器、生产 |
| 黑名单 | ACCEPT | 只拒绝特定流量 | 低 | 个人机器 |
生产推荐:白名单模式:
# 1. 先放行已建立和关联的连接(不阻断回包)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 2. 放行回环
iptables -A INPUT -i lo -j ACCEPT
# 3. 放行 SSH(管理口)
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
# 4. 放行 Web 服务
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 5. 放行 ICMP(ping)
iptables -A INPUT -p icmp -j ACCEPT
# 6. 默认 DROP(兜底)
iptables -P INPUT DROP⚠️ 注意:先加放行规则,最后再设 DROP。否则 SSH 立刻断开。
四、规则匹配条件
3.1 基础匹配
| 条件 | 说明 | 示例 |
|---|---|---|
-s / --source | 源 IP | -s 192.168.1.0/24 |
-d / --destination | 目标 IP | -d 10.0.0.1 |
-p / --protocol | 协议 | -p tcp / -p udp / -p icmp |
-i / --in-interface | 入网卡 | -i eth0 |
-o / --out-interface | 出网卡 | -o eth1 |
--sport | 源端口 | --sport 1024:65535 |
--dport | 目标端口 | --dport 80 |
3.2 TCP 扩展匹配
# ===== TCP 标志位匹配 =====
#
# 【易错提醒】--tcp-flags MASK COMP 语义:检查 MASK 列出的所有标志位,
# 其组合必须等于 COMP。其中 SYN=1、其余=0 正是"正常握手的合法 SYN 包",
# **不能**直接 DROP,否则所有新 TCP 连接(SSH/HTTP/HTTPS)都会被拒。
#
# 1) 合法 SYN:要么在业务端口放行,要么不做处理(交给后面的规则)。
# 示例:只允许 80/443/22 的新 SYN 进来,并顺便做 SYN 并发限频防 CC:
iptables -N NEW-SYN
iptables -A INPUT -p tcp --tcp-flags ALL SYN -m state --state NEW -j NEW-SYN
iptables -A NEW-SYN -p tcp -m multiport --dports 22,80,443 -m limit --limit 50/s --limit-burst 100 -j ACCEPT
iptables -A NEW-SYN -j DROP
#
# 2) 真正用于"防扫描"的是异常标志位组合,以下才应该 DROP:
#
# NULL scan(全部 0 位):
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
# XMAS scan(全部 1 位,FIN/URG/PSH 同时置位):
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP
# SYN+FIN 同时置位(非法):
iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
# SYN+RST 同时置位(非法):
iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
# FIN 但无 ACK(FIN scan):
iptables -A INPUT -p tcp --tcp-flags FIN,ACK FIN -j DROP
# 同时匹配多个端口
iptables -A INPUT -p tcp -m multiport --dports 80,443,8080 -j ACCEPT
# 源端口范围
iptables -A INPUT -p tcp --sport 1024:65535 -j ACCEPT3.3 state 模块(连接追踪)
state 模块是 iptables 最核心的功能之一,基于连接追踪机制判断数据包属于哪个连接状态:
| 状态 | 含义 | 典型场景 |
|---|---|---|
NEW | 新连接的第一个包 | SYN 包 |
ESTABLISHED | 已建立连接的后续包 | SYN-ACK 之后的数据 |
RELATED | 关联连接(由已建立连接衍生) | FTP 数据连接、ICMP 错误 |
INVALID | 无法识别的包 | 状态异常,应丢弃 |
UNTRACKED | 被 raw 表标记为不追踪 | 特殊绕过场景 |
# 放行已建立的连接(最重要的规则)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 丢弃无效包
iptables -A INPUT -m state --state INVALID -j DROP
# 只允许新连接到 80/443
iptables -A INPUT -p tcp -m multiport --dports 80,443 -m state --state NEW -j ACCEPT3.4 connlimit 模块(并发连接数限制)
# 限制每个 IP 到 22 端口最多 3 个并发连接
iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP
# 限制并发 + 掩码匹配(整个 /24 最多 10 个 SSH)
iptables -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 10 --connlimit-mask 24 -j DROP3.5 limit 模块(速率限制)
# 限制 ping 速率:每秒最多 5 个包
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/s -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
# 限制日志速率(防止日志刷屏)
iptables -A INPUT -p tcp --dport 22 -m limit --limit 3/min --limit-burst 5 -j LOG --log-prefix "SSH-DROP: "| 参数 | 含义 |
|---|---|
--limit 5/s | 平均每秒 5 个包 |
--limit 10/m | 平均每分钟 10 个包 |
--limit-burst 10 | 突发允许 10 个包(令牌桶容量) |
3.6 mac 模块(MAC 地址匹配)
# 只允许特定 MAC 地址访问 SSH
iptables -A INPUT -p tcp --dport 22 -m mac --mac-source 00:11:22:33:44:55 -j ACCEPT
# 阻止某 MAC 访问外网
iptables -A FORWARD -m mac --mac-source AA:BB:CC:DD:EE:FF -j DROP3.7 time 模块(时间段匹配)
# 只在工作时间允许访问 80 端口(周一到周五 9:00-18:00)
iptables -A INPUT -p tcp --dport 80 -m time --weekdays Mon,Tue,Wed,Thu,Fri --timestart 09:00 --timestop 18:00 -j ACCEPT
# 周末禁止 SSH
iptables -A INPUT -p tcp --dport 22 -m time --weekdays Sat,Sun -j DROP3.8 owner 模块(用户匹配)
# 只允许 root 用户发起出站连接
iptables -A OUTPUT -m owner --uid-owner root -j ACCEPT
# 禁止 www-data 用户出站
iptables -A OUTPUT -m owner --uid-owner www-data -j DROP3.9 字符串匹配
# 阻止包含特定字符串的包
iptables -A INPUT -p tcp --dport 80 -m string --string "cmd.exe" --algo bm -j DROP
# 阻止 SQL 注入特征
iptables -A INPUT -p tcp --dport 80 -m string --string "union select" --algo bm -j DROP3.10 常用匹配条件速查
| 匹配 | 命令 | 场景 |
|---|---|---|
| 源 IP | -s 10.0.0.0/8 | 限制来源 |
| 目标 IP | -d 192.168.1.1 | 限制目标 |
| 协议 | -p tcp/udp/icmp | 指定协议 |
| 目标端口 | --dport 80 | 指定端口 |
| 源端口 | --sport 1024:65535 | 高端口范围 |
| 多端口 | -m multiport --dports 80,443,8080 | 一次匹配多个 |
| 连接状态 | -m state --state ESTABLISHED,RELATED | 放行回包 |
| 并发限制 | -m connlimit --connlimit-above 3 | 防 SSH 暴力 |
| 速率限制 | -m limit --limit 5/s | 防 ping 洪泛 |
| MAC | -m mac --mac-source 00:11:22:33:44:55 | 绑定 MAC |
| 时间 | -m time --weekdays Mon-Fri | 工作时间策略 |
| 用户 | -m owner --uid-owner root | 按用户过滤 |
| 字符串 | -m string --string "xxx" --algo bm | 深度包检测 |
| IP 段 | -m iprange --src-range 10.0.0.1-10.0.0.100 | IP 范围 |
| 网卡 | -i eth0 / -o eth1 | 指定网卡 |
五、nat 表:地址转换
4.1 SNAT(源地址转换)
SNAT 修改数据包的源 IP,用于内网通过公网 IP 出去:
# 内网 192.168.1.0/24 通过 eth0 的公网 IP 出去
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.1
# MASQUERADE(动态公网 IP,自动取网卡当前 IP)
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADESNAT vs MASQUERADE:
| 方式 | 适用 | 性能 | 说明 |
|---|---|---|---|
SNAT --to-source | 固定公网 IP | 高 | 直接指定 IP,不需要查网卡 |
MASQUERADE | 动态 IP(PPPoE/DHCP) | 略低 | 每个包都查一次网卡 IP |
4.2 DNAT(目标地址转换)
DNAT 修改数据包的目标 IP,用于端口转发:
# 外部访问 8080 → 转发到内网 192.168.1.100:80
iptables -t nat -A PREROUTING -p tcp --dport 8080 -i eth0 -j DNAT --to-destination 192.168.1.100:80
# 整个端口段转发
iptables -t nat -A PREROUTING -p tcp --dport 8000:9000 -j DNAT --to-destination 192.168.1.100
# SSH 跳板:外部 2222 → 内网 192.168.1.200:22
iptables -t nat -A PREROUTING -p tcp --dport 2222 -j DNAT --to-destination 192.168.1.200:224.3 本地端口转发(REDIRECT)
REDIRECT 是 DNAT 的特例,目标是本机:
# 本机 80 → 本机 8080(透明代理常用)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
# 本机产生的流量也转发
iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port 80804.4 透明代理(配合 REDIRECT)
# 所有 HTTP 流量重定向到 Squid 3128
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3128
# 所有流量重定向到指定代理端口(按协议分流)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3128 # HTTP → Squid
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 3129 # HTTPS → 另一代理4.5 nat 表完整流程
入站包 → PREROUTING (DNAT/REDIRECT) → 路由 → FORWARD/INPUT → POSTROUTING (SNAT/MASQUERADE) → 出注意:nat 表的 PREROUTING 在路由判断之前执行,所以 DNAT 改了目标 IP 后,路由判断会用新 IP 来决定走 FORWARD 还是 INPUT。
六、mangle 表:数据包修改
5.1 TOS 标记(QoS)
# 标记 SSH 流量为低延迟
iptables -t mangle -A PREROUTING -p tcp --dport 22 -j TOS --set-tos 0x10
# 标记 FTP 数据为高吞吐
iptables -t mangle -A PREROUTING -p tcp --dport 20 -j TOS --set-tos 0x085.2 MARK 标记(配合 tc 做流量控制)
# 给 SSH 流量打标记 1
iptables -t mangle -A PREROUTING -p tcp --dport 22 -j MARK --set-mark 1
# 给 HTTP 流量打标记 2
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 2
# 然后用 tc 按标记做 QoS(tc 配置超出本文范围)5.3 TTL 修改
# 固定 TTL=64(防止 ISP 检测共享上网)
iptables -t mangle -A PREROUTING -j TTL --ttl-set 64
# 递增 TTL(绕过某些路由限制)
iptables -t mangle -A PREROUTING -j TTL --ttl-inc 1七、动作(Target)
| 动作 | 表 | 含义 |
|---|---|---|
ACCEPT | filter | 允许通过,停止当前链 |
DROP | filter | 丢弃,不回应 |
REJECT | filter | 拒绝,回 ICMP 错误 |
LOG | 全部 | 记录日志(不中断规则链,继续匹配下一条) |
SNAT | nat | 改源 IP |
DNAT | nat | 改目标 IP |
MASQUERADE | nat | 动态 SNAT |
REDIRECT | nat | 本地端口转发 |
MARK | mangle | 打标记 |
RETURN | 全部 | 退出当前链,回到调用链 |
QUEUE | 全部 | 交给用户态处理 |
LOG 动作:
# 记录被 DROP 的 SSH 尝试(限制速率防刷屏)
iptables -A INPUT -p tcp --dport 22 -m limit --limit 5/min --limit-burst 10 -j LOG --log-prefix "SSH-BLOCK: " --log-level 4
# 然后再 DROP
iptables -A INPUT -p tcp --dport 22 -j DROP注意 LOG 的顺序:LOG 要放在 DROP 前面,因为 LOG 不会中断规则链的匹配,下一条规则继续。
八、规则管理操作
7.1 增删改查
# ── 查 ──
# 列出 filter 表所有规则(带行号)
iptables -L -n -v --line-numbers
# 只看 INPUT 链
iptables -L INPUT -n -v --line-numbers
# 看指定表(nat)
iptables -t nat -L -n -v --line-numbers
# ── 增 ──
# 追加到链末尾
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 插入到链首部
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
# 插入到指定位置(行号 2)
iptables -I INPUT 2 -p tcp --dport 22 -j ACCEPT
# ── 删 ──
# 按行号删除
iptables -D INPUT 3
# 按规则内容删除(写完整匹配条件)
iptables -D INPUT -p tcp --dport 80 -j ACCEPT
# 清空整条链
iptables -F INPUT
# 清空所有链
iptables -F
# ── 改 ──
# 替换指定行
iptables -R INPUT 1 -p tcp --dport 22 -j ACCEPT
# ── 策略 ──
# 设置默认策略
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT7.2 规则顺序至关重要
iptables 是自上而下匹配,一旦匹配成功就执行动作,不再继续:
# ❌ 错误:先 DROP 再 ACCEPT,永远不会到 ACCEPT
iptables -A INPUT -s 10.0.0.0/8 -j DROP
iptables -A INPUT -s 10.0.0.1 -j ACCEPT # 10.0.0.1 已经被上面 DROP 了
# ✅ 正确:先放行再 DROP
iptables -A INPUT -s 10.0.0.1 -j ACCEPT # 先放行特定 IP
iptables -A INPUT -s 10.0.0.0/8 -j DROP # 再 DROP 整个网段九、持久化
8.1 RHEL/CentOS
# 保存当前规则到 /etc/sysconfig/iptables
service iptables save
# 或
iptables-save > /etc/sysconfig/iptables
# 开机自启
systemctl enable iptables
systemctl start iptables
# 从文件恢复
iptables-restore < /etc/sysconfig/iptables8.2 Ubuntu/Debian
Ubuntu 默认用 ufw,iptables 规则不自动持久化:
# 安装持久化包
apt install iptables-persistent -y
# 保存当前规则
netfilter-persistent save
# 或手动
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
# 恢复
netfilter-persistent reload8.3 通用方案(systemd service)
如果发行版没有自带持久化,自己写 systemd 服务:
# 保存
iptables-save > /etc/iptables/rules.v4
# 创建服务
tee /etc/systemd/system/iptables-restore.service << 'EOF'
[Unit]
Description=Restore iptables rules
Before=network-pre.target
[Service]
Type=oneshot
# 【兼容说明】用 sh -c + stdin 重定向,而不是把文件作为位置参数:
# iptables-restore [file] 的位置参数语法是 iptables 1.8.5+ 才支持;
# CentOS 7 / Debian 9 及更旧版本(iptables ≤1.4.x/1.6.x)只接受 stdin,必须用 < 重定向。
ExecStart=/bin/sh -c '/sbin/iptables-restore < /etc/iptables/rules.v4'
[Install]
WantedBy=multi-user.target
EOF
systemctl enable iptables-restore8.4 iptables-save / iptables-restore 格式
# 导出(可读格式)
iptables-save
# 输出:
# *filter
# :INPUT DROP [0:0]
# :FORWARD DROP [0:0]
# :OUTPUT ACCEPT [0:0]
# -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# -A INPUT -i lo -j ACCEPT
# -A INPUT -p tcp --dport 22 -j ACCEPT
# COMMIT
# *nat
# :PREROUTING ACCEPT [0:0]
# :POSTROUTING ACCEPT [0:0]
# -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# COMMITiptables-restore 是原子操作(要么全部成功要么全部回滚),比逐条 iptables -A 快得多,适合脚本批量恢复。
十、生产场景配置
场景 1:Web 服务器防火墙
#!/bin/bash
# web-server-firewall.sh
#
# 【关键】顺序:先 Policy=ACCEPT,再清空,再加规则,最后切回 DROP。避免清空 + DROP Policy 的自锁窗口。
# Policy 先切 ACCEPT
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# 清空
iptables -F
iptables -t nat -F
iptables -t mangle -F
# ---------- 规则区(Policy 仍为 ACCEPT,SSH 会话安全) ----------
# 1. 回环
iptables -A INPUT -i lo -j ACCEPT
# 2. 已建立连接
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 3. 丢弃无效包
iptables -A INPUT -m state --state INVALID -j DROP
# 4. SSH 限制(仅内网 + 防 CC)
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -m connlimit --connlimit-above 3 -j DROP
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
# 5. Web
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
# 6. ICMP 限速
iptables -A INPUT -p icmp -m limit --limit 5/s -j ACCEPT
iptables -A INPUT -p icmp -j DROP
# 7. 日志(记录被拒绝的包,限速防刷屏)
iptables -A INPUT -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "FW-DROP: " --log-level 4
# ---------- 所有规则就绪,Policy 最后切回 DROP ----------
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 持久化
iptables-save > /etc/sysconfig/iptables场景 2:网关路由器(共享上网)
#!/bin/bash
# gateway-router.sh
# 开启转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 持久化
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.d/99-ipforward.conf
sysctl -p
# LAN = eth1 (192.168.1.1), WAN = eth0 (公网)
LAN="eth1"
WAN="eth0"
LAN_NET="192.168.1.0/24"
# 【关键】Policy 先 ACCEPT → 清空 → 加规则 → 最后切 DROP
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# 清空
iptables -F
iptables -t nat -F
# ---------- filter 表规则 ----------
# 网关本身
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i $LAN -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
# 转发规则
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i $LAN -o $WAN -s $LAN_NET -j ACCEPT
iptables -A FORWARD -i $WAN -o $LAN -m state --state ESTABLISHED,RELATED -j ACCEPT
# NAT(MASQUERADE 动态公网 IP)
iptables -t nat -A POSTROUTING -s $LAN_NET -o $WAN -j MASQUERADE
# ---------- 规则就绪,Policy 切回 DROP ----------
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# 持久化
iptables-save > /etc/sysconfig/iptables场景 3:端口转发
# 外部访问公网 IP:8080 → 内网 192.168.1.100:80
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# 允许转发到内网
iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 80 -j ACCEPT
# 回包 SNAT(如果不做 MASQUERADE,回包源 IP 会是 192.168.1.100,外部看到的是内网 IP)
iptables -t nat -A POSTROUTING -p tcp -d 192.168.1.100 --dport 80 -j SNAT --to-source 192.168.1.1
# 或者简单点:全做 MASQUERADE
iptables -t nat -A POSTROUTING -d 192.168.1.100 -j MASQUERADE场景 4:Docker 适配
Docker 自己管理 iptables 规则,不要手动改 Docker 的链,而是加自己的规则到 DOCKER-USER 链:
# Docker 的链:
# DOCKER(nat 表的 DNAT 规则)
# DOCKER(filter 表的容器转发规则)
# DOCKER-ISOLATION(容器间隔离)
# DOCKER-USER(留给用户自定义,最优先)
# 只允许 192.168.1.0/24 访问 Docker 容器的 3306
iptables -I DOCKER-USER -p tcp --dport 3306 ! -s 192.168.1.0/24 -j DROP
# 阻止容器访问内网其他段
iptables -I DOCKER-USER -d 10.0.0.0/8 -j DROP
# 允许容器访问外网但不访问内网
iptables -I DOCKER-USER -d 192.168.0.0/16 -j DROP
iptables -I DOCKER-USER -d 10.0.0.0/8 -j DROP
iptables -I DOCKER-USER -d 172.16.0.0/12 -j DROP⚠️ 重要:在 /etc/docker/daemon.json 里设 "iptables": false 会破坏 Docker 网络,不要这样做。正确方式是用 DOCKER-USER 链。
场景 5:防 CC 攻击
# 限制每个 IP 到 80 端口最多 50 个并发连接
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j DROP
# 限制每秒新连接数(防 SYN Flood)
iptables -A INPUT -p tcp --dport 80 -m state --state NEW -m limit --limit 20/s --limit-burst 50 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j DROP
# SYN Flood 防护(配合 sysctl)
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
iptables -A INPUT -p tcp --syn -m limit --limit 10/s --limit-burst 20 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP场景 6:限速(QoS)
# mangle 表标记流量
iptables -t mangle -A PREROUTING -p tcp --dport 22 -j MARK --set-mark 1 # SSH 高优先
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 2 # HTTP 中优先
iptables -t mangle -A PREROUTING -p tcp --dport 6881 -j MARK --set-mark 3 # BT 低优先
# 配合 tc 做带宽分配(tc 配置略)
# tc qdisc add dev eth0 root handle 1: htb
# tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit
# tc class add dev eth0 parent 1:1 classid 1:10 htb rate 40mbit ceil 100mbit # SSH
# tc class add dev eth0 parent 1:1 classid 1:20 htb rate 40mbit ceil 100mbit # HTTP
# tc class add dev eth0 parent 1:1 classid 1:30 htb rate 10mbit ceil 20mbit # BT
# tc filter add dev eth0 parent 1:0 protocol ip handle 1 fw flowid 1:10
# tc filter add dev eth0 parent 1:0 protocol ip handle 2 fw flowid 1:20
# tc filter add dev eth0 parent 1:0 protocol ip handle 3 fw flowid 1:30十一、常用命令速查
| 命令 | 作用 |
|---|---|
iptables -L -n -v | 查看规则(不解析 + 详细) |
iptables -L INPUT -n --line-numbers | 看 INPUT 链带行号 |
iptables -t nat -L -n -v | 看 nat 表 |
iptables -t mangle -L -n | 看 mangle 表 |
iptables -S | 以命令格式输出规则 |
iptables -F | 清空所有 filter 规则 |
iptables -t nat -F | 清空 nat 表 |
iptables -Z | 清空计数器 |
iptables -A INPUT ... | 追加规则 |
iptables -I INPUT 2 ... | 插入到第 2 行 |
iptables -D INPUT 3 | 删除第 3 行 |
iptables -R INPUT 1 ... | 替换第 1 行 |
iptables -P INPUT DROP | 设默认策略 |
iptables-save > file | 导出规则 |
iptables-restore < file | 恢复规则 |
iptables -L -n -v --line-numbers | grep DROP | 搜索含 DROP 的规则 |
十二、firewalld 与 iptables 的关系
11.1 firewalld 底层
firewalld 在 RHEL 7+ 默认安装,它不是 iptables 的替代品,而是上层封装:
firewalld 命令 → firewalld 守护进程 → 翻译成 nftables/iptables 规则 → 内核 netfilter11.2 共存问题
# 如果用 iptables 手动管理,建议禁用 firewalld
systemctl stop firewalld
systemctl disable firewalld
# 或者用 firewalld 的 direct rules 直接写 iptables 规则
firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 22 -j ACCEPT11.3 firewalld 到 iptables 的映射
| firewalld | iptables 等价 |
|---|---|
firewall-cmd --add-service=http | iptables -A INPUT -p tcp --dport 80 -j ACCEPT |
firewall-cmd --add-port=8080/tcp | iptables -A INPUT -p tcp --dport 8080 -j ACCEPT |
firewall-cmd --add-masquerade | iptables -t nat -A POSTROUTING -j MASQUERADE |
firewall-cmd --add-forward-port=80:tcp:8080 | iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080 |
十三、排障
排障 1:规则不生效
# 1. 检查规则顺序(被前面的 DROP 拦了?)
iptables -L INPUT -n -v --line-numbers
# 2. 检查是否在正确的表
# DNAT/SNAT 在 nat 表,过滤在 filter 表
iptables -t nat -L -n -v
# 3. 检查是否在正确的链
# 入站过滤在 INPUT,转发过滤在 FORWARD
iptables -L FORWARD -n -v
# 4. 检查网卡方向
# 入站用 -i,出站用 -o
# FORWARD 既有 -i 又有 -o排障 2:SSH 被自己锁在外面
# 如果在云服务器上,用控制台/VNC 进入
# 临时清空
iptables -F
iptables -P INPUT ACCEPT
# 或者只清 INPUT
iptables -F INPUT
iptables -P INPUT ACCEPT
# 然后重新配,先加放行 SSH 再设 DROP预防方法:配 iptables 时加定时自动恢复:
# 先设一个 5 分钟后自动清空的任务
(crontab -l; echo "*/5 * * * * /sbin/iptables -F && /sbin/iptables -P INPUT ACCEPT") | crontab -
# 确认没问题后删掉这个 cron
crontab -l | grep -v iptables | crontab -排障 3:nat 不生效(端口转发不通)
# 1. 检查转发是否开启
cat /proc/sys/net/ipv4/ip_forward
# 0 → 没开
echo 1 > /proc/sys/net/ipv4/ip_forward
# 2. 检查 DNAT 规则
iptables -t nat -L PREROUTING -n -v
# 看包计数 pkts 是否在涨
# 3. 检查 FORWARD 链是否放行
iptables -L FORWARD -n -v
# DNAT 后包走 FORWARD 链
# 4. 检查回包路由
# 内网机器的默认网关是不是指向本机?
# 如果不是,回包不走网关,SNAT 不生效排障 4:Docker 规则冲突
# Docker 重启会重建所有 iptables 规则
# 自定义规则放在 DOCKER-USER 链才不会被覆盖
# 检查 DOCKER-USER 链
iptables -L DOCKER-USER -n -v
# 如果规则丢失 → Docker 重启了
# 解决:把规则写到 /etc/iptables/rules.v4 的 DOCKER-USER 链
# 或用 Docker 的 startup 脚本排障 5:连接追踪表满了
# 查看当前连接数
cat /proc/sys/net/netfilter/nf_conntrack_count
# 查看上限
cat /proc/sys/net/netfilter/nf_conntrack_max
# 日志报 "nf_conntrack: table full, dropping packet"
# 解决:调大上限
echo 262144 > /proc/sys/net/netfilter/nf_conntrack_max
# 持久化
echo 'net.netfilter.nf_conntrack_max = 262144' >> /etc/sysctl.d/99-conntrack.conf
sysctl -p
# 对应内存约 256MB(每个连接约 1KB)排障 6:日志找不到
# iptables 日志走内核日志
# 查看
dmesg | grep "FW-DROP"
# 或
journalctl -k | grep "FW-DROP"
# 如果用 rsyslog 单独存
echo ':msg, contains, "FW-DROP" /var/log/iptables.log' >> /etc/rsyslog.d/iptables.conf
systemctl restart rsyslog排障 7:性能下降
# 1. 规则太多 → 匹配慢
# 优化:高频规则放前面
iptables -L INPUT -n -v --line-numbers | sort -k2 -rn | head
# 按 pkts 排序,包数最多的应该放最前面
# 2. state 模块开销大 → 对大流量用 raw 表 NOTRACK
iptables -t raw -A PREROUTING -p tcp --dport 80 -j NOTRACK
# 不追踪 80 端口的连接,省 conntrack 开销
# 3. 使用 hashlimit 替代 limit
# hashlimit 按源 IP 分别限速,limit 是全局的
iptables -A INPUT -p tcp --dport 80 -m hashlimit --hashlimit-name http \
--hashlimit 50/s --hashlimit-burst 100 --hashlimit-mode srcip -j ACCEPT排障 8:MASQUERADE 后内网不通
# 1. 确认 MASQUERADE 规则
iptables -t nat -L POSTROUTING -n -v
# 2. 确认 FORWARD 放行
iptables -A FORWARD -i $LAN -o $WAN -j ACCEPT
iptables -A FORWARD -i $WAN -o $LAN -m state --state ESTABLISHED,RELATED -j ACCEPT
# 3. 确认内网机器的默认网关指向本机
# 内网机器:route add default gw 192.168.1.1
# 4. 确认 DNS 能解析
# 如果 DNS 在外网,MASQUERADE 要包含 DNS 端口排障 9:重启后规则丢失
# 1. 检查持久化
# RHEL: /etc/sysconfig/iptables 存在?
# Ubuntu: /etc/iptables/rules.v4 存在?
# 2. 检查开机服务
systemctl is-enabled iptables # RHEL
systemctl is-enabled netfilter-persistent # Ubuntu
# 3. 手动恢复测试
iptables-restore < /etc/sysconfig/iptables
# 报错 → 规则文件格式有问题排障 10:REJECT vs DROP 哪个用
| 动作 | 对端行为 | 丢包类型 | 适用 |
|---|---|---|---|
DROP | 超时(等很久) | 静默丢弃 | 防扫描、隐藏服务 |
REJECT | 立即收到 ICMP 拒绝 | 明确拒绝 | 内网、调试、明确告知 |
# 外网防扫描 → DROP
iptables -A INPUT -s 外网 -j DROP
# 内网调试 → REJECT
iptables -A INPUT -s 内网 -j REJECT --reject-with icmp-port-unreachable排障 11:IPv6 用 ip6tables
# iptables 只管 IPv4,IPv6 用 ip6tables
ip6tables -L -n -v
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -P INPUT DROP
# 持久化
ip6tables-save > /etc/iptables/rules.v6排障 12:conntrack 看连接状态
# 查看当前追踪的连接
cat /proc/net/nf_conntrack | head -20
# 统计各状态连接数
cat /proc/net/nf_conntrack | awk '{print $4}' | sort | uniq -c
# 查看某 IP 的连接
conntrack -L -s 192.168.1.100 # 需安装 conntrack-tools十四、最佳实践
13.1 规则设计原则
- 白名单模式:默认 DROP,只放行明确需要的
- 先放行再 DROP:规则从上到下匹配,放行规则在 DROP 前面
- ESTABLISHED 优先:第一条永远放行已建立的连接
- lo 永远放行:本机回环不要拦
- INVALID 必丢:无法识别的包丢弃
- 日志限速:LOG 动作配 limit 防刷屏
- Docker 用 DOCKER-USER:不碰 Docker 自己的链
- 持久化:改完
iptables-save,别丢
13.2 安全加固 Checklist
# 内核参数加固
cat >> /etc/sysctl.d/99-security.conf << 'EOF'
# SYN Flood 防护
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_synack_retries = 2
# IP 欺骗防护
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# 禁源路由
net.ipv4.conf.all.accept_source_route = 0
# 禁 ICMP 重定向
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# 连接追踪
net.netfilter.nf_conntrack_max = 262144
EOF
sysctl -p /etc/sysctl.d/99-security.conf13.3 推荐初始配置模板
#!/bin/bash
# /usr/local/sbin/firewall-init.sh
#
# 【关键】顺序:先把所有 Policy 临时切为 ACCEPT,再清空/重建,最后再设回 DROP。
# 如果先清空再设 DROP(原错误写法),清空后到 SSH 规则被重新写入之间窗口,SSH 会话会立即被断开。
# Step 0: Policy 先切 ACCEPT,防止清空瞬间锁死远程连接
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# Step 1: 清空旧规则与用户自定义链
iptables -F
iptables -X
iptables -t nat -F
iptables -t mangle -F
# Step 2: 新规则加载(此时 Policy 仍为 ACCEPT,SSH 会话安全)
# 回环
iptables -A INPUT -i lo -j ACCEPT
# 已建立连接(conntrack,必须早于业务规则)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 无效包
iptables -A INPUT -m state --state INVALID -j DROP
# SSH(先限制并发,再放行;请按需把 192.168.1.0/24 换成自己的管理网段)
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -m connlimit --connlimit-above 3 -j DROP
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT
# Web
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
# ICMP 限速
iptables -A INPUT -p icmp -m limit --limit 5/s -j ACCEPT
iptables -A INPUT -p icmp -j DROP
# 日志(只打未匹配的,限频避免刷屏)
iptables -A INPUT -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "FW-DROP: "
# Step 3: 所有规则就绪,Policy 最后切回 DROP(避免锁死)
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Step 4: 持久化
iptables-save > /etc/sysconfig/iptables 2>/dev/null || iptables-save > /etc/iptables/rules.v4