2_iptables规则实战

1. 命令语法总览

一条 iptables 命令由四个部分组成:操作哪张表的哪条链、做什么操作、匹配什么条件、执行什么动作。

1
iptables [-t 表名] 操作命令 [链名] [匹配条件] [-j 动作]

示例拆解:

1
2
3
iptables -t filter -I INPUT -s 192.168.1.1 -p tcp --dport 22 -j DROP
# ~~~~~~~~ ~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~
# 指定表 操作+链 匹配条件 动作

1.1 操作参数

决定对规则做什么操作(增、删、改、查、清空)。

参数全称作用示例
-IInsert插入规则到链头部(或指定位置)-I INPUT-I INPUT 3
-AAppend追加规则到链尾部-A INPUT
-DDelete删除规则(按编号或条件)-D INPUT 3-D INPUT -s 1.1.1.1 -j DROP
-RReplace替换指定编号的规则-R INPUT 3 -s 1.1.1.1 -j ACCEPT
-LList列出规则-L-L INPUT
-FFlush清空链中的所有规则-F-F INPUT
-X删除自定义链-X MY_CHAIN
-PPolicy设置链的默认策略-P INPUT DROP
-NNew创建自定义链-N MY_CHAIN

1.2 表和链参数

参数作用说明
-t指定操作的表filter(默认,可省略)、natmangleraw
链名跟在操作参数后面INPUT、OUTPUT、FORWARD、PREROUTING、POSTROUTING

1.3 匹配条件参数

决定规则 “ 抓什么包 “。多个条件同时使用时是 “ 与 “ 关系,所有条件都满足才匹配。

参数作用示例
-s源地址-s 192.168.1.0/24-s 1.1.1.1,2.2.2.2
-d目标地址-d 10.0.0.1
-p协议-p tcp-p udp-p icmp
-i入口网卡-i eth0
-o出口网卡-o eth1
-m加载扩展模块-m tcp-m multiport-m state
--dport目标端口(需 -m 模块)--dport 22--dport 80:88
--sport源端口(需 -m 模块)--sport 1024:65535
!取反! -s 1.1.1.1! --dport 22

1.4 动作参数

参数作用说明
-j ACCEPT放行允许数据包通过
-j DROP丢弃静默丢弃,不返回任何响应
-j REJECT拒绝丢弃并返回错误信息(如 ICMP port unreachable)
-j LOG记录日志记录后继续匹配后续规则,不终止
-j SNAT源地址转换用于 nat 表 POSTROUTING 链
-j DNAT目标地址转换用于 nat 表 PREROUTING 链
-j MASQUERADE动态源地址转换类似 SNAT,适用于动态 IP

1.5 显示辅助参数

参数作用
-n不做 DNS 反解,IP 直接显示为数字
-v显示详细信息(包计数、字节数等)
--line-numbers显示规则编号

下面用一张图展示命令的组成结构:

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
    CMD["iptables"] --> TBL["-t 表名"]
    CMD --> OP["操作参数"]
    OP --> CHAIN["链名"]
    CHAIN --> MATCH["匹配条件"]
    MATCH --> ACTION["-j 动作"]

    TBL -.- T1["filter"]
    TBL -.- T2["nat"]
    TBL -.- T3["mangle / raw"]

    OP -.- O1["-I 插入"]
    OP -.- O2["-A 追加"]
    OP -.- O3["-D 删除"]
    OP -.- O4["-L 查看"]

    MATCH -.- M1["-s 源地址"]
    MATCH -.- M2["-d 目标地址"]
    MATCH -.- M3["-p 协议"]
    MATCH -.- M4["-m 扩展模块"]

    ACTION -.- A1["ACCEPT"]
    ACTION -.- A2["DROP"]
    ACTION -.- A3["REJECT"]

    classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef success fill:#10B981,stroke:#059669,color:#fff
    classDef warning fill:#F59E0B,stroke:#D97706,color:#fff
    classDef info fill:#0EA5E9,stroke:#0284C7,color:#fff

    class CMD primary
    class TBL,OP,CHAIN,MATCH,ACTION info
    class T1,T2,T3,O1,O2,O3,O4,M1,M2,M3,M4,A1,A2,A3 success

参数语法搞清楚后,接下来通过实际操作逐步掌握它们的用法。

2. 查看规则

动手之前,先学会看规则。

2.1 基础查看

1
2
3
4
5
6
7
# 查看 filter 表所有链的规则(filter 是默认表,可省略 -t filter)
iptables -L

# 查看指定表的规则
iptables -t nat -L
iptables -t mangle -L
iptables -t raw -L

-L 的输出信息太少,实际工作中几乎不用裸 -L

2.2 详细查看

1
iptables --line-numbers -nvL INPUT

各选项含义:

选项作用
--line-numbers(可简写 --line显示规则编号,删除时需要
-n不做 DNS 反解,IP 显示为数字,速度更快
-v显示详细信息,包括计数器
-L INPUT只看 INPUT 链(也可以换成 FORWARD、OUTPUT 等)

示例输出:

1
2
3
4
5
Chain INPUT (policy ACCEPT 14M packets, 2062M bytes)
num pkts bytes target prot opt in out source destination
1 2697K 114M KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0
2 2697K 114M KUBE-EXTERNAL all -- * * 0.0.0.0/0 0.0.0.0/0
3 906M 477G KUBE-FIREWALL all -- * * 0.0.0.0/0 0.0.0.0/0

2.3 输出字段解读

链头部信息:Chain INPUT (policy ACCEPT 14M packets, 2062M bytes)

字段含义
policy ACCEPT默认策略为 ACCEPT:未被任何规则匹配到的包,默认放行
14M packets默认策略已匹配到 1400 万个包
2062M bytes这些包的总大小为 2062MB

规则字段:

字段含义说明
num规则编号删除规则时使用
pkts此规则匹配到的包数量判断规则是否生效
bytes匹配到的包的总大小同上
target动作ACCEPT / DROP / REJECT / 自定义链名
prot协议tcp / udp / icmp / all
opt选项一般为 --
in入口网卡* 表示任意
out出口网卡* 表示任意
source源地址0.0.0.0/0 表示任意
destination目标地址0.0.0.0/0 表示任意

pktsbytes 列判断规则是否生效。如果加了规则但计数器一直是 0,说明没有报文命中——要么匹配条件写错,要么规则被前面的规则抢先匹配。

查看规则掌握后,接下来学习规则的增删改操作。

3. 增加规则

3.1 第一条规则

先清空 filter 表的 INPUT 链,从零开始(生产环境慎用):

1
iptables -F INPUT

添加一条规则,禁止来自 123.117.179.97 的所有流量:

1
iptables -t filter -I INPUT -s 123.117.179.97 -j DROP

逐段拆解:

部分含义
-t filter操作 filter 表(可省略,默认就是 filter)
-I INPUT在 INPUT 链头部插入规则(Insert)
-s 123.117.179.97匹配条件:源地址为该 IP
-j DROP动作:丢弃

验证:

1
2
iptables -nvL INPUT
# 看 pkts 和 bytes 是否在增长,说明规则正在生效

3.2 -I 和 -A 的区别

1
2
3
4
5
6
7
8
# -I:插入到链的头部(第 1 条),最先被匹配
iptables -I INPUT -s 1.1.1.1 -j DROP

# -A:追加到链的尾部(最后一条),最后才被匹配
iptables -A INPUT -s 1.1.1.2 -j DROP

# -I + 编号:插入到指定位置
iptables -I INPUT 3 -s 1.1.1.3 -j DROP

3.3 规则顺序至关重要

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart TD
    PKT(["数据包进入"]) --> R1{"规则1: 张三 -> 拒绝"}
    R1 -->|"匹配"| DROP1["DROP: 拒绝张三"]
    R1 -->|"不匹配"| R2{"规则2: 所有人 -> 放行"}
    R2 -->|"匹配"| ACC["ACCEPT: 放行"]
    R2 -->|"不匹配"| R3{"规则3: 李四 -> 拒绝"}
    R3 -->|"匹配"| DROP2["DROP: 拒绝李四"]
    R3 -->|"不匹配"| DEF["默认策略"]

    classDef primary fill:#3B82F6,stroke:#2563EB,color:#fff
    classDef success fill:#10B981,stroke:#059669,color:#fff
    classDef danger fill:#EF4444,stroke:#DC2626,color:#fff
    classDef decision fill:#F59E0B,stroke:#D97706,color:#fff

    class PKT primary
    class ACC success
    class DROP1,DROP2 danger
    class R1,R2,R3 decision
    class DEF primary

这是 iptables 最容易踩的坑。规则从上往下逐条匹配,命中第一条就执行动作并停止后续匹配。

类比保安查名单:

1
2
3
规则1:张三 → 拒绝
规则2:所有人 → 放行
规则3:李四 → 拒绝 ← 永远不会被执行!

张三被规则 1 拦住。但规则 3 想拦李四?没用——李四在规则 2 就被放行了,根本走不到规则 3。

实际例子:

1
2
3
4
5
6
7
# 错误:先放行所有 → 再拒绝某个 IP → 拒绝规则永远不会生效
iptables -A INPUT -j ACCEPT
iptables -A INPUT -s 1.1.1.1 -j DROP # 不会生效

# 正确:先拒绝具体的 → 再放行其他
iptables -I INPUT -s 1.1.1.1 -j DROP
iptables -A INPUT -j ACCEPT

规则的增加和顺序搞清楚后,还需要掌握规则的删除和修改。

4. 删除规则

两种方式:

1
2
3
4
5
6
7
8
9
10
11
12
# 方式一:按编号删除(先查编号)
iptables --line -nvL INPUT
iptables -D INPUT 3 # 删除第 3 条

# 方式二:按完整匹配条件删除
iptables -D INPUT -s 1.1.1.2 -j DROP

# 清空某条链的所有规则
iptables -t filter -F INPUT

# 清空某张表的所有规则
iptables -t filter -F

⚠️ 如果默认策略是 DROP,执行 -F 清空规则后,所有流量都会被拒绝——包括你的 SSH 连接。你会把自己锁在外面。

5. 修改规则

建议采用 “ 先删后加 “ 的方式,比 -R 更安全:

1
2
3
4
5
6
# 不推荐:用 -R 修改(必须记住编号和原始匹配条件)
iptables -R INPUT 3 -s 192.168.1.146 -j ACCEPT

# 推荐:先删后加
iptables -D INPUT -s 192.168.1.146 -j DROP
iptables -I INPUT -s 192.168.1.146 -j ACCEPT

修改默认策略(不是修改规则,是修改链的兜底动作):

1
2
3
4
5
# 将 FORWARD 链的默认策略改为 ACCEPT
iptables -P FORWARD ACCEPT

# 将 FORWARD 链的默认策略改为 DROP
iptables -P FORWARD DROP

增删改查都覆盖后,接下来深入匹配条件——它决定了规则 “ 抓什么包 “。

6. 匹配条件详解

匹配条件决定了规则筛选数据包的维度。第 1 章已列出所有参数速查,这里展开讲解每个条件的细节和注意事项。

6.1 源地址匹配 -s

1
2
3
4
5
6
7
8
9
10
11
# 单个 IP
iptables -I INPUT -s 1.1.1.2 -j DROP

# 多个 IP(逗号分隔,不能有空格)
iptables -I INPUT -s 1.1.1.3,1.1.1.4 -j DROP

# 网段
iptables -I INPUT -s 10.0.0.0/24 -j DROP

# 取反:源地址不是 1.1.1.6 的包才匹配
iptables -I INPUT ! -s 1.1.1.6 -j ACCEPT

取反的常见误区

! -s 1.1.1.6 -j ACCEPT 的含义:

  • ✅ 正确理解:源 IP 不是 1.1.1.6 → 放行
  • ❌ 错误理解:源 IP 是 1.1.1.6 → 拒绝

两者完全不同:源 IP 是 1.1.1.6 的包不匹配这条规则,它会继续往下走匹配后续规则,而不是被拒绝。

6.2 目标地址匹配 -d

1
2
# 只丢弃从 1.1.1.7 发往本机 IP 1.1.1.8 的报文
iptables -I INPUT -s 1.1.1.7 -d 1.1.1.8 -j DROP
  • 不指定 -s:源地址默认 0.0.0.0/0(任意)
  • 不指定 -d:目标地址默认 0.0.0.0/0(任意)
  • -d 同样支持多 IP、网段、取反

多条件组合是 “ 与 “ 关系:-s 1.1.1.7 -d 1.1.1.8 表示源地址为 1.1.1.7 且目标地址为 1.1.1.8 的包才匹配。

6.3 协议匹配 -p

1
2
3
4
5
# 拒绝来自 1.1.1.2 的 TCP 请求
iptables -I INPUT -s 1.1.1.2 -p tcp -j DROP

# 禁止别人 ping 本机(ICMP 协议)
iptables -I INPUT -p icmp -j DROP

支持的协议类型:tcp, udp, icmp, udplite, esp, ah, sctp(CentOS 7 还支持 icmpv6, mh)。

不指定 -p 时默认匹配所有协议,等同于 -p all

6.4 网卡匹配 -i / -o

当服务器有多块网卡时,可以按网卡过滤:

1
2
3
4
5
# 丢弃从 eth0 流入的 ICMP 报文
iptables -I INPUT -i eth0 -p icmp -j DROP

# 丢弃从 eth1 流出的 ICMP 报文
iptables -I OUTPUT -o eth1 -p icmp -j DROP

使用限制:

选项含义可用链不可用链
-i报文从哪块网卡流入PREROUTING、INPUT、FORWARDOUTPUT、POSTROUTING
-o报文从哪块网卡流出FORWARD、OUTPUT、POSTROUTINGPREROUTING、INPUT

数据包还没进来时,谈不上 “ 从哪个网卡出去 “;已经出去的包,也谈不上 “ 从哪个网卡进来 “。

6.5 扩展匹配模块

基本匹配条件之外,iptables 通过 -m 加载扩展模块来支持更丰富的匹配能力。

Tcp 扩展模块

1
2
# 拒绝来自 1.1.1.2 的、目标端口为 22(SSH)的 TCP 请求
iptables -I INPUT -s 1.1.1.2 -p tcp -m tcp --dport 22 -j DROP

拆解 -p tcp -m tcp --dport 22

部分作用
-p tcp匹配 TCP 协议的报文
-m tcp加载 tcp 扩展模块
--dport 22tcp 模块提供的匹配条件:目标端口为 22

-p tcp-m tcp 不冲突:-p 过滤协议,-m 加载模块。只是碰巧模块也叫 tcp。

端口范围和源端口:

1
2
3
4
5
6
7
8
# 目标端口 22~25
iptables -I INPUT -p tcp -m tcp --dport 22:25 -j DROP

# 源端口 80
iptables -I INPUT -p tcp -m tcp --sport 80 -j ACCEPT

# 取反:目标端口不是 22 的放行
iptables -I INPUT -p tcp -m tcp ! --dport 22 -j ACCEPT

Multiport 扩展模块

tcp 模块只能指定连续端口范围。指定多个离散端口需要 multiport 模块:

1
2
3
4
5
# 同时拒绝 22、80、443 端口
iptables -I INPUT -s 1.1.1.2 -p tcp -m multiport --dports 22,80,443 -j DROP

# 混合使用离散端口和范围
iptables -I INPUT -p tcp -m multiport --dports 22,80:88,443 -j DROP

State 扩展模块

state 模块基于 netfilter 的连接跟踪(conntrack)机制,按连接状态过滤数据包。注意:这里的 “ 状态 “ 不是 TCP 协议的状态(SYN_SENT、TIME_WAIT 等),而是 conntrack 自己维护的一套状态,适用于 TCP、UDP、ICMP 等所有协议。

状态含义典型场景
NEW连接的第一个包客户端发来的 SYN 包、第一个 UDP 包、第一个 ICMP echo-request
ESTABLISHED已建立连接的后续包(双向都算)TCP 三次握手后的数据包、UDP 收到过回包后的后续包
RELATED与已有连接相关的新连接FTP 数据通道(由控制通道协商产生)、ICMP 错误回复(如 port unreachable)
INVALID无法识别或不属于任何已知连接的包校验和错误、无法关联到任何 conntrack 条目的包
1
2
3
4
5
6
7
8
# 放行已建立连接和相关连接的回包(几乎所有防火墙配置都需要这条)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# 只允许新连接访问 80 端口
iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT

# 丢弃无法识别的包
iptables -A INPUT -m state --state INVALID -j DROP

为什么 ESTABLISHED,RELATED 这条规则几乎必加?服务器主动发起的请求(如 curl 外部 API、DNS 查询、yum 更新),其响应包的状态是 ESTABLISHED。不加这条规则,服务器自身的对外请求也会被拦截。

各匹配条件总览:

分类参数作用示例值
基本匹配-s源地址单 IP、多 IP、网段、取反
基本匹配-d目标地址同上
基本匹配-p协议tcp、udp、icmp
基本匹配-i / -o入口/出口网卡eth0、eth1
扩展匹配-m tcptcp 模块--dport 22--sport 80--dport 22:25
扩展匹配-m multiport多端口模块--dports 22,80,443--dports 22,80:88,443
扩展匹配-m state连接状态模块--state ESTABLISHED,RELATED,NEW

匹配条件掌握后,看看如何将它们组织成完整的防护策略。

7. 黑白名单机制

两种策略的核心区别在于默认策略和规则动作的方向相反:黑名单默认放行、只拦已知威胁;白名单默认拒绝、只放明确允许的流量。

7.1 黑名单模式

  • 默认策略:ACCEPT(放行一切)
  • 规则动作:DROP / REJECT(只拦特定目标)
  • 适合场景:大部分流量正常,只需拦截少量已知恶意 IP

类比:开放式公园,门口贴了几张通缉犯照片,只拦这些人,其他人自由进出。

7.2 白名单模式

  • 默认策略:DROP(拒绝一切)
  • 规则动作:ACCEPT(只放行明确允许的)
  • 适合场景:安全要求高,只开放必要的端口和 IP

类比:私人会所,门口有一份邀请名单,名单上的才能进,其他人一律拒绝。

7.3 白名单的安全实践

⚠️ 不要直接用 -P DROP 设置默认策略!

原因:执行 iptables -F 清空规则后(比如调试时手滑),默认策略 DROP 仍然生效,所有流量都被拒绝——包括 SSH,你会被锁在服务器外面。

安全做法:保持默认策略为 ACCEPT,在链的最后一条加上 “ 拒绝所有 “ 作为兜底:

1
2
3
4
5
6
7
8
9
10
# 默认策略保持 ACCEPT(安全网)
iptables -P INPUT ACCEPT

# 先放行需要的
iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# 最后兜底拒绝所有
iptables -A INPUT -j DROP

这样即使 iptables -F 清空规则,默认策略 ACCEPT 会保证 SSH 不断。

策略确定后,还要解决一个实际问题:规则怎么保存?

8. 规则持久化

iptables 规则存储在内核内存中,重启即丢失,必须手动保存。

8.1 CentOS / RHEL

1
2
3
4
5
6
7
8
9
# CentOS 6
service iptables save
# 规则保存到 /etc/sysconfig/iptables

# CentOS 7(默认用 firewalld,需先切换)
yum install -y iptables-services
systemctl disable firewalld
systemctl enable iptables
service iptables save

导出/导入方式:

1
2
3
4
5
# 导出当前规则到文件
iptables-save > /etc/sysconfig/iptables

# 从文件恢复规则
iptables-restore < /etc/sysconfig/iptables

8.2 Ubuntu / Debian

1
2
3
4
5
6
7
8
9
10
11
sudo apt-get install iptables-persistent

# 保存
sudo netfilter-persistent save

# 重新加载
sudo netfilter-persistent reload

# 规则保存在:
# /etc/iptables/rules.v4(IPv4)
# /etc/iptables/rules.v6(IPv6)

所有基础操作都准备就绪,下面用一个完整案例把它们串起来。

9. 实战:Web 服务器防火墙配置

假设你有一台全新的云服务器,运行 Nginx(80/443)和 SSH(22),需要配置防火墙。

需求:

  • 允许 SSH(端口 22)
  • 允许 HTTP(端口 80)和 HTTPS(端口 443)
  • 允许已建立连接的回包(如服务器主动请求外部 API 的响应)
  • 允许本机回环通信(localhost)
  • 拒绝其他一切入站流量
  • 不限制出站流量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash
# ============================================
# Web 服务器防火墙配置脚本
# ============================================

# 1. 清空现有规则(先确认默认策略是 ACCEPT)
iptables -P INPUT ACCEPT # 先确保默认放行,防止被锁
iptables -F # 清空所有规则
iptables -X # 删除所有自定义链

# 2. 允许回环接口(本机内部通信,很多程序依赖它)
iptables -A INPUT -i lo -j ACCEPT

# 3. 允许已建立连接和相关连接的回包
# 比如服务器 curl 外部 API,响应包的状态是 ESTABLISHED
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# 4. 允许 SSH(建议限制来源 IP,此处为简化未限制)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 5. 允许 HTTP 和 HTTPS
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT

# 6. 允许 ping(可选,方便排障)
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# 7. 兜底:拒绝所有其他入站流量(用 REJECT 返回拒绝信息,日志更友好)
iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable

# 8. 保存规则
# CentOS: service iptables save
# Ubuntu: netfilter-persistent save

echo "防火墙配置完成,当前规则:"
iptables --line -nvL INPUT

配置后验证:

1
2
3
4
5
6
7
8
9
10
11
# 查看规则和计数器
iptables --line -nvL INPUT

# 测试 SSH 是否正常(从另一台机器)
ssh root@your-server-ip

# 测试 HTTP 是否正常
curl http://your-server-ip

# 测试其他端口是否被拒绝
telnet your-server-ip 3306 # 应该被拒绝

10. 命令速查表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ========== 查看 ==========
iptables -L # 查看 filter 表
iptables -t nat -L # 查看 nat 表
iptables --line -nvL INPUT # 详细查看 INPUT 链(推荐)

# ========== 增加 ==========
iptables -I INPUT -s IP -j DROP # 插入到头部
iptables -A INPUT -s IP -j DROP # 追加到尾部
iptables -I INPUT 3 -s IP -j DROP # 插入到第 3 条

# ========== 删除 ==========
iptables -D INPUT 3 # 按编号删除
iptables -D INPUT -s IP -j DROP # 按条件删除
iptables -F INPUT # 清空 INPUT 链
iptables -F # 清空 filter 表所有链

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

# ========== 保存 ==========
service iptables save # CentOS
netfilter-persistent save # Ubuntu
iptables-save > /path/to/file # 导出到文件
iptables-restore < /path/to/file # 从文件恢复

下一篇:iptables 进阶与连接跟踪 —— conntrack 连接跟踪原理,state 扩展、常用扩展模块、TCP 标志位匹配和自定义链管理。