2_iptables规则实战

1. 查看规则

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

1.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

1.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

1.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,说明没有报文命中——要么匹配条件写错,要么规则被前面的规则抢先匹配。

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

2. 增加规则

2.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 是否在增长,说明规则正在生效

2.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

2.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

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

3. 删除规则

两种方式:

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 连接。你会把自己锁在外面。

4. 修改规则

建议采用 “ 先删后加 “ 的方式,比 -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

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

5. 匹配条件详解

匹配条件决定了规则筛选数据包的维度。

5.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 的包不匹配这条规则,它会继续往下走匹配后续规则,而不是被拒绝。

5.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 的包才匹配。

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

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

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

5.5 端口匹配(扩展匹配)

端口匹配不是基本匹配条件,需要加载扩展模块(-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

下面是各匹配条件的总览:

mindmap
  root["匹配条件"]
    基本匹配
      源地址 -s
        单IP
        多IP
        网段
        取反
      目标地址 -d
      协议 -p
        tcp
        udp
        icmp
      网卡
        入口 -i
        出口 -o
    扩展匹配 -m
      tcp 模块
        --dport 目标端口
        --sport 源端口
        端口范围 22:25
      multiport 模块
        离散端口
        混合范围
      state 模块
        ESTABLISHED
        RELATED
        NEW

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

6. 黑白名单机制

%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#3B82F6', 'primaryTextColor': '#1E3A5F', 'primaryBorderColor': '#2563EB', 'lineColor': '#60A5FA', 'secondaryColor': '#10B981', 'tertiaryColor': '#F59E0B'}}}%%
flowchart LR
    subgraph blacklist [黑名单模式]
        direction TB
        B_DEF["默认策略: ACCEPT"] --> B_R1["规则: DROP 恶意IP-A"]
        B_R1 --> B_R2["规则: DROP 恶意IP-B"]
        B_R2 --> B_PASS["其余全部放行"]
    end

    subgraph whitelist [白名单模式]
        direction TB
        W_R1["规则: ACCEPT 信任IP-A"] --> W_R2["规则: ACCEPT 端口80"]
        W_R2 --> W_R3["规则: ACCEPT 端口443"]
        W_R3 --> W_DROP["兜底规则: DROP 全部"]
    end

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

    class B_DEF,B_PASS success
    class B_R1,B_R2 danger
    class W_R1,W_R2,W_R3 success
    class W_DROP danger

6.1 黑名单模式

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

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

6.2 白名单模式

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

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

6.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 不断。

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

7. 规则持久化

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

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

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

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

8. 实战: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 # 应该被拒绝

9. 命令速查表

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 标志位匹配和自定义链管理。