⚠️ 提示:本站 AI 助手使用博主个人 API Token,没事别特么瞎用。
运维 · · 约 56 分钟阅读

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最简语法,适合个人机器
nftablesiptables 继任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)

表决定了规则什么时候匹配和能做什么动作:

表优先级功能支持的链
raw1(最高)在连接追踪之前处理,可标记”不追踪”PREROUTING, OUTPUT
mangle2修改数据包的 TOS/TTL/MARK 等全部 5 链
nat3地址转换(SNAT/DNAT/MASQUERADE)PREROUTING, OUTPUT, POSTROUTING
filter4(最低)过滤(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 ACCEPT

3.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 ACCEPT

3.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 DROP

3.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 DROP

3.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 DROP

3.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 DROP

3.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 DROP

3.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.100IP 范围
网卡-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 MASQUERADE

SNAT 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:22

4.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 8080

4.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 0x08

5.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)

动作表含义
ACCEPTfilter允许通过,停止当前链
DROPfilter丢弃,不回应
REJECTfilter拒绝,回 ICMP 错误
LOG全部记录日志(不中断规则链,继续匹配下一条)
SNATnat改源 IP
DNATnat改目标 IP
MASQUERADEnat动态 SNAT
REDIRECTnat本地端口转发
MARKmangle打标记
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 ACCEPT

7.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/iptables

8.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 reload

8.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-restore

8.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
# COMMIT

iptables-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 规则 → 内核 netfilter

11.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 ACCEPT

11.3 firewalld 到 iptables 的映射

firewalldiptables 等价
firewall-cmd --add-service=httpiptables -A INPUT -p tcp --dport 80 -j ACCEPT
firewall-cmd --add-port=8080/tcpiptables -A INPUT -p tcp --dport 8080 -j ACCEPT
firewall-cmd --add-masqueradeiptables -t nat -A POSTROUTING -j MASQUERADE
firewall-cmd --add-forward-port=80:tcp:8080iptables -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 规则设计原则

  1. 白名单模式:默认 DROP,只放行明确需要的
  2. 先放行再 DROP:规则从上到下匹配,放行规则在 DROP 前面
  3. ESTABLISHED 优先:第一条永远放行已建立的连接
  4. lo 永远放行:本机回环不要拦
  5. INVALID 必丢:无法识别的包丢弃
  6. 日志限速:LOG 动作配 limit 防刷屏
  7. Docker 用 DOCKER-USER:不碰 Docker 自己的链
  8. 持久化:改完 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.conf

13.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
问问 AI