Linux 防火墙配置与管理完全教程

1 阅读1小时+

Linux 防火墙配置与管理完全教程

一、教程简介

本教程系统地讲解 Linux 防火墙的核心原理与四种主流配置工具:iptables、nftables、firewalld 和 UFW。涵盖从 netfilter 内核框架的五个 Hook 点到数据包在内核中的完整流转路径,从有状态/无状态防火墙的本质差异到每种工具的命令详解与实战 Demo。

无论你是在个人 VPS 上用 UFW 快速加固系统,还是在生产环境中用 nftables 追求极致性能,抑或是用 firewalld 的 Zone 模型管理多网络环境,本教程都能提供清晰、可操作的指导。


二、适用人群

角色典型需求
Linux 运维工程师生产环境防火墙策略制定、NAT 端口转发、DDoS 防护规则
安全工程师纵深防御体系中的主机防火墙层、入侵检测规则联动
个人开发者 / VPS 用户快速加固云服务器、开放常用服务端口、防 SSH 暴力破解
架构师 / 技术选型在 iptables / nftables / firewalld / UFW 之间做出技术决策
嵌入式开发者路由器/网关上的 netfilter 规则定制

三、完整文档目录

编号文件名核心内容
0101-原理概述.mdnetfilter 内核框架、五个 Hook 点、有状态 vs 无状态防火墙、四表五链模型
0202-iptables详解.md四表五链全景图、数据包流转路径、规则匹配性能分析、常用匹配模块与目标动作
0303-nftables详解.mdnftables 与 iptables 架构差异、集合/映射 O(1) 高效查找、原子规则更新、迁移指南
0404-firewalld详解.mdZone 信任级别模型、运行时 vs 永久配置分离、富规则语法、D-Bus API
0505-UFW详解.mdUFW 极简设计哲学、命令→iptables 映射原理、应用配置文件机制
0606-方案选择决策指南.md决策流程图、4 方案 x 12 维度对比矩阵、场景速查表、常见误区与 CheatSheet

四、快速场景速查表

场景推荐方案备选方案对应文档
个人桌面 / 开发机 (Ubuntu/Debian)UFWiptables(直接规则)05, 02
个人桌面 / 开发机 (Fedora/RHEL)firewalldiptables04, 02
小型 VPS / Web 服务器(单一管理员)UFW 或 firewalldiptables05, 04
中型生产环境(需要精细控制)firewalld + 富规则nftables04, 03
大型 / 高流量环境(性能敏感)nftablesiptables(遗留)03, 02
容器环境(Docker/K8s 宿主机)iptablesnftables(需谨慎)02, 03
路由器 / NAT 网关iptables 或 nftablesfirewalld02, 03
嵌入式 / Linux From Scratchiptablesnftables(较新内核)02, 03
安全合规要求高nftables(原子更新)firewalld03, 04
学习目的先 iptables 再 nftables02, 03

五、四种方案一句话简介

序号方案一句话简介
1iptablesLinux 防火墙的事实标准(2001-),通过四表五链模型直接操作 netfilter 内核框架
2nftables下一代内核包过滤子系统,统一替代 {ip,ip6,arp,eb}tables,支持集合/映射 O(1) 查找
3firewalldRHEL 系列的 Zone 模型防火墙,运行时/永久配置分离,D-Bus 动态管理
4UFWUbuntu/Debian 的极简防火墙前端,"一条命令搞定"的设计哲学

六、Demo 目录使用说明

教程配套的 demo 目录位于 demos/,包含四种方案的配置示例与可执行脚本:

demos/
├── README.md              # 本使用说明
├── iptables/              # iptables 规则脚本
│   ├── basic-rules.sh     # 基础防护规则(INPUT DROP + 白名单)
│   └── port-forwarding.sh # NAT 端口转发(DNAT/SNAT/MASQUERADE)
├── nftables/              # nftables 规则文件
│   ├── basic-rules.nft    # 完整 inet filter 表(集合/映射/计数器)
│   └── compare-iptables.nft # 10+ 组 iptables vs nftables 并排对比
├── firewalld/             # firewalld 脚本
│   └── zone-demo.sh       # Zone 切换、富规则、直接规则演示
└── ufw/                   # UFW 脚本
    └── quick-setup.sh     # 默认策略、应用名开放、限速、IP 白名单

每个脚本均可通过 sudo bash <脚本名>.sh 执行(测试环境,生产环境请先阅读脚本内容并按需修改)。


七、使用建议(阅读顺序)

新手路径

01-原理概述 → 02-iptables详解 → 根据你的场景选择 03/04/05
→ 06-方案选择决策指南

运维工程师路径

01-原理概述 → 02-iptables详解 → 04-firewalld详解 → 03-nftables详解
→ 06-方案选择决策指南

架构师/选型路径

01-原理概述 → 02-iptables详解 → 03-nftables详解 → 04-firewalld详解
→ 05-UFW详解 → 06-方案选择决策指南

速查路径

直接查阅本文"快速场景速查表"和"四种方案一句话简介表",定位需要的文档编号后跳转阅读。


八、关于本文档

  • 作者: Linux 防火墙技术研究小组
  • 版本: v2.0
  • 许可: CC BY-SA 4.0
  • 反馈: 欢迎提交 Issue / PR 改进本教程

提示:所有文档中的 IP 示例均使用 192.168.1.0/24 网段和 203.0.113.0/24 公网示例网段(RFC 5737)。请根据你的实际网络环境进行替换。


01 — Linux 防火墙原理概述

在深入学习各种防火墙配置工具之前,必须理解 Linux 防火墙的底层架构:netfilter 内核框架如何工作、有状态和无状态防火墙的本质区别、数据包如何在内核协议栈中流转以及四表五链模型的完整设计。


一、整体架构分层

Linux 防火墙系统可以划分为清晰的三层架构,每层承担不同的职责:

┌─────────────────────────────────────────────────────────────────┐
│                    用户态管理工具层 (User Space)                    │
│                                                                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌───────────────┐   │
│  │ iptables │ │ nft      │ │ firewall-cmd │ │     ufw       │   │
│  │ (传统)   │ │(下一代)  │ │ (firewalld)  │ │  (极简前端)   │   │
│  └────┬─────┘ └────┬─────┘ └──────┬───────┘ └──────┬────────┘   │
│       │            │              │                 │             │
│  ┌────┴────────────┴──────────────┴─────────────────┴───────┐    │
│  │              内核接口层                                   │    │
│  │   Netlink (xtables API)  │  Netlink (nf_tables API)      │    │
│  │   setsockopt/getsockopt  │  (NFNL_SUBSYS_NFTABLES)       │    │
│  └──────────────────────┬───────────────────────────────────┘    │
│                         │                                         │
├─────────────────────────┼─────────────────────────────────────────┤
│                    内核层 (Kernel Space)                           │
│                         │                                         │
│  ┌──────────────────────┴──────────────────────────────────┐     │
│  │                  netfilter 核心框架                        │     │
│  │                                                           │     │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │     │
│  │  │  五个 Hook    │  │  连接跟踪     │  │  NAT 子系统   │    │     │
│  │  │  注册点       │  │  (conntrack) │  │  (DNAT/SNAT)  │    │     │
│  │  └──────────────┘  └──────────────┘  └──────────────┘    │     │
│  │                                                           │     │
│  │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │     │
│  │  │ xtables 模块  │  │ nf_tables    │  │  数据包匹配   │    │     │
│  │  │ (iptables后端)│  │ (nftables后端)│  │  引擎         │    │     │
│  │  └──────────────┘  └──────────────┘  └──────────────┘    │     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                   │
│                         ↓                                         │
│               Linux 内核网络协议栈 (TCP/IP Stack)                    │
└─────────────────────────────────────────────────────────────────┘

三层通信路径总结

用户态工具 ──(Netlink/setsockopt)──▶ netfilter 内核框架 ──▶ 协议栈 Hook 点
     │                                        │
     └──(procfs /sys 只读)──▶ 查看内核状态 ◀──┘

关键原则:用户态工具通过 Netlink 将规则写入内核,内核在协议栈的 Hook 点执行匹配和动作;/proc/net/ 等 procfs 接口仅用于查看连接跟踪等信息,不能用于配置防火墙规则。


二、netfilter 内核框架详解

2.1 什么是 netfilter

netfilter 是 Linux 内核 2.4 版本(2001 年)引入的包过滤子系统,提供了数据包过滤、网络地址转换(NAT)和端口转换的完整框架。它通过在协议栈的关键位置嵌入钩子函数(Hook Functions),使开发者能够在数据包经过的不同阶段注册回调函数,从而实现对网络流量的精细控制。

netfilter 的核心设计哲学是:不对协议栈本身做侵入式修改,而是通过注册回调的方式,让内核模块在数据包流转的关键节点上"观察"并"决策"每个数据包的命运。

2.2 五个 Hook 点详解

netfilter 在内核协议栈中定义了五个标准 Hook 点,每个 Hook 点位于数据包处理流水线中的不同阶段:

                                         网络
                                          │
                                 ┌────────▼────────┐
                                 │   网卡驱动接收    │
                                 └────────┬────────┘
                                          │
                    ┌─────────────────────▼─────────────────────┐
                    │         [1] NF_IP_PRE_ROUTING              │
                    │                                             │
                    │  - 所有入站数据包必经之路                    │
                    │  - DNAT(目标地址转换)在此进行              │
                    │  - 原始目标地址在此处被修改                  │
                    │  - 对应链:PREROUTING                      │
                    └─────────────────────┬─────────────────────┘
                                          │
                                 ┌────────▼────────┐
                                 │    路由决策       │
                                 │ 目标IP == 本机?  │
                                 └───┬──────────┬───┘
                                     │ 是       │ 否(需转发)
                    ┌────────────────▼──┐  ┌───▼────────────────┐
                    │ [2] NF_IP_LOCAL_IN│  │ [3] NF_IP_FORWARD  │
                    │                  │  │                    │
                    │ - 发往本机的包    │  │ - 经本机转发的包   │
                    │ - INPUT 链在此   │  │ - FORWARD 链在此   │
                    │ - 过滤进入服务的  │  │ - 过滤转发的流量   │
                    │   流量           │  │                    │
                    └────────┬─────────┘  └─────────┬──────────┘
                             │                      │
                    ┌────────▼─────────┐            │
                    │    本地进程       │            │
                    │  (应用程序接收)   │            │
                    └────────┬─────────┘            │
                             │                      │
                    ┌────────▼──────────────────────▼──────────┐
                    │          [4] NF_IP_LOCAL_OUT              │
                    │                                            │
                    │  - 本机发出的数据包                          │
                    │  - OUTPUT 链在此                            │
                    │  - SNAT 可选在此(早于路由决策的场景)        │
                    └─────────────────────┬──────────────────────┘
                                          │
                                 ┌────────▼────────┐
                                 │    路由决策       │
                                 │ (确定出接口/下一跳)│
                                 └────────┬────────┘
                                          │
                    ┌─────────────────────▼──────────────────────┐
                    │        [5] NF_IP_POST_ROUTING               │
                    │                                             │
                    │  - 所有出站数据包必经之路                     │
                    │  - SNAT/MASQUERADE 在此进行                 │
                    │  - 源地址在此被最终修改                       │
                    │  - 对应链:POSTROUTING                      │
                    └─────────────────────┬──────────────────────┘
                                          │
                                 ┌────────▼────────┐
                                 │   网卡驱动发送    │
                                 └────────┬────────┘
                                          │
                                         网络

五个 Hook 点速查表:

Hook常量名触发时机主要用途对应链
Hook 1NF_IP_PRE_ROUTING数据包进入协议栈后,路由决策前DNAT、早期过滤、连接跟踪入口PREROUTING
Hook 2NF_IP_LOCAL_IN路由决策判定为本地后,进入本地进程前入站流量过滤(防火墙核心)INPUT
Hook 3NF_IP_FORWARD路由决策判定为转发后转发流量过滤(网关核心)FORWARD
Hook 4NF_IP_LOCAL_OUT本地进程生成数据包后,路由决策前出站流量过滤、本机 DNAT(透明代理)OUTPUT
Hook 5NF_IP_POST_ROUTING路由决策完成后,离开协议栈之前SNAT、MASQUERADEPOSTROUTING

2.3 连接跟踪子系统(conntrack)

netfilter 的连接跟踪子系统是有状态防火墙的基础。它为每个通过的数据包创建连接跟踪条目,记录连接的协议、状态、方向和超时信息。

连接跟踪状态转换图(TCP 三次握手为例):

      Client                       Server
         │                            │
         │ ──── SYN ────────────────► │
         │                            │
    ┌────▼──────┐                     │
    │   NEW     │  conntrack 创建条目   │
    └────┬──────┘                     │
         │                            │
         │ ◄──── SYN-ACK ──────────── │
         │                            │
    ┌────▼──────────┐                 │
    │  ESTABLISHED  │  双向通信确认    │
    └────┬──────────┘                 │
         │                            │
         │ ──── ACK ────────────────► │
         │                            │
    ┌────▼──────────┐                 │
    │  ESTABLISHED  │  连接已建立      │
    └────┬──────────┘                 │
         │                            │
         │ ──── 数据交换 ────────────► │
         │ ◄──── 数据交换 ──────────── │
         │                            │
         │ ──── FIN ────────────────► │
         │ ◄──── FIN/ACK ──────────── │
         │                            │
    ┌────▼──────┐                     │
    │ CLOSE/FIN │  进入关闭流程       │
    └────┬──────┘                     │
         │                            │
         │ (超时后 conntrack 条目删除) │

conntrack 维护的五种连接状态:

状态含义典型场景
NEW连接的第一个数据包,conntrack 表中无对应条目TCP SYN 包、UDP 新流
ESTABLISHED连接已建立,双向通信已确认TCP 三次握手完成后的数据包
RELATED与已建立连接相关的新连接(辅助连接)FTP 数据通道、SIP RTP 流、ICMP 差错报文
INVALID无法识别或状态异常的数据包TCP RST 重放、状态错乱、过期残留
UNTRACKED被显式标记为不跟踪的数据包使用 NOTRACK 目标的高性能场景
# 查看 conntrack 表内容
conntrack -L          # 列出所有连接跟踪条目
conntrack -S          # 统计信息(新建/删除/查找次数)
conntrack -E          # 实时事件流(NEW/UPDATE/DESTROY)

# 查看 conntrack 表容量
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count

三、有状态防火墙 vs 无状态防火墙

3.1 核心原理对比

无状态防火墙(Stateless Firewall)

无状态防火墙对每个数据包进行独立判断,不记录任何历史连接信息。每个数据包到达时,防火墙仅根据该包的头部信息(源IP、目标IP、源端口、目标端口、协议类型)与规则集进行逐条匹配。

工作流程:

Packet_1 (SYN, src:1.2.3.4:54321dst:5.6.7.8:80)
  → 检查规则:允许 TCP/80入站?→ 匹配 → 通过

Packet_2 (SYN-ACK, src:5.6.7.8:80dst:1.2.3.4:54321)
  → 检查规则:允许 TCP/80入站?→ 不匹配(方向相反)→ 需要额外规则!
  → 必须专门添加"允许源端口80的出站SYN-ACK"规则 → 否则被拒绝!

Packet_3 (ACK, src:1.2.3.4:54321dst:5.6.7.8:80)
  → 同样需要逐条匹配...

缺点总结:

  • 必须为双向流量分别创建规则(规则数量翻倍)
  • 无法区分"合法的响应数据包"和"伪造的攻击数据包"
  • 对 FTP、SIP 等动态端口协议支持困难
  • 规则数量随服务增多呈线性增长
有状态防火墙(Stateful Firewall)

有状态防火墙维护一个连接跟踪表(conntrack table),记录所有通过防火墙的活动连接。当新数据包到达时,防火墙首先检查它是否属于某个已知连接,如果是,则无需逐条匹配规则,直接放行。

工作流程:

Packet_1 (SYN, src:1.2.3.4:54321dst:5.6.7.8:80)
  → 查 conntrack 表:未找到(新连接)
  → 逐条匹配规则:匹配到"允许 TCP/80"
  → 在 conntrack 表中创建条目:[NEW][ESTABLISHED]
  → 放行

Packet_2 (SYN-ACK, src:5.6.7.8:80dst:1.2.3.4:54321)
  → 查 conntrack 表:找到关联条目(状态:ESTABLISHED)
  → **跳过所有规则匹配,直接放行!**

Packet_3 (ACK, src:1.2.3.4:54321dst:5.6.7.8:80)
  → 查 conntrack 表:找到关联条目
  → 直接放行(无需再遍历规则链)

3.2 有状态防火墙的优势

优势说明
配置简洁只需定义"发起方向"的规则,响应流量自动放行(单条规则 vs 双向两条规则)
安全性强自动拒绝不属于任何合法连接的伪造数据包,大幅减少攻击面
协议支持好通过 RELATED 状态自动追踪 FTP 数据通道、SIP RTP 流等辅助连接
性能优化ESTABLISHED 流量走快速路径,无需遍历全规则链

3.3 典型的"最小化"有状态防火墙规则

# iptables 版本 —— 三行搞定基础防护
iptables -P INPUT DROP
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# nftables 版本 —— 同样简洁
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0; policy drop; }
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input tcp dport 22 ct state new accept

规则解读:

  1. 默认丢弃所有入站流量
  2. 已建立和相关的连接直接放行(不检查后续规则)
  3. 仅允许 SSH 新连接的建立
  4. 其他所有入站请求全部被丢弃

四、数据包在内核中的完整流转路径

4.1 场景 A:入站数据包(外部 → 本机进程)

网卡驱动接收
    ↓
[1] PREROUTING Hook
    ├── raw 表(跳过 conntrack?)
    ├── conntrack 处理(NEW/ESTABLISHED 判定)
    ├── mangle 表(修改 TOS/TTL?)
    └── nat 表(DNAT?)
    ↓
路由决策:目标 IP == 本机 IP?
    ↓ 是
[2] INPUT Hook
    ├── mangle 表
    └── filter 表(核心过滤)
    ↓ 通过(ACCEPT)
本地进程接收

4.2 场景 B:转发数据包(外部 → 本机 → 另一网络)

网卡驱动接收
    ↓
[1] PREROUTING Hook(同场景 A)
    ├── conntrack → mangle → nat (DNAT)
    ↓
路由决策:目标 IP != 本机 IP?(需要转发)
    ↓ 是
[3] FORWARD Hook
    ├── mangle 表
    └── filter 表(网关过滤核心)
    ↓ 通过
[5] POSTROUTING Hook
    ├── mangle 表
    └── nat 表(SNAT/MASQUERADE)
    ↓
网卡驱动发送

4.3 场景 C:出站数据包(本机进程 → 外部)

本地进程生成数据包
    ↓
[4] OUTPUT Hook
    ├── raw 表
    ├── conntrack 处理
    ├── mangle 表
    ├── nat 表(本机 DNAT → 透明代理)
    └── filter 表(限制本机出站)
    ↓
路由决策(确定出接口和下一跳)
    ↓
[5] POSTROUTING Hook
    ├── mangle 表
    └── nat 表(SNAT/MASQUERADE)
    ↓
网卡驱动发送

4.4 关键路径速查

路径经过的 Hook 点(按顺序)典型场景
外部 → 本机PREROUTING → INPUT外部用户访问本机 Web 服务
本机 → 外部OUTPUT → POSTROUTING本机 curl 访问外部 API
外部 → 本机 → 外部(转发)PREROUTING → FORWARD → POSTROUTING作为网关/路由器转发流量
外部 → 本机 → 外部(DNAT+转发)PREROUTING(DNAT) → FORWARD → POSTROUTING(SNAT)端口映射到内网服务器

五、四表五链模型详解

iptables 的核心架构可以概括为"四表五链",理解这个模型是掌握 iptables 及所有基于 netfilter 的工具的基础。

5.1 四个表(Tables)

表是按功能分类的规则集合,每个表负责特定类型的处理:

                             优先级(处理顺序)
                                    │
┌───────────────────────────────────┼───────────────────────────────┐
│                                   ▼                               │
│  表          功能                 包含的链              使用场景    │
│                                                                   │
│  raw     跳过连接跟踪        PREROUTING, OUTPUT        高性能场景   │
│           ─────────────                                   │        │
│  mangle  修改包头标记        PREROUTING, INPUT,         QoS/TOS      │
│          (MARK/TOS/TTL)     FORWARD, OUTPUT,               │        │
│                              POSTROUTING                   │        │
│  nat     网络地址转换        PREROUTING(DNAT),           端口映射     │
│                              INPUT, OUTPUT,                │        │
│                              POSTROUTING(SNAT)             │        │
│  filter  数据包过滤          INPUT, FORWARD,         防火墙核心     │
│          (默认表)            OUTPUT                                │
└───────────────────────────────────────────────────────────────────┘

各表详细说明:

raw 表 —— 最高优先级。包含特殊目标 NOTRACK,用于标记某些数据包不需要被连接跟踪处理。适用场景:高并发 DNS 服务器(大量短时 UDP 查询会使 conntrack 表成为瓶颈)、已知安全的内部流量。

mangle 表 —— 第二优先级。用于对数据包进行"修饰"——修改 IP 头部中的特定字段:

  • 修改 TTL(Time To Live)值
  • 设置/修改 DSCP 字段实现 QoS
  • 给数据包打上内核标记(MARK),供后续路由策略(ip rule)使用
  • 给连接打上标记(CONNMARK),用于复杂策略路由

nat 表 —— 第三优先级。实现网络地址转换:

  • DNAT(目标地址转换):发生在 PREROUTING 链,修改目标地址和端口
  • SNAT(源地址转换):发生在 POSTROUTING 链,修改源地址实现内网上网

filter 表 —— 最低优先级(但最常用),默认表。负责最基础的数据包过滤:INPUT/OUTPUT/FORWARD 三个链的防火墙规则。

5.2 五个链(Chains)

链是按数据包流转位置分类的规则序列,每个链对应 netfilter 的一个 Hook 点:

链名对应 Hook 点数据包来源数据包去向典型规则示例
PREROUTINGNF_IP_PRE_ROUTING网卡收到路由决策DNAT、早期过滤
INPUTNF_IP_LOCAL_IN路由决策(本地)本地进程过滤入站流量
FORWARDNF_IP_FORWARD路由决策(转发)另一网卡网关过滤
OUTPUTNF_IP_LOCAL_OUT本地进程路由决策限制本机出站
POSTROUTINGNF_IP_POST_ROUTING路由决策后网卡发送SNAT、MASQUERADE

5.3 表与链的交叉矩阵

每个数据包经过链时,该链上不同表的规则按优先级顺序执行:

                PREROUTING    INPUT    FORWARD    OUTPUT    POSTROUTING
   raw              ✓           -         -         ✓            -
   mangle           ✓           ✓         ✓         ✓            ✓
   nat              ✓           ✓(极少)   -         ✓            ✓
   filter           -           ✓         ✓         ✓            -

以 PREROUTING 链为例的处理顺序:

数据包到达 PREROUTING 链
  ↓
[1] raw 表 PREROUTING 规则 → NOTRACK?(跳过 conntrack)
  ↓
[2] conntrack 处理 → 判定 NEW/ESTABLISHED/RELATED/INVALID
  ↓
[3] mangle 表 PREROUTING 规则 → 修改 TOS/TTL/MARK?
  ↓
[4] nat 表 PREROUTING 规则 → DNAT?
  ↓
路由决策 ...

5.4 四表五链全景图

                              数据包到达
                                  │
                    ┌─────────────▼─────────────┐
                    │     [PREROUTING 链]        │
                    │  raw → conntrack → mangle │
                    │  → nat (DNAT)             │
                    └─────────────┬─────────────┘
                                  │
                         ┌────────▼────────┐
                         │    路由决策       │
                         │ 目标IP == 本机?  │
                         └───┬──────────┬───┘
                             │ 是       │ 否(需转发)
              ┌──────────────▼──┐  ┌───▼──────────────┐
              │   [INPUT 链]    │  │  [FORWARD 链]     │
              │  mangle → filter│  │  mangle → filter  │
              └───────┬─────────┘  └───┬───────────────┘
                      │                │
              ┌───────▼─────────┐      │
              │    本地进程      │      │
              └───────┬─────────┘      │
                      │                │
              ┌───────▼─────────────────▼───────┐
              │         [OUTPUT 链]              │
              │  raw → conntrack → mangle       │
              │  → nat (DNAT) → filter          │
              └───────────────┬─────────────────┘
                              │
                     ┌────────▼────────┐
                     │    路由决策       │
                     │ (确定出接口/下一跳)│
                     └────────┬────────┘
                              │
              ┌───────────────▼───────────────┐
              │      [POSTROUTING 链]          │
              │  mangle → nat (SNAT/MASQ)     │
              └───────────────┬───────────────┘
                              │
                        数据包发出

六、规则匹配引擎的三种实现

从 netfilter 的视角,共有三种规则匹配引擎共存于 Linux 内核中:

                    用户空间工具
                         │
        ┌────────────────┼────────────────┐
        ▼                ▼                ▼
   ┌──────────┐   ┌──────────┐   ┌──────────────┐
   │ iptables │   │ nft      │   │ bpfilter     │
   │ 命令行   │   │ 命令行    │   │ (实验性)      │
   └────┬─────┘   └────┬─────┘   └──────┬───────┘
        │              │                │
        ▼              ▼                ▼
   ┌──────────┐   ┌──────────┐   ┌──────────────┐
   │ xtables  │   │nf_tables │   │   BPF        │
   │ (遗留)   │   │ (新一代)  │   │  (eBPF)      │
   └────┬─────┘   └────┬─────┘   └──────┬───────┘
        │              │                │
        └──────────────┼────────────────┘
                       ▼
              ┌─────────────────┐
              │ netfilter 核心   │
              │ 五个 Hook 点     │
              └─────────────────┘
引擎引入时间用户工具状态
xtables2001 (Linux 2.4)iptables/ip6tables/arptables/ebtables遗留但广泛部署,逐步淘汰
nf_tables2014 (Linux 3.13)nft(单一命令)Linux 防火墙的未来标准
bpfilter2018 (Linux 4.18)iptables(透明兼容)实验性,目标是用 eBPF 替代 netfilter

七、总结

理解 Linux 防火墙原理,核心在于掌握以下几点:

  1. 三层架构:用户态工具 → Netlink 内核接口 → netfilter 框架 + 协议栈 Hook 点,这是所有防火墙方案共通的底层路径。

  2. 五个 Hook 点:PREROUTING → INPUT/FORWARD → OUTPUT → POSTROUTING,数据包在每个 Hook 点都可能被拦截、修改或丢弃。

  3. 有状态防火墙是现代标准:通过 conntrack 连接跟踪,只需定义"发起方向"的规则,响应流量自动放行。这是安全性和配置简洁性的双重飞跃。

  4. 四表五链模型:raw → mangle → nat → filter 的处理顺序决定了规则的执行优先级;表与链的交叉矩阵是理解 iptables 的关键。

  5. 三种匹配引擎共存:xtables(iptables 后端)、nf_tables(nftables 后端)、bpfilter(实验性),nftables 是未来的方向。

  6. 方案选择取决于场景:从极简的 UFW 到高性能的 nftables,没有银弹,只有最适合当前场景的方案。

掌握了这些原理之后,再阅读后续的各方案详解文档,你就能理解每个工具的"为什么"而不只是"怎么做"。


下一步:02 — iptables 详解

02 — iptables 详解

iptables 是 Linux 内核 netfilter 框架的传统用户空间管理工具,自 2001 年伴随 Linux 2.4 内核发布以来,一直是 Linux 防火墙的事实标准。本文深入讲解 iptables 的核心概念、工作原理和进阶用法。


一、四表五链全景图

iptables 的核心架构可以概括为"四表五链"——四张功能表(raw/mangle/nat/filter)和五条数据包流经的链(PREROUTING/INPUT/FORWARD/OUTPUT/POSTROUTING)。

                              数据包到达
                                  │
                    ┌─────────────▼─────────────┐
                    │     [PREROUTING 链]        │
                    │                            │
                    │  1. raw       (NOTRACK)    │
                    │  2. conntrack (状态判定)    │
                    │  3. mangle    (TOS/MARK)   │
                    │  4. nat       (DNAT)       │
                    └─────────────┬─────────────┘
                                  │
                         ┌────────▼────────┐
                         │    路由决策       │
                         │ 目标IP == 本机?  │
                         └───┬──────────┬───┘
                             │ 是       │ 否(需转发)
              ┌──────────────▼──┐  ┌───▼──────────────┐
              │   [INPUT 链]    │  │  [FORWARD 链]     │
              │                │  │                   │
              │  1. mangle     │  │  1. mangle        │
              │  2. filter     │  │  2. filter        │
              └───────┬─────────┘  └───┬───────────────┘
                      │                │
              ┌───────▼─────────┐      │
              │    本地进程      │      │
              └───────┬─────────┘      │
                      │                │
              ┌───────▼─────────────────▼───────┐
              │         [OUTPUT 链]              │
              │                                  │
              │  1. raw       (NOTRACK)          │
              │  2. conntrack (状态判定)          │
              │  3. mangle    (TOS/MARK)         │
              │  4. nat       (本机 DNAT)        │
              │  5. filter    (出站过滤)          │
              └───────────────┬─────────────────┘
                              │
                     ┌────────▼────────┐
                     │    路由决策       │
                     │ (确定出接口/下一跳)│
                     └────────┬────────┘
                              │
              ┌───────────────▼───────────────┐
              │      [POSTROUTING 链]          │
              │                                │
              │  1. mangle    (TOS/MARK)       │
              │  2. nat       (SNAT/MASQ)      │
              └───────────────┬───────────────┘
                              │
                        数据包发出

1.1 四个表详解

表名功能包含的链典型使用场景优先级
raw跳过连接跟踪PREROUTING, OUTPUT高并发 DNS 服务器、内部信任流量、conntrack 表已满时的逃生通道最高
mangle修改数据包头部标记PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTINGTOS/DSCP 修改、TTL 调整、连接标记(CONNMARK)、策略路由标记(MARK)第二
nat网络地址转换PREROUTING, INPUT, OUTPUT, POSTROUTING端口转发(DNAT)、源地址转换(SNAT)、本机透明代理(REDIRECT)第三
filter数据包过滤(默认表)INPUT, FORWARD, OUTPUT防火墙核心:放行/拒绝/丢弃规则最低

当不指定 -t 参数时,iptables 默认操作 filter 表。

1.2 五个链详解

链名对应 Hook 点数据包来源数据包去向典型规则示例
PREROUTINGNF_IP_PRE_ROUTING网卡收到路由决策DNAT、连接跟踪
INPUTNF_IP_LOCAL_IN路由决策(本地)本地进程-p tcp --dport 22 -j ACCEPT
FORWARDNF_IP_FORWARD路由决策(转发)另一网卡网关过滤、Docker 容器间通信
OUTPUTNF_IP_LOCAL_OUT本地进程路由决策限制本机出站 DNS 查询
POSTROUTINGNF_IP_POST_ROUTING路由决策后网卡发送SNAT、MASQUERADE

1.3 表与链的交叉矩阵

                PREROUTING    INPUT    FORWARD    OUTPUT    POSTROUTING
   raw              ✓           -         -         ✓            -
   mangle           ✓           ✓         ✓         ✓            ✓
   nat              ✓           ✓         -         ✓            ✓
   filter           -           ✓         ✓         ✓            -

   ✓  = 该表在此链上有挂钩
   -  = 无挂钩

二、数据包流转路径图

2.1 三条关键路径

路径 A: 外部 → 本机(访问本机服务)
  ┌─────────┐     ┌─────────────┐     ┌──────────┐     ┌──────────┐
  │ 网卡接收  │ →  │ PREROUTING  │ →   │   INPUT   │ →   │ 本地进程  │
  │          │    │ raw→ct→mangle│     │ mangle    │    │          │
  │          │    │ → nat(DNAT) │     │ → filter  │    │          │
  └─────────┘    └─────────────┘     └──────────┘    └──────────┘

路径 B: 外部 → 本机 → 转发(网关模式)
  ┌─────────┐   ┌─────────────┐   ┌───────────┐   ┌──────────────┐   ┌─────────┐
  │ 网卡接收  │ → │ PREROUTING  │ → │ FORWARD   │ → │ POSTROUTING  │ → │ 网卡发送 │
  │         │   │ raw→ct→mangle│   │ mangle    │   │ mangle       │   │         │
  │         │   │ → nat(DNAT) │   │ → filter  │   │ → nat(SNAT)  │   │         │
  └─────────┘   └─────────────┘   └───────────┘   └──────────────┘   └─────────┘

路径 C: 本机 → 外部(本机主动出站)
  ┌──────────┐    ┌──────────┐    ┌──────────────┐    ┌─────────┐
  │ 本地进程  │ →  │  OUTPUT  │ →  │ POSTROUTING  │ →  │ 网卡发送 │
  │          │    │ raw→ct→  │    │ mangle       │    │         │
  │          │    │ mangle→nat│   │ → nat(SNAT)  │    │         │
  │          │    │ → filter │    │              │    │         │
  └──────────┘    └──────────┘    └──────────────┘    └─────────┘

2.2 关键路径速查

路径经过的链(按顺序)典型场景
外部 → 本机PREROUTING → INPUT外部用户访问本机 Web 服务
本机 → 外部OUTPUT → POSTROUTING本机 curl 访问外部 API
外部 → 本机 → 外部(转发)PREROUTING → FORWARD → POSTROUTING作为网关/路由器转发流量
外部 → 本机 → 外部(DNAT+转发)PREROUTING(DNAT) → FORWARD → POSTROUTING(SNAT)端口映射到内网服务器
本机透明代理OUTPUT(DNAT/REDIRECT) → POSTROUTING将本机出站流量重定向到本地代理

2.3 特殊说明:nat 表的 OUTPUT 链

nat 表的 OUTPUT 链是一个容易被忽视但非常实用的特性。它允许对本机发出的数据包进行 DNAT,典型场景:

  • 透明代理:将本机发向 80 端口的流量重定向到本地代理端口(如 3128)
  • 本机服务访问优化:将本机发出的数据库请求重定向到本地缓存
# 将本机发出的所有 80 端口流量重定向到本地 3128 代理
iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-ports 3128

三、规则匹配顺序与性能分析

3.1 核心原则:第一匹配即停止

iptables 的规则匹配采用**"第一匹配即停止"(First Match Wins)**策略:

数据包进入链
    
    
规则 1: 匹配? ───  ──  执行目标动作
                         
           终结性目标?     
规则 2: 匹配? ───  ──      停止处理
                
                  
   ...      非终结性目标?   
                  
              继续匹配
规则 N: 匹配? ───  ── 
     
    
链默认策略(policy)

终结性目标:ACCEPT、DROP、REJECT、SNAT、DNAT、MASQUERADE、REDIRECT 非终结性目标:LOG、MARK、RETURN(自定义链中)、NOTRACK

3.2 规则遍历过程示例

假设 INPUT 链有以下规则:

Chain INPUT (policy DROP)
num  target     prot opt source               destination
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
2    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22
3    ACCEPT     tcp  --  192.168.1.0/24       0.0.0.0/0            tcp dpt:80
4    LOG        tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
5    DROP       all  --  0.0.0.0/0            0.0.0.0/0

数据包处理过程:

场景匹配路径结果原因
已建立连接的 SSH 响应包规则 1ACCEPT(1 次匹配)ctstate ESTABLISHED 命中
来自 192.168.1.100 的新 HTTP 请求规则 1→2→3ACCEPT(3 次匹配)规则 3 命中
来自 10.0.0.1 的新 HTTP 请求规则 1→2→3→4→5先 LOG 后 DROP(5 次匹配)规则 4 非终结 + 规则 5 命中
来自外网的 UDP 53 端口扫描规则 1→2→3→4DROP(策略)无规则匹配,执行 policy DROP

3.3 规则顺序对性能的影响

iptables 的规则匹配是 O(n) 线性查找。规则顺序直接影响性能:

最佳实践排序(从快到慢):

1. ESTABLISHED,RELATED 规则     ← 绝大多数合法流量在此命中,立即跳过后面的所有规则
2. 高频匹配的具体规则(如 SSH)  ← 将常用服务放在前面
3. 低频匹配的具体规则(如 MySQL)← 偶尔使用的服务放在后面
4. LOG 规则(非终结性)         ← 日志记录
5. 默认拒绝(或链的默认策略)    ← 兜底规则

性能基准参考(规则数量对吞吐量的影响):

规则数量最坏匹配次数预估吞吐量影响(64 字节包)
10 条10 次~950K pps(基准的 95%)
50 条50 次~850K pps(基准的 85%)
100 条100 次~800K pps(基准的 80%)
500 条500 次~500K pps(基准的 50%)
1000 条1000 次~300K pps(基准的 30%)

注:以上为相对参考值。将 ESTABLISHED 规则放在第一位可使绝大多数流量仅需 1 次匹配。

3.4 错误顺序示例与修复

# 错误示例:DROP 在最前面,后面的 ACCEPT 永远不会被执行
iptables -A INPUT -j DROP                          # ← 所有包都被丢弃
iptables -A INPUT -p tcp --dport 22 -j ACCEPT      # ← 这条规则永远到不了

# 正确示例:具体规则在前,默认拒绝在后
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT  # 最快路径
iptables -A INPUT -p tcp --dport 22 -j ACCEPT      # SSH
iptables -A INPUT -p tcp --dport 80 -j ACCEPT      # HTTP
iptables -A INPUT -j DROP                          # 最后拒绝所有

3.5 链的默认策略(Policy)

每个内置链都有一个默认策略,当数据包走到链末尾仍未匹配任何规则时执行:

# 设置默认策略
iptables -P INPUT DROP     # INPUT 链默认丢弃
iptables -P FORWARD DROP   # FORWARD 链默认丢弃
iptables -P OUTPUT ACCEPT  # OUTPUT 链默认放行

注意事项:

  • 默认策略只能是 ACCEPTDROP,不能是 REJECT(REJECT 需要生成 ICMP 响应,这必须通过匹配规则来实现)
  • 如果远程通过 SSH 管理服务器,在设置 iptables -P INPUT DROP 之前,务必先添加允许 SSH 的规则,否则会将自己锁在外面

四、常用匹配模块

iptables 通过**模块化匹配扩展(match extensions)**提供丰富的匹配能力。使用 -m 参数加载匹配模块。

4.1 conntrack(连接跟踪匹配)

conntrackstate 模块的现代替代品,提供更精确的连接状态匹配:

# 基本状态匹配
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# 按方向匹配
iptables -A INPUT -m conntrack --ctdir ORIGINAL -j ACCEPT  # 原始方向
iptables -A INPUT -m conntrack --ctdir REPLY -j ACCEPT     # 回复方向
特性state 模块(遗留)conntrack 模块(推荐)
INVALID 原因不区分可匹配具体原因(--ctstatus
方向感知支持 ORIGINAL/REPLY
状态数4 种5 种(多了 UNTRACKED)

4.2 tcp(TCP 协议匹配)

# 基本端口匹配
iptables -A INPUT -p tcp --dport 80 -j ACCEPT          # 目标端口 80
iptables -A INPUT -p tcp --sport 1024:65535 -j ACCEPT  # 源端口范围
iptables -A INPUT -p tcp --dport 80:90 -j ACCEPT       # 目标端口范围 80-90

# TCP 标志位匹配(高级过滤)
iptables -A INPUT -p tcp --tcp-flags SYN,RST,ACK SYN -j ACCEPT   # 仅 SYN 标志(新连接请求)
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP            # 空标志包(扫描探测)
iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP    # SYN+FIN 组合(可疑扫描)
iptables -A INPUT -p tcp --tcp-flags ALL SYN,ACK -j ACCEPT      # SYN+ACK(服务器响应)
iptables -A INPUT -p tcp --tcp-flags ALL RST -j ACCEPT          # RST(连接重置)

4.3 udp(UDP 协议匹配)

iptables -A INPUT -p udp --dport 53 -j ACCEPT     # DNS 端口
iptables -A INPUT -p udp --sport 123 -j ACCEPT    # NTP 端口
iptables -A INPUT -p udp --dport 67:68 -j ACCEPT  # DHCP 端口范围

4.4 multiport(多端口匹配)

当需要匹配多个不连续端口时,multiport 比写多条规则更高效:

# 匹配多个目标端口(一条规则替代多条)
iptables -A INPUT -p tcp -m multiport --dports 22,80,443 -j ACCEPT

# 端口范围组合
iptables -A INPUT -p tcp -m multiport --dports 20:22,80,443,8080:8090 -j ACCEPT

# 最多可指定 15 个端口(可通过 net.netfilter.nf_conntrack_max 调整)

4.5 limit(速率限制匹配)

limit 模块使用令牌桶算法限制数据包匹配速率,是防御 DoS 攻击的重要工具:

# 令牌桶参数:--limit 速率 --limit-burst 桶容量
iptables -A INPUT -p tcp --dport 22 \
    -m limit --limit 3/minute --limit-burst 5 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

输出解读:

  • --limit 3/minute:每分钟允许 3 个新连接(每 20 秒生成 1 个令牌)
  • --limit-burst 5:初始桶中有 5 个令牌(允许初始突发 5 个连接)
  • 第 6 个连接起被 DROP(桶已空,令牌补充速度只有 3/min)

速率单位: /second/minute/hour/day

# 防 ping 洪水
iptables -A INPUT -p icmp --icmp-type echo-request \
    -m limit --limit 1/second --limit-burst 5 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP

4.6 recent(最近访问记录)

recent 模块基于源 IP 维护动态访问记录,实现智能限速(防暴力破解专用):

# SSH 暴力破解防护 —— 60 秒内尝试超过 3 次的新连接一律拒绝
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
    -m recent --set --name SSH                    # 记录每一次新连接
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
    -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP  # 第 4 次起拒绝

输出解读:

  • --set:将当前源 IP 记录到名为 SSH 的列表中
  • --update:更新该 IP 的最后出现时间,同时检查命中次数
  • --seconds 60:仅查看过去 60 秒内的记录
  • --hitcount 4:60 秒内第 4 次命中时执行 DROP

4.7 其他常用匹配模块速查

模块用途示例
iprange匹配 IP 范围(比 CIDR 更灵活)-m iprange --src-range 192.168.1.100-192.168.1.200
mac匹配源 MAC 地址-m mac --mac-source 00:11:22:33:44:55
owner匹配进程 UID/GID-m owner --uid-owner 1000
time按时间范围匹配-m time --timestart 09:00 --timestop 18:00
string匹配数据包载荷内容-m string --string "cmd.exe" --algo bm
comment为规则添加注释-m comment --comment "允许 SSH"
connlimit限制每 IP 并发连接数-m connlimit --connlimit-above 10 -j REJECT
hashlimit基于哈希的精确限速-m hashlimit --hashlimit-name mylimit --hashlimit-above 10/sec
tos匹配 TOS/DSCP 字段-m tos --tos 0x10
ttl匹配 TTL 值-m ttl --ttl-lt 64

五、常用目标(Target)

目标(Target)是匹配成功后执行的动作,分为终结性非终结性两种。

5.1 终结性目标(执行后停止当前链处理)

ACCEPT —— 放行
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 允许数据包通过当前链,继续协议栈的下一个处理阶段
DROP —— 静默丢弃
iptables -A INPUT -s 203.0.113.50 -j DROP
# 丢弃数据包,不给发送方任何回应
# 优点:攻击者无法判断目标是否存在(不暴露端口)
# 缺点:合法访问超时较长(等待 TCP 重传超时,通常 30-120 秒)
REJECT —— 拒绝并回应
iptables -A INPUT -j REJECT                                    # 默认 ICMP port unreachable
iptables -A INPUT -j REJECT --reject-with icmp-host-unreachable
iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset     # TCP RST(推荐)
# 优点:合法客户端快速收到拒绝通知(TCP RST 立即返回)
# 缺点:暴露主机存在的事实

DROP vs REJECT 选择建议:

场景推荐理由
WAN/公网接口DROP隐藏服务存在,降低被扫描识别的风险
LAN/内网接口REJECT快速告知客户端连接被拒绝,改善用户体验
针对已知攻击 IPDROP不给攻击者任何反馈
SNAT —— 静态源地址转换
# 固定单个公网 IP
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.10

# IP 范围(轮询分配)
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 \
    -j SNAT --to-source 203.0.113.10-203.0.113.20

# 适用场景:有固定公网 IP 的企业网络
MASQUERADE —— 动态源地址转换
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# 自动使用出口网卡的当前 IP 地址
# 适用场景:PPPoE 拨号、DHCP 动态 IP 的家用路由器
# 网卡 IP 变化时自动适应,无需手动更新规则
DNAT —— 目标地址转换
# 单端口映射
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80

# IP 范围(轮询,实现简单负载均衡)
iptables -t nat -A PREROUTING -p tcp --dport 80 \
    -j DNAT --to-destination 192.168.1.100-192.168.1.110
REDIRECT —— 端口重定向
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3128
# 是 DNAT 的特化版本,只能重定向到本机
# 适用场景:透明代理(如 Squid)

5.2 非终结性目标(执行后继续匹配后续规则)

LOG —— 日志记录
iptables -A INPUT -j LOG --log-prefix "IPT-DROP: " --log-level 4
# 日志写入 /var/log/kern.log(或通过 rsyslog/journald 配置)
# 注意:LOG 后会继续匹配后续规则,需要配合终结性目标

常用 LOG 选项:

  • --log-prefix "前缀":日志前缀,最长 29 字符
  • --log-level 0-7:日志级别(默认 4=warning)
  • --log-tcp-sequence:记录 TCP 序列号
  • --log-tcp-options:记录 TCP 选项
  • --log-ip-options:记录 IP 选项
MARK —— 数据包标记
iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
# 标记存储在 sk_buff 结构中,仅内核可见
# 配合 ip rule 和路由表实现策略路由
RETURN —— 返回调用链
# 在自定义链中:返回调用链继续执行下一条规则
# 在内置链中:执行该链的默认策略
iptables -A MY_CHAIN -s 192.168.1.0/24 -j RETURN

5.3 用户自定义链

# 创建自定义链
iptables -N MY_RULES

# 向自定义链添加规则
iptables -A MY_RULES -s 192.168.1.0/24 -j ACCEPT
iptables -A MY_RULES -j DROP

# 从内置链跳转到自定义链(类似函数调用)
iptables -A INPUT -j MY_RULES

# 删除自定义链(需先清空和移除所有引用)
iptables -F MY_RULES    # 清空链
iptables -X MY_RULES    # 删除链

六、iptables-save/restore 持久化

6.1 为什么需要持久化

iptables 规则存储在内核内存中,系统重启后所有规则丢失。必须通过持久化机制将规则保存到磁盘,系统启动时自动加载。

6.2 iptables-save —— 导出规则

# 导出所有表的规则
iptables-save

# 导出指定表
iptables-save -t filter
iptables-save -t nat

# 导出到文件
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6

输出格式:

# Generated by iptables-save v1.8.7
*filter                          # 表名
:INPUT DROP [23:2156]            # 链名 默认策略 [包计数:字节计数]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [156:18234]
-A INPUT -i lo -j ACCEPT         # 规则(-A 追加)
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
COMMIT                           # 提交
*nat
:PREROUTING ACCEPT [0:0]
...
COMMIT
# Completed

6.3 iptables-restore —— 恢复规则

# 从文件恢复规则(默认刷新现有规则)
iptables-restore < /etc/iptables/rules.v4

# 不刷新现有规则(追加模式)
iptables-restore --noflush < /etc/iptables/rules.v4

# 测试语法(不实际加载)
iptables-restore --test < /etc/iptables/rules.v4

6.4 持久化方案对比

方案适用发行版配置文件位置
iptables-persistentDebian/Ubuntu/etc/iptables/rules.v4 + rules.v6
iptables-servicesRHEL/CentOS 7/etc/sysconfig/iptables
systemd 自定义服务通用自行创建 service 文件
netfilter-persistent较新 Debian/Ubuntuiptables-persistent 的现代替代

6.5 systemd 持久化示例

# /etc/systemd/system/iptables-restore.service
[Unit]
Description=Restore iptables firewall rules
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
ExecStart=/sbin/ip6tables-restore /etc/iptables/rules.v6
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
# 启用
systemctl enable iptables-restore.service
systemctl start iptables-restore.service

6.6 保存前检查规则的最佳实践

# 1. 查看当前规则(带行号)
iptables -L -n -v --line-numbers

# 2. 导出备份(带时间戳)
iptables-save > /etc/iptables/rules.v4.$(date +%Y%m%d_%H%M%S).bak

# 3. 原子加载新规则
iptables-restore < /etc/iptables/rules.v4

# 4. 验证新规则生效
iptables -L -n

# 5. 回滚(如果出错)
iptables-restore < /etc/iptables/rules.v4.YYYYMMDD_HHMMSS.bak

七、适用场景与注意事项

适用场景

场景说明
传统服务器(通用 Linux)iptables 适用所有 Linux 发行版,兼容性最好
Docker/K8s 宿主机Docker 直接操作 iptables,天然兼容
NAT 网关/路由器iptables DNAT/SNAT/MASQUERADE 功能成熟稳定
嵌入式低版本内核内核 >= 2.4 即可,依赖最少
Fail2ban 联动Fail2ban 默认使用 iptables 封禁 IP
学习目的理解 iptables 是理解所有 Linux 防火墙的基础

注意事项

  1. 规则顺序至关重要:ESTABLISHED 规则必须放在最前面,DROP 必须放在最后
  2. 远程操作风险iptables -P INPUT DROP 之前务必添加允许 SSH 的规则
  3. 重启丢失规则:iptables 规则存储在内存中,必须通过 iptables-persistent 等工具持久化
  4. 逐步淘汰趋势:RHEL 8+ / Debian 10+ 已默认推荐 nftables,iptables 进入维护模式
  5. 与 nftables 混用的冲突:iptables 和 nftables 同时操作 netfilter Hook 点时可能产生意料之外的交互

常用速查命令

# 查看规则
iptables -L -n -v                    # 查看 filter 表(默认)
iptables -t nat -L -n -v             # 查看 nat 表
iptables -L -n -v --line-numbers     # 带行号
iptables -S                          # 查看可执行的规则列表

# 管理规则
iptables -A INPUT ... -j ACCEPT      # 追加到链末尾
iptables -I INPUT 1 ... -j ACCEPT    # 插入到链开头(位置 1)
iptables -D INPUT 3                  # 按行号删除
iptables -D INPUT ... -j ACCEPT      # 按规则内容删除
iptables -F INPUT                    # 清空链
iptables -Z                          # 清零计数器

# 默认策略
iptables -P INPUT DROP               # 设置默认策略
iptables -P INPUT ACCEPT             # 恢复默认放行

下一步:03 — nftables 详解

03 — nftables 详解

nftables 是 Linux 内核 netfilter 框架的下一代包过滤子系统,旨在彻底替代 {ip,ip6,arp,eb}tables。它提供了统一的配置语法、更高的性能和更灵活的规则管理能力。


一、nftables vs iptables 架构差异

1.1 iptables 的架构缺陷

iptables 从 2001 年伴随 Linux 2.4 发布后,逐渐暴露出以下架构性问题:

问题详细说明
代码重复IPv4 (iptables)、IPv6 (ip6tables)、ARP (arptables)、以太网桥 (ebtables) 各有独立的内核模块和用户空间工具,大量代码重复
规则膨胀表面一条规则(如 "允许 80 端口"),内核中实际复制为多条内部规则,检查每个可能的协议族和扩展组合
性能退化每条规则都需要遍历内核模块链表,规则越多性能越差(O(n) 线性查找)
缺乏原子性规则是逐条添加/删除的,中间状态可能产生安全空窗期
难以调试用户空间的 iptables 命令和内核中的实际规则表示差异巨大,难以追踪

1.2 nftables 的架构革新

nftables 在 2014 年随 Linux 3.13 内核引入,从零开始重新设计:

┌──────────────────────────────────────────────────────────────────┐
│                       用户空间 (nft CLI)                            │
│                                                                    │
│  nft list ruleset         nft -f ruleset.nft       nft add rule   │
│  (查看规则)                (加载规则文件)            (添加单条规则)   │
└──────────────────────────────────┬───────────────────────────────┘
                                   │  netlink (NFNL_SUBSYS_NFTABLES)
┌──────────────────────────────────▼───────────────────────────────┐
│                         内核空间 (nf_tables)                       │
│                                                                    │
│  ┌────────────────┐   ┌─────────────────┐   ┌───────────────────┐ │
│  │  表达式解析器   │   │  规则编译器       │   │  JIT 字节码引擎    │ │
│  │  (parser)      │   │  (compiler)      │   │  (jit engine)     │ │
│  └───────┬────────┘   └────────┬────────┘   └─────────┬─────────┘ │
│          │                     │                        │          │
│          └─────────────────────┼────────────────────────┘          │
│                                ▼                                   │
│                     ┌────────────────────┐                         │
│                     │  统一数据包匹配引擎  │                         │
│                     │  (generic packet    │                         │
│                     │   matching engine)  │                         │
│                     └────────────────────┘                         │
│                                │                                   │
│          ┌─────────────────────┼───────────────────────┐           │
│          ▼                     ▼                       ▼           │
│  ┌──────────────┐     ┌──────────────┐       ┌──────────────┐     │
│  │ IPv4 匹配     │     │ IPv6 匹配     │       │ ARP/网桥匹配   │    │
│  │ (单一模块)    │     │ (单一模块)    │       │ (单一模块)     │    │
│  └──────────────┘     └──────────────┘       └──────────────┘     │
│                                                                    │
│  ┌──────────────┐     ┌──────────────┐       ┌──────────────┐     │
│  │ 集合查找 O(1) │     │ 映射查找 O(1) │       │ 连接跟踪       │    │
│  │ (hash/rbtree)│     │ (hash/rbtree)│       │ (conntrack)    │    │
│  └──────────────┘     └──────────────┘       └──────────────┘     │
└────────────────────────────────────────────────────────────────────┘

1.3 核心差异对比表

维度iptables (xtables)nftables
协议族处理4 个独立工具(iptables/ip6tables/arptables/ebtables)单一 nft 命令,通过 family 区分
表/链结构固定四表五链,不可自定义完全可自定义的表和链
规则存储二进制 blob,每个匹配扩展独立高级抽象语法树(AST),编译为统一字节码
匹配执行遍历内核模块链表(逐模块调用)内联 JIT 编译后的字节码(接近原生代码速度)
规则查找O(n) 线性遍历支持 O(1) 集合/映射查找
规则更新逐条操作,非原子事务性原子更新
规则膨胀1 条用户规则 → 内核中多条规则1 条用户规则 → 1 条编译规则
动态规则不支持支持(运行时添加/删除,无需刷新)
IPv4/IPv6分开管理inet family 统一处理双栈
代码量~30000 行(内核)+ 4 个独立工具~10000 行(内核)+ 1 个统一工具

1.4 性能优势来源

  1. JIT 编译:将规则编译为 BPF(Berkeley Packet Filter)字节码,内核直接执行
  2. 集合查找:使用哈希表或红黑树,将 O(n) 规则遍历优化为 O(1) 集合查找
  3. 内联执行:匹配指令被编译为内联代码,减少函数调用开销
  4. 规则压缩:一条 nftables 规则可以等效替代多条 iptables 规则

性能基准参考(100 条规则,简单匹配场景):

指标iptablesnftables
规则加载时间~50ms~5ms
吞吐量(64 字节包)~800K pps~1.2M pps
规则集内存占用~120KB~30KB
原子更新耗时逐条操作,不可原子化~1ms(单次事务提交)

二、核心概念:Table / Family / Chain / Rule / Set / Map

2.1 Family(协议族)

nftables 中 family 决定了规则作用于哪个协议栈:

Family说明对应 iptables 工具
ipIPv4 协议栈iptables
ip6IPv6 协议栈ip6tables
inet统一的 IPv4+IPv6 协议栈(推荐iptables + ip6tables 两套规则
arpARP 协议栈arptables
bridge以太网桥接ebtables
netdev设备级(Ingress 钩子,早于协议栈)无(nftables 独有)

inet family 的优势: 一条规则同时处理 IPv4 和 IPv6 流量,无需维护两套相同策略的规则集,减少 50% 的维护工作。

2.2 Table(表)

Table 是规则的容器,在 nftables 中可以任意命名(不像 iptables 固定的四表):

# 创建自定义名称的表
nft add table inet my_firewall
nft add table ip my_nat_rules

# 删除表(级联删除表中所有链和规则)
nft delete table inet my_firewall

# 列出所有表
nft list tables

社区惯例: 虽然表名可任意命名,但通常遵循 iptables 的习惯使用 filternatmangle 等名称。

2.3 Chain(链)

Chain 定义了数据包的处理路径,绑定到特定的 Hook 点:

# 语法
nft add chain [family] [table] [chain] \
    { type [type] hook [hook] priority [priority] ; policy [policy] ; }

# 参数说明:
#   type:     filter(过滤), nat(地址转换), route(路由)
#   hook:     prerouting, input, forward, output, postrouting, ingress
#   priority: 数值,越小越优先(可为负值)
#   policy:   accept(默认放行), drop(默认丢弃)

# 示例
nft add chain inet my_filter input_chain \
    { type filter hook input priority 0; policy drop; }

优先级参考表:

优先级值用途对应 iptables 表
-300原始包过滤(conntrack 之前)raw
-150连接跟踪修改conntrack
0标准过滤filter
100DNATnat(PREROUTING)
100SNAT/MASQUERADEnat(POSTROUTING)
300路由后处理无直接对应

2.4 Rule(规则)

Rule 是链中的最小执行单元,由匹配条件和动作组成:

规则结构:
  [匹配条件1] [匹配条件2] ... [动作]

匹配条件可以自然串联(逻辑与):
  ip saddr 192.168.1.0/24 tcp dport 22 ct state new accept
  = 匹配来自 192.168.1.0/24 网段、目标端口 22、状态为 NEW 的 TCP 包 → 放行

2.5 Set(集合)—— O(1) 高效查找原理

集合是 nftables 最强大的特性之一。它将多个离散值分组在一起,内部使用哈希表或红黑树存储,支持 O(1) 高效查找:

iptables 的线性匹配(O(n)):

数据包到达 → 检查规则1(不匹配)→ 规则2(不匹配)→ ... → 规则100(匹配!)
最坏情况:100 次检查


nftables 的集合查找(O(1)):

数据包到达 → 提取目标端口 → 哈希查找 → 一次命中/未命中
无论集合中有多少个端口,查找时间恒定

集合使用示例:

# 匿名集合(临时,无法在其他规则中引用)
nft add rule inet filter input ip saddr { 10.0.0.1, 10.0.0.2, 10.0.0.3 } accept

# 命名集合(可复用)
nft add set inet filter allowed_ports { type inet_service; }
nft add element inet filter allowed_ports { 22, 80, 443, 8080 }

# 在规则中使用命名集合
nft add rule inet filter input tcp dport @allowed_ports accept

集合内部存储方式:

存储方式适用场景查找复杂度示例
哈希表(默认)离散值,无固定顺序O(1){ 22, 80, 443 }
红黑树需要范围查找或前缀匹配O(log n){ 10.0.0.0/8, 172.16.0.0/12 }
间隔树IP 地址段重叠匹配O(log n){ 192.168.1.100-192.168.1.200 }
# 指定存储方式
nft add set inet filter ssh_blocklist \
    { type ipv4_addr; flags interval; }
nft add element inet filter ssh_blocklist \
    { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }

性能对比 —— 假设匹配 100 个源 IP:

方案规则数最坏匹配次数内存占用
iptables(逐条)100 条100 次~12KB
nftables(集合)1 条 + 1 个集合1 次(哈希)~3KB

2.6 Map(映射)

Map 是键值对形式的字典结构,将输入值映射到输出动作或目标值:

# 定义映射:端口 → 动作
nft add map inet filter port_to_action \
    { type inet_service: verdict; }
nft add element inet filter port_to_action \
    { 22: accept, 80: accept, 443: accept }

# 在规则中使用映射
nft add rule inet filter input tcp dport vmap @port_to_action

DNAT 映射示例(一条规则替代多条 iptables DNAT 规则):

nft add map inet nat dnat_map \
    { type inet_service: ipv4_addr . inet_service; }
nft add element inet nat dnat_map {
    8080: 192.168.1.100 . 80,
    2222: 192.168.1.50  . 22,
    8443: 192.168.1.200 . 443
}
nft add rule inet nat prerouting dnat to tcp dport map @dnat_map

输出解读: 一条规则完成了三个端口的 DNAT 映射(8080→192.168.1.100:80、2222→192.168.1.50:22、8443→192.168.1.200:443),如果用 iptables 需要 3 条 -j DNAT 规则。


三、nft 语法规则

3.1 语法对照表(iptables → nftables)

场景iptables 命令nftables 命令
列出规则iptables -L -n -vnft list ruleset
列出特定表iptables -t filter -Lnft list table inet filter
设置默认策略iptables -P INPUT DROP在 chain 定义中 policy drop
允许 loopbackiptables -A INPUT -i lo -j ACCEPTnft add rule inet filter input iif lo accept
允许已建立连接iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPTnft add rule inet filter input ct state established,related accept
允许端口iptables -A INPUT -p tcp --dport 22 -j ACCEPTnft add rule inet filter input tcp dport 22 accept
允许多端口iptables -A INPUT -p tcp -m multiport --dports 22,80,443 -j ACCEPTnft add rule inet filter input tcp dport {22,80,443} accept
拒绝并重置iptables -A INPUT -j REJECT --reject-with tcp-resetnft add rule inet filter input reject with tcp reset
DNATiptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80nft add rule inet nat prerouting tcp dport 8080 dnat to 192.168.1.100:80
SNATiptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.10nft add rule inet nat postrouting ip saddr 192.168.1.0/24 oif eth0 snat to 203.0.113.10
MASQUERADEiptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADEnft add rule inet nat postrouting ip saddr 192.168.1.0/24 oif eth0 masquerade
限速iptables -A INPUT -p tcp --dport 22 -m limit --limit 3/minute -j ACCEPTnft add rule inet filter input tcp dport 22 limit rate 3/minute accept
日志iptables -A INPUT -j LOG --log-prefix "DROP: "nft add rule inet filter input log prefix "DROP: "
IP 范围iptables -A INPUT -m iprange --src-range 10.0.0.1-10.0.0.100 -j ACCEPTnft add rule inet filter input ip saddr 10.0.0.1-10.0.0.100 accept
保存规则iptables-save > rules.v4nft list ruleset > rules.nft
恢复规则iptables-restore < rules.v4nft -f rules.nft
刷新规则iptables -Fnft flush ruleset
删除单条iptables -D INPUT 3nft delete rule inet filter input handle 5

3.2 表达式链式组合

nftables 语法允许条件自然地串联,无需像 iptables 那样使用多个 -m 参数:

# iptables: 需要多个 -m 参数
iptables -A INPUT -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -m recent --set

# nftables: 自然顺序书写
nft add rule inet filter input tcp dport 22 ct state new ...

3.3 声明式配置文件格式

nftables 支持声明式配置文件,与 nft list ruleset 输出一致,便于版本管理:

#!/usr/sbin/nft -f

# 清空现有规则
flush ruleset

# 定义表
table inet filter {

    # 定义集合(端口白名单)
    set allowed_ports {
        type inet_service
        elements = { 22, 80, 443, 8080 }
    }

    # 定义 IP 黑名单(使用间隔树存储)
    set banned_ips {
        type ipv4_addr
        flags interval
        elements = { 10.0.0.0/8, 172.16.0.0/12 }
    }

    # 定义链
    chain input {
        type filter hook input priority 0; policy drop;

        # loopback 无条件放行
        iif lo accept

        # 已建立连接快速放行
        ct state established,related accept

        # 黑名单直接丢弃
        ip saddr @banned_ips drop

        # 使用集合开放端口
        tcp dport @allowed_ports ct state new accept

        # 日志记录(记录丢弃的包)
        log prefix "nft-input-drop: " counter drop
    }
}

四、原子规则更新机制

4.1 原子更新的意义

在 iptables 中,添加或删除规则是逐条操作的,中间状态可能产生安全空窗期:

# 危险操作序列!
iptables -D INPUT 3          # 删除了一条负载均衡规则
iptables -I INPUT 3 ...      # 在新规则插入之前,存在短暂的安全空窗期
#     ↑—— 在这两个命令之间,合法流量可能被误拦,攻击流量可能被放行

nftables 通过 nft -f 将整个规则集作为一个原子事务提交:

# 所有变更在单个事务中原子提交
nft -f new_ruleset.nft
# 新规则集要么完全生效,要么完全不生效,不存在中间状态

4.2 原子更新的实现方式

用户空间:  编写完整规则文件
              │
              ▼
            nft -f rules.nft
              │
              ▼ (netlink 通信)
内核空间:  接收完整规则集
              │
              ▼
           解析、验证所有规则
              │
              ▼
        ┌─ 全部有效?──┐
        │              │
        ▼ 是           ▼ 否
    生成新规则集      回滚(原规则集不变)
        │
        ▼
    原子替换指针
  (RCU 机制:读取者不受影响)
        │
        ▼
    新规则立即生效
    旧规则延迟回收(RCU grace period)

4.3 原子更新的安全优势

优势说明
零空窗期新旧规则切换是瞬时指针替换
全有或全无规则文件中有任何语法错误,整个事务回滚
RCU 保护Read-Copy-Update 机制保护正在处理的数据包不受影响
可回滚保存旧规则集,随时可以恢复

五、从 iptables 迁移指南

5.1 官方翻译工具

nftables 提供了 iptables-translate 工具,将 iptables 命令翻译为等价的 nftables 命令:

# 翻译单条规则
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# 输出: nft add rule ip filter INPUT tcp dport 22 counter accept

# 翻译 NAT 规则
iptables-translate -t nat -A PREROUTING -p tcp --dport 8080 \
    -j DNAT --to-destination 192.168.1.100:80
# 输出: nft add rule ip nat PREROUTING tcp dport 8080 dnat to 192.168.1.100:80

# 批量翻译规则集
iptables-save | sed 's/^\[.*\]//' | iptables-restore-translate -f -

5.2 迁移步骤

步骤 1: 审计现有 iptables 规则
  ├── iptables-save > current_rules.v4
  ├── ip6tables-save > current_rules.v6
  └── 记录所有自定义脚本

步骤 2: 翻译规则
  ├── iptables-restore-translate -f current_rules.v4 > translated.nft
  ├── ip6tables-restore-translate -f current_rules.v6 >> translated.nft
  └── 检查翻译结果,手动优化

步骤 3: 优化规则结构
  ├── 将离散规则合并为集合/映射
  ├── 使用 inet family 统一 IPv4/IPv6
  └── 添加注释和文档

步骤 4: 测试环境验证
  ├── 在测试机器加载: nft -f translated.nft
  ├── 验证服务可达性
  └── 对比 iptables  nftables 规则效果

步骤 5: 生产环境切换
  ├── 备份当前 iptables 规则
  ├── 计划维护窗口
  ├── 停止 iptables 服务
  ├── 加载 nftables 规则: nft -f rules.nft
  └── 启用 nftables 服务: systemctl enable nftables

步骤 6: 持久化配置
  ├── 规则文件: /etc/nftables.conf (Debian/Ubuntu)
  ├──  /etc/sysconfig/nftables.conf (RHEL/Fedora)
  └── systemctl enable nftables

5.3 兼容性注意事项

注意事项说明
Docker 冲突Docker 仍使用 iptables,与 nftables 混用可能导致规则混乱
Fail2ban旧版 Fail2ban 使用 iptables,需升级或配置 nftables 后端
Kuberneteskube-proxy 默认使用 iptables,切换到 nftables 模式需验证
Libvirt虚拟机网络默认使用 iptables,需确认兼容性
内核版本推荐 >= 4.9(部分高级特性需要 >= 5.x)

5.4 共存策略

如果暂时无法完全切换到 nftables:

# 策略 1: iptables 为主,nftables 处理特殊需求
# 注意:iptables 先于 nftables 执行(默认 priority 均为 0)

# 策略 2: nftables 为主,iptables 仅用于兼容 Docker
# 使用 nftables 的 iptables 兼容层(内核选项 NF_TABLES_IPV4/IPV6)

# 策略 3: 分阶段替换
# ① 先替换 INPUT/OUTPUT 链 → nftables
# ② 再替换 NAT 规则 → nftables
# ③ 最后替换 FORWARD 链 → nftables

六、适用场景与注意事项

适用场景

场景说明
高性能环境nftables 是性能最优的 Linux 防火墙方案
大型规则集集合/映射将 O(n) 查找降为 O(1),大规模规则场景优势显著
双栈网络inet family 一条规则搞定 IPv4+IPv6
安全合规原子更新消除规则变更期间的安全空窗期
RHEL 8+ / Debian 10+新一代发行版的默认推荐方案
从 iptables 迁移官方提供 iptables-translate 翻译工具

注意事项

  1. Docker 兼容性:Docker 仍使用 iptables 管理容器网络,与 nftables 混用时需配置正确的后端和优先级
  2. 旧版工具兼容:Fail2ban、kube-proxy、Libvirt 等工具默认依赖 iptables
  3. 内核版本要求:推荐 >= 4.9(3.13 基本可用,但部分高级特性缺失)
  4. 学习成本:从 iptables 迁移需要学习新的语法体系(但更直观)
  5. 文件持久化路径因发行版而异:Debian/Ubuntu 用 /etc/nftables.conf,RHEL 用 /etc/sysconfig/nftables.conf

下一步:04 — firewalld 详解

04 — firewalld 详解

firewalld 是 RHEL/CentOS/Fedora 系列发行版默认的防火墙管理工具,提供基于 Zone(区域)和 Service(服务)的动态防火墙管理框架。与 iptables 直接操作底层规则不同,firewalld 通过抽象层为管理员提供更直观、更安全的配置方式。


一、Zone 信任级别模型

1.1 设计理念

firewalld 的核心设计理念是:网络信任级别决定防火墙策略。基于这一理念,firewalld 引入了 Zone 概念:

  • 每个 Zone 代表一个网络信任级别
  • 网络接口绑定到某个 Zone 后,自动继承该 Zone 的所有规则
  • 切换网络环境时,只需更改接口的 Zone 绑定,无需逐条修改规则
┌───────────────────────────────────────────────────────────────┐
                         firewalld                              
                                                                
   Zone: public (默认)           Zone: internal                 
   ┌─────────────────────┐     ┌─────────────────────┐         
    Interface: eth0           Interface: eth1              
    ──────────────────        ──────────────────           
    Service: ssh              Service: samba               
    Service: dhcpv6           Service: mdns                
    Port: 80/tcp              Service: ssh                 
    Port: 443/tcp             Port: 8080/tcp               
    Masquerade: no            Masquerade: no               
   └─────────────────────┘     └─────────────────────┘         
                                                                
   Zone: dmz                     Zone: drop                     
   ┌─────────────────────┐     ┌─────────────────────┐         
    Interface: eth2           (无接口绑定)                  
    ──────────────────                                     
    Service: http             所有入站流量丢弃              
    Service: https            无任何回应                    
   └─────────────────────┘     └─────────────────────┘         
                                                                
   ┌───────────────────────────────────────────────────────┐   
                 后端引擎                                     
        iptables (RHEL 7)  /  nftables (RHEL 8+)            
   └───────────────────────────────────────────────────────┘   
└───────────────────────────────────────────────────────────────┘

1.2 Zone 工作原理

数据包到达
    │
    ▼
┌─────────────────────────────────────┐
│ 1. 检查源地址绑定                    │  ← 最高优先级
│    (Source-based zone binding)      │
│    例: 来源 192.168.1.0/24 → internal│
├─────────────────────────────────────┤
│ 2. 检查接口绑定                      │  ← 次优先级
│    (Interface-based zone binding)   │
│    例: eth0 → public               │
├─────────────────────────────────────┤
│ 3. 使用默认 Zone                     │  ← 最低优先级
│    (Default zone)                   │
│    例: 默认 = public               │
└─────────────────────────────────────┘
    │
    ▼
应用该 Zone 的全部规则

1.3 Zone 信任等级总览

Zone信任级别默认行为适用场景默认允许的服务
trusted最高接受所有入站连接完全信任的网络(家庭内网)所有
home拒绝入站,仅允许特定服务家庭网络ssh, mdns, ipp-client, samba-client, dhcpv6-client
internal中高拒绝入站,仅允许特定服务内部网络(办公室 LAN)ssh, mdns, ipp-client, samba-client, dhcpv6-client
work中等拒绝入站,仅允许特定服务工作网络ssh, dhcpv6-client, ipp-client
public中低(默认拒绝所有入站公共网络(咖啡厅 WiFi、数据中心)ssh, dhcpv6-client
dmz拒绝入站,仅允许特定服务DMZ 隔离区(对外服务)ssh
external拒绝入站,开启伪装作为 NAT 网关的外网接口ssh(masquerade 开启)
block极低拒绝所有入站(ICMP 拒绝)极度不信任的网络
drop最低丢弃所有入站(无回应)攻击场景/最高安全要求

1.4 Zone 用途选择指南

你的网络环境是什么?

  ┌─ 家庭网络 ─────────────→ 使用 home
  ├─ 办公室/公司内网 ──────→ 使用 internal
  ├─ 咖啡厅/机场 ──────────→ 使用 public (默认)
  ├─ 对外提供服务的服务器 ──→ 使用 dmz
  ├─ 作为 NAT 网关的外网口 ─→ 使用 external
  ├─ 遭到攻击时的应急防护 ─→ 切换到 drop
  └─ 完全控制的隔离环境 ────→ 使用 trusted

1.5 查看和切换 Zone

# 查看当前默认 Zone
firewall-cmd --get-default-zone

# 查看所有 Zone
firewall-cmd --get-zones

# 查看 Zone 详细信息
firewall-cmd --zone=public --list-all

# 查看所有活跃 Zone
firewall-cmd --get-active-zones

# 查看接口所属 Zone
firewall-cmd --get-zone-of-interface=eth0

# 切换默认 Zone
firewall-cmd --set-default-zone=dmz

# 将接口绑定到指定 Zone
firewall-cmd --zone=internal --add-interface=eth1          # 运行时
firewall-cmd --zone=internal --add-interface=eth1 --permanent  # 永久

二、运行时配置 vs 永久配置

2.1 双重配置模式

firewalld 最显著的特性之一是运行时配置和永久配置的完全分离

┌─────────────────┐     --permanent     ┌─────────────────┐
│   运行时配置      │ ◄────────────────── │   永久配置        │
│   (内存存储)      │ ──────────────────► │   (磁盘存储)      │
│                  │   --runtime-to-     │                  │
│   立即生效        │    permanent        │   需 reload       │
│                  │                     │   才能生效         │
│   测试新规则      │                     │   持久化保存       │
│                  │                     │                  │
│  ┌──────────┐    │                     │                  │
│  │ 规则 A   │    │                     │                  │
│  │ 规则 B   │    │                     │                  │
│  └──────────┘    │                     │                  │
│       │          │                     │                  │
│       │ 重启     │                     │                  │
│       ▼          │                     │                  │
│   规则全部丢失!  │                     │                  │
└─────────────────┘                    └─────────────────┘
特性运行时配置永久配置
存储位置内存磁盘(/etc/firewalld/
生效时机立即生效reload 后生效
生命周期重启后丢失永久保留
修改命令firewall-cmd [操作]firewall-cmd [操作] --permanent
安全测试可以大胆试验需要 reload 才能生效

2.2 安全工作流最佳实践

# 场景:需要开放一个新的 Web 服务端口 8080

# 步骤 1: 仅修改运行时,立即测试
firewall-cmd --zone=public --add-port=8080/tcp

# 步骤 2: 从外部测试端口可达性
curl -I http://server-ip:8080

# 步骤 3a: 测试成功 —— 持久化变更
firewall-cmd --runtime-to-permanent

# 步骤 3b: 测试失败 —— 直接重载恢复
firewall-cmd --reload
# 运行时变更全部丢弃,系统恢复到原来的永久配置状态

# 步骤 3c: 如果只是个别规则有问题,单独回滚
firewall-cmd --remove-port=8080/tcp

2.3 关键操作命令

# 【只影响运行时】添加服务
firewall-cmd --add-service=http

# 【只影响永久配置】添加服务(不立即生效)
firewall-cmd --add-service=http --permanent

# 【推荐工作流】先测试,后持久化
firewall-cmd --add-service=http                           # 1. 运行时添加,立即测试
firewall-cmd --add-service=http --permanent               # 2. 测试通过,永久配置

# 重载永久配置(丢弃所有未保存的运行时变更)
firewall-cmd --reload

# 完全重载(重建整个规则集)
firewall-cmd --complete-reload

# 查看运行时和永久的差异
firewall-cmd --zone=public --list-all                      # 运行时
firewall-cmd --zone=public --list-all --permanent          # 永久

三、服务(Service)定义和自定义

3.1 服务配置文件

Service 将网络服务的端口、协议、内核模块等打包为单个配置,是 firewalld 管理便利性的核心:

<!-- /etc/firewalld/services/myapp.xml -->
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>MyApp</short>
  <description>My Application - Web UI + API</description>
  <port protocol="tcp" port="8080"/>
  <port protocol="tcp" port="8443"/>
  <port protocol="udp" port="9090"/>
  <helper name="tftp"/>
  <destination ipv4="192.168.1.0/24"/>
</service>

配置文件位置:

  • 系统预定义:/usr/lib/firewalld/services/
  • 用户自定义:/etc/firewalld/services/(优先级更高)

3.2 操作服务

# 列出所有可用服务
firewall-cmd --get-services

# 查看服务定义详情
firewall-cmd --info-service=ssh
firewall-cmd --info-service=http

# 添加服务到 Zone
firewall-cmd --zone=public --add-service=http              # 运行时
firewall-cmd --zone=public --add-service=http --permanent   # 永久

# 移除服务
firewall-cmd --zone=public --remove-service=http

# 查询服务是否已添加(返回 yes/no)
firewall-cmd --zone=public --query-service=http

3.3 创建自定义服务

# 方法 1: 从模板复制并修改
cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/mycustom.xml
vim /etc/firewalld/services/mycustom.xml

# 方法 2: 直接创建
cat > /etc/firewalld/services/myapp.xml << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>MyApp</short>
  <description>My Application - Web + API</description>
  <port protocol="tcp" port="8080"/>
  <port protocol="tcp" port="8443"/>
</service>
EOF

# 重新加载使新服务可用
firewall-cmd --reload

# 验证新服务
firewall-cmd --info-service=myapp

# 使用新服务
firewall-cmd --zone=public --add-service=myapp --permanent

3.4 直接操作端口

# 不通过服务定义,直接操作端口
firewall-cmd --zone=public --add-port=8080/tcp
firewall-cmd --zone=public --add-port=8000-9000/tcp  # 端口范围
firewall-cmd --zone=public --remove-port=8080/tcp

# 添加源地址到 Zone(该源的流量使用此 Zone 的规则)
firewall-cmd --zone=internal --add-source=192.168.1.0/24

四、富规则(Rich Rules)语法详解

4.1 什么是富规则

富规则用于表达超出简单"端口/服务放行"的复杂防火墙需求,被 firewalld 翻译为底层的 iptables/nftables 规则。

4.2 富规则结构

rule
  [family="ipv4|ipv6"]
  [source address="CIDR" [invert="true"]]
  [destination address="CIDR" [invert="true"]]
  [service name="service"]
  [port port="port" protocol="tcp|udp"]
  [protocol value="tcp|udp|icmp"]
  [icmp-block name="name"]
  [masquerade]
  [forward-port port="port" protocol="tcp|udp" to-port="port" to-addr="address"]
  [log [prefix="text"] [level="level"] [limit value="rate/duration"]]
  [audit]
  [accept|reject|drop|mark set="mark"]

4.3 富规则实战示例

# 【示例 1】仅允许特定 IP 访问 SSH(白名单)
firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="192.168.1.100"
  service name="ssh"
  accept
'

# 【示例 2】按 IP 限速 SSH(防暴力破解,每分钟最多 3 次)
firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4"
  service name="ssh"
  limit value="3/m"
  accept
'

# 【示例 3】白名单模式:仅允许特定 IP 段访问数据库
firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="10.0.0.0/8"
  port port="3306" protocol="tcp"
  accept
'

# 【示例 4】端口转发(外网 8080 → 内网 80)
firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4"
  forward-port port="8080" protocol="tcp" to-port="80" to-addr="192.168.1.100"
'

# 【示例 5】拒绝并记录日志
firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="203.0.113.50"
  log prefix="BLOCKED-IP: " level="warning" limit value="5/m"
  reject
'

# 【示例 6】限制 ICMP 速率(防 ping 洪水)
firewall-cmd --zone=public --add-rich-rule='
  rule protocol value="icmp"
  limit value="1/s"
  accept
'

# 查看和删除富规则
firewall-cmd --zone=public --list-rich-rules
firewall-cmd --zone=public --remove-rich-rule='rule family="ipv4" service name="ssh" accept'

输出解读(示例 2): 规则限定同一源 IP 每分钟最多发起 3 个新的 SSH 连接。第 4 个连接到达时将被拒绝。这比 iptables -m recent 语法更直观。

4.4 直接规则(Direct Rules)

当富规则无法满足需求时,可使用直接规则直接操作 iptables:

# 在 filter 表 INPUT 链最前面插入自定义规则
firewall-cmd --direct --add-rule ipv4 filter INPUT 1 \
    -p tcp --dport 2222 -s 192.168.1.0/24 -j ACCEPT

# NAT 规则
firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 \
    -p tcp --dport 8443 -j DNAT --to-destination 192.168.1.100:443

# 查看
firewall-cmd --direct --get-all-rules

# 持久化
firewall-cmd --direct --add-rule ... --permanent

注意事项: 不推荐在 firewalld 运行时直接使用 iptables 命令修改规则,因为 firewalld 重载时会覆盖手动规则。如需自定义 iptables 规则,应使用富规则或直接规则。


五、D-Bus API 集成

5.1 接口信息

firewalld 通过 D-Bus 接口暴露全部功能,使外部应用程序可以动态管理防火墙:

属性
服务名org.fedoraproject.FirewallD1
对象路径/org/fedoraproject/FirewallD1
接口org.fedoraproject.FirewallD1
配置接口org.fedoraproject.FirewallD1.config

5.2 应用场景

应用说明
Cockpit Web 管理Red Hat 的 Web 管理界面通过 D-Bus 与 firewalld 交互
NetworkManager 集成连接新 Wi-Fi 时自动切换 Zone
自动化脚本Python/Ansible 等工具直接通过 D-Bus API 操作
容器编排Docker/Podman 插件动态管理规则
# 通过 dbus-send 查询状态
dbus-send --system --print-reply \
    --dest=org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.freedesktop.DBus.Properties.Get \
    string:"org.fedoraproject.FirewallD1" string:"state"

# 获取默认 Zone
dbus-send --system --print-reply \
    --dest=org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.fedoraproject.FirewallD1.getDefaultZone

六、后端架构:iptables vs nftables

┌─────────────────────────────────┐
│          firewalld                │
│  (D-Bus 服务, 配置管理)           │
│                                   │
│  Zone 管理 → Service 管理 → 富规则 │
└──────────────┬──────────────────┘
               │
    ┌──────────┴──────────┐
    ▼                     ▼
┌───────────┐      ┌───────────┐
│ iptables  │      │ nftables  │
│ 后端       │      │ 后端       │
│ (RHEL 7)  │      │ (RHEL 8+) │
└─────┬─────┘      └─────┬─────┘
      │                  │
      ▼                  ▼
┌─────────────────────────────────┐
│     Linux 内核 (netfilter)       │
└─────────────────────────────────┘
# 查看当前后端
cat /etc/firewalld/firewalld.conf | grep -i backend

# 切换后端(编辑 /etc/firewalld/firewalld.conf)
# FirewallBackend=nftables
# systemctl restart firewalld
特性iptables 后端nftables 后端
RHEL/CentOS 7默认可用(需 backport)
RHEL/CentOS 8+已移除默认
性能标准更高
原子更新不支持支持
Docker 兼容良好需配置

七、适用场景与注意事项

适用场景

场景说明
RHEL/CentOS/Fedora 服务器系统默认集成,开箱即用
多网络环境切换Zone 模型天然适合笔记本在不同网络间切换
中等规模生产环境Zone + 富规则提供足够灵活性
需要 GUI 管理提供 firewall-config 图形界面
自动化运维(Ansible/Puppet)D-Bus API 和命令行均支持脚本化
NetworkManager 联动Wi-Fi 切换时自动切换 Zone

注意事项

  1. 切勿绕过 firewalld 直接操作 iptables:firewalld 重载时会覆盖手动规则
  2. 运行时 vs 永久分离:务必理解 --permanent 的含义,测试时只用运行时
  3. Docker 兼容性:Docker 直接操作 iptables,可能绕过 firewalld 的规则,需安装 docker-firewalld 或配置 nftables 后端
  4. 富规则 vs 直接规则:优先使用富规则,直接规则是最后手段
  5. 发行版限定:firewalld 主要面向 RHEL 系列,Ubuntu/Debian 默认使用 UFW

常用速查命令

# 状态与信息
firewall-cmd --state                      # 运行状态
firewall-cmd --get-default-zone           # 默认 Zone
firewall-cmd --get-active-zones           # 活跃 Zone
firewall-cmd --zone=public --list-all     # Zone 详情

# 服务管理
firewall-cmd --get-services               # 所有服务
firewall-cmd --add-service=http            # 添加服务(运行时)
firewall-cmd --add-service=http --permanent # 添加服务(永久)

# 端口管理
firewall-cmd --add-port=8080/tcp
firewall-cmd --remove-port=8080/tcp

# 富规则
firewall-cmd --list-rich-rules
firewall-cmd --add-rich-rule='...'

# 持久化
firewall-cmd --runtime-to-permanent       # 运行时同步到永久
firewall-cmd --reload                      # 重载永久配置

下一步:05 — UFW 详解

05 — UFW 详解

UFW(Uncomplicated Firewall)是 Ubuntu/Debian 系列发行版默认推荐的防火墙前端工具。它的设计哲学是"极致简单"——用最少的命令完成最常见的防火墙配置,同时保持底层 iptables 的全部威力。


一、设计哲学与定位

1.1 核心原则

UFW 的设计遵循三个核心原则:

  1. 极简语法:命令语义化,一看就懂 —— ufw allow 80 vs iptables -A INPUT -p tcp --dport 80 -j ACCEPT
  2. 安全默认值:默认拒绝所有入站、允许所有出站,用户只需声明要开放什么
  3. 零学习曲线:一条命令即可完成常见防火墙配置,适合个人用户和小型服务器

1.2 架构总览

┌─────────────────────────────────────────────────┐
│                    用户                           │
│              ufw allow 80/tcp                     │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│           ufw 前端 (Python 程序)                  │
│  - 解析用户命令                                   │
│  - 管理规则文件                                   │
│  - 生成 iptables 规则                            │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│              规则存储层                           │
│  /etc/ufw/user.rules   (用户规则)                │
│  /etc/ufw/before.rules (系统规则-在前面)          │
│  /etc/ufw/after.rules  (系统规则-在后面)          │
│  /lib/ufw/user.rules    (初始模板)               │
└─────────────────────┬───────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────┐
│          iptables (内核 netfilter)               │
│  - UFW 使用自定义链结构                           │
│  - ufw-before-input → ufw-user-input →            │
│    ufw-after-input → ufw-track-input              │
└─────────────────────────────────────────────────┘

1.3 适用与不适用场景

UFW 适合的场景:
  ✓ 个人桌面用户
  ✓ 小型 VPS 管理员
  ✓ 开发者(快速保护开发环境)
  ✓ 不想深入理解 iptables 的运维人员

UFW 不太适合的场景:
  ✗ 需要复杂的 NAT 规则
  ✗ 需要按数据包内容过滤(应用层防火墙)
  ✗ 大规模规则集管理(建议 firewalld 或 nftables)
  ✗ 需要 Zone 或动态策略切换

二、命令 → iptables 规则映射原理

2.1 UFW 自定义 iptables 链结构

UFW 在 iptables 中创建了一套复杂的链结构来实现其功能:

INPUT
    ↓
ufw-before-logging-input
    ↓
ufw-before-input        ← 系统规则(loopback、已建立连接等)
    ↓
ufw-reject-input        ← 处理 REJECT 动作
    ↓
ufw-track-input         ← 连接跟踪
    ↓
ufw-user-input          ← ★ 用户通过 ufw 命令添加的规则
    ↓
ufw-after-input         ← 系统规则(最后执行的规则)
    ↓
ufw-after-logging-input
    ↓
ufw-skip-to-policy-input
    ↓
DROP / ACCEPT(默认策略)

各链的职责:

链名用途用户是否应修改
ufw-before-input最前面的规则(如 loopback、已建立连接)高级用户可修改 /etc/ufw/before.rules
ufw-user-input用户通过 ufw 命令添加的规则间接修改(通过 ufw 命令)
ufw-after-input用户规则之后执行的规则高级用户可修改 /etc/ufw/after.rules
ufw-reject-input处理 REJECT 动作不建议手动修改

2.2 命令映射对照表

UFW 命令执行后,在 /etc/ufw/user.rules 中生成规则条目,最终由 iptables 执行:

UFW 命令等效 iptables 规则说明
ufw allow 22/tcpiptables -A ufw-user-input -p tcp --dport 22 -j ACCEPT开放端口
ufw deny 80/tcpiptables -A ufw-user-input -p tcp --dport 80 -j DROP拒绝端口(静默)
ufw reject 8080/tcpiptables -A ufw-user-input -p tcp --dport 8080 -j REJECT拒绝并回应
ufw limit 22/tcpiptables -A ufw-user-input -p tcp --dport 22 -m limit --limit 3/min -j ACCEPT限速规则
ufw allow from 192.168.1.100iptables -A ufw-user-input -s 192.168.1.100 -j ACCEPT允许特定 IP
ufw allow from 192.168.1.0/24 to any port 3306iptables -A ufw-user-input -s 192.168.1.0/24 -p tcp --dport 3306 -j ACCEPTIP + 端口限制
ufw default deny incomingiptables -P ufw-user-input DROP默认拒绝入站
ufw default allow outgoingiptables -P ufw-user-output ACCEPT默认允许出站
ufw allow proto tcp from any to any port 22iptables -A ufw-user-input -p tcp --dport 22 -j ACCEPT明确指定协议

2.3 查看 UFW 生成的 iptables 规则

# 查看 UFW 创建的 iptables 链
sudo iptables -L -n -v | grep ufw

# 查看用户规则链(你在 ufw 中添加的规则都在这里)
sudo iptables -L ufw-user-input -n -v

# 查看 before 链(系统规则)
sudo iptables -L ufw-before-input -n -v

# 查看 after 链
sudo iptables -L ufw-after-input -n -v

# 查看完整规则集
sudo ufw show raw
# 或直接读取规则文件
sudo cat /etc/ufw/user.rules

2.4 规则文件内部格式

*filter
:ufw-user-input - [0:0]
:ufw-user-output - [0:0]
:ufw-user-forward - [0:0]
...
### tuple ### allow tcp 22 0.0.0.0/0 any 0.0.0.0/0 in
-A ufw-user-input -p tcp --dport 22 -j ACCEPT
### tuple ### allow tcp 80,443 0.0.0.0/0 any 0.0.0.0/0 in
-A ufw-user-input -p tcp -m multiport --dports 80,443 -j ACCEPT
...
COMMIT

关键注意: ### tuple ### 注释行是 UFW 用来追踪规则的元数据,不要手动删除这些注释,否则 UFW 无法正确管理规则。


三、应用配置文件机制

3.1 配置文件位置与格式

UFW 的应用配置文件使你可以用应用名称代替端口号来管理防火墙:

路径用途优先级
/etc/ufw/applications.d/用户自定义应用配置高(覆盖系统配置)
/usr/share/ufw/applications.d/系统预定义应用配置
# /etc/ufw/applications.d/myapp
[MyApp]
title=My Custom Web Application
description=A custom web app with web UI and API
ports=8080/tcp|8443/tcp

[MyApp-Full]
title=My Custom Web Application (Full)
description=Web UI, API, and Database
ports=8080/tcp|8443/tcp|3306/tcp

[MyApp-Secure]
title=My Custom Web Application (HTTPS Only)
description=Secure web access only
ports=8443/tcp

关键语法:

  • [ProfileName] —— 配置文件名(用于 ufw allow 命令)
  • title= —— 简短标题
  • description= —— 详细说明
  • ports= —— 端口/协议定义,多个用 | 分隔

3.2 查看和使用应用

# 列出所有可用应用配置
ufw app list

# 查看应用配置详情
ufw app info 'Nginx Full'
ufw app info OpenSSH

# 按应用名开放端口
ufw allow 'Nginx Full'    # 同时开放 80 和 443
ufw allow OpenSSH          # 开放 22

3.3 常见预定义应用

应用名包含端口说明
OpenSSH22/tcpSSH 服务
Nginx HTTP80/tcpNginx HTTP
Nginx HTTPS443/tcpNginx HTTPS
Nginx Full80,443/tcpNginx HTTP + HTTPS
Apache Full80,443/tcpApache HTTP + HTTPS
Samba137,138/udp, 139,445/tcp文件共享
CUPS631/tcp, 631/udp打印服务
Postfix25,465,587/tcp邮件服务器

3.4 创建自定义应用

# 创建自定义应用配置
sudo tee /etc/ufw/applications.d/myapp << 'EOF'
[MyApp]
title=My Custom Application
description=Web UI on 8080, API on 9090
ports=8080/tcp|9090/tcp
EOF

# 更新应用列表
sudo ufw app update MyApp

# 查看新应用
sudo ufw app info MyApp

# 使用新应用
sudo ufw allow MyApp

四、日志和报告

4.1 日志配置

# 开启日志
sudo ufw logging on

# 日志级别
sudo ufw logging low      # 仅记录被阻止的包(默认)
sudo ufw logging medium   # 记录阻止+允许的包(含速率限制)
sudo ufw logging high     # 记录所有包(含速率限制)
sudo ufw logging full     # 记录所有包(无速率限制,可能产生大量日志)

# 关闭日志
sudo ufw logging off

# 查看日志状态
sudo ufw status verbose

4.2 日志位置

# 查看 UFW 日志(rsyslog 系统)
sudo tail -f /var/log/ufw.log

# 或通过 journalctl
sudo journalctl -u ufw -f

# 或查看 kern.log
sudo grep UFW /var/log/kern.log | tail -20

4.3 日志条目解读

[UFW BLOCK] IN=eth0 OUT= MAC=00:11:22:33:44:55:66:77:88:99:aa:bb:08:00
SRC=203.0.113.50 DST=192.168.1.10 LEN=60 TOS=0x00 PREC=0x00 TTL=47
PROTO=TCP SPT=45678 DPT=22 WINDOW=29200 RES=0x00 SYN URGP=0

日志字段解读表:

字段含义示例值含义
[UFW BLOCK]动作类型此包被阻止
SRC=源 IP203.0.113.50(攻击来源)
DST=目标 IP192.168.1.10(你的服务器)
DPT=目标端口22(攻击者试图连接 SSH)
SPT=源端口45678(攻击者发起连接时使用的随机端口)
PROTO=协议TCP

4.4 状态查看命令

sudo ufw status                  # 基本状态
sudo ufw status verbose          # 详细状态(含日志设置和默认策略)
sudo ufw status numbered         # 编号状态(每条规则带编号,便于删除)
sudo ufw show raw                # 查看底层 iptables 规则
sudo ufw show added              # 查看添加的规则报告
sudo ufw show listening          # 查看监听端口报告

ufw status numbered 输出示例与解读:

Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 22/tcp                     LIMIT IN    Anywhere       ← SSH 限速
[ 2] 80/tcp                     ALLOW IN    Anywhere       ← HTTP 开放
[ 3] 443/tcp                    ALLOW IN    Anywhere       ← HTTPS 开放
[ 4] 3306/tcp                   ALLOW IN    192.168.1.0/24 ← MySQL 仅内网访问

删除规则:sudo ufw delete 3(删除编号 3 的规则)


五、与 Docker 共存时的注意事项

5.1 核心问题

Docker 直接操作 iptables,绕过 UFW。这是 UFW 用户最容易踩的坑:

正常期望:
  客户端 → UFW 拒绝 → 无法访问

实际情况(Docker 运行时):
  客户端 → Docker iptables 规则(在 UFW 之前执行)→ 直接放行!

原因: Docker 在 DOCKER-USER 链中以优先级更高的方式插入规则,这些规则在 UFW 的 ufw-user-input 链之前执行。如果你用 -p 8080:8080 暴露端口,即使 UFW 拒绝了 8080,外部仍可直接访问。

5.2 解决方案

方案 1:绑定到 localhost(推荐,最安全)
# 启动容器时只绑定到本地回环接口
docker run -p 127.0.0.1:8080:8080 myapp

# 效果:端口仅能从本机访问,完全不经过 iptables 的外部放行
# 如果外部需要访问,通过 Nginx 反向代理 + UFW 允许 80/443
方案 2:使用 DOCKER-USER 链
# Docker 提供的 DOCKER-USER 链供用户添加规则
# 这个链在 Docker 自动规则之后执行,可以覆盖 Docker 的行为
sudo iptables -I DOCKER-USER 1 -p tcp --dport 8080 -j DROP

# 更精确:只允许特定 IP 访问
sudo iptables -I DOCKER-USER 1 -p tcp --dport 8080 \
    ! -s 192.168.1.0/24 -j DROP

# 持久化此规则:编辑 /etc/ufw/after.rules,在 COMMIT 之前添加
# -A DOCKER-USER -p tcp --dport 8080 ! -s 192.168.1.0/24 -j DROP
方案 3:禁用 Docker 的 iptables 操作
// /etc/docker/daemon.json
{
  "iptables": false
}

警告: 禁用后需手动管理所有 Docker 网络规则(容器间通信、端口映射等),维护成本高。

5.3 方案对比

方案难度安全级别维护成本推荐场景
绑定 localhost极低最高极低所有场景(首选)
DOCKER-USER 链必须暴露端口到外网时
禁用 Docker iptables中等很高仅限高级用户

5.4 Docker Compose 安全配置

version: '3.8'
services:
  web:
    image: nginx:latest
    ports:
      # 安全:仅绑定到 localhost,通过反向代理访问
      - "127.0.0.1:8080:80"
    # 危险(会绕过 UFW):
    # - "8080:80"

  db:
    image: mysql:8
    # 不暴露端口到宿主机(仅容器间通信)
    expose:
      - "3306"
    networks:
      - internal

networks:
  internal:
    driver: bridge

六、适用场景与注意事项

适用场景

场景说明
Ubuntu/Debian 桌面和服务器系统默认推荐,开箱即用
小型 VPS(单一管理员)极简操作,一条命令搞定
开发环境快速加固无需深入理解 iptables 即可保护开发机
学习防火墙的入门工具通过 UFW 理解防火墙概念后再深入 iptables
SSH + Web 服务的基本防护防 SSH 暴力破解、开放 HTTP/HTTPS 端口

注意事项

  1. 默认策略是 DENY INCOMING:启用 UFW 后,所有入站连接被阻止,务必先 ufw allow ssh
  2. Docker 绕过 UFW:使用 -p 8080:8080 暴露端口的容器可能直接暴露到公网
  3. ufw enable 后立即生效:如果 SSH 规则未先配置,远程连接会断开
  4. 规则顺序很重要ufw status numbered 查看规则编号,编号越小优先级越高
  5. limited 规则的局限性:无法实现更复杂的速率限制(如 recent 模块的 --hitcount),如需高级限速请直接使用 iptables
  6. 不适用于大规模规则集:超过 20-30 条规则后管理开始不便,建议切换 firewalld 或 nftables

常用速查命令

# 基础操作
sudo ufw enable                   # 启用(同时设置开机自启)
sudo ufw disable                  # 禁用
sudo ufw reset                    # 重置所有规则
sudo ufw reload                   # 重载配置

# 规则管理
sudo ufw allow 22/tcp             # 开放端口
sudo ufw allow 80,443/tcp         # 开放多个端口
sudo ufw deny 8080/tcp            # 拒绝端口(静默丢弃)
sudo ufw reject 8080/tcp          # 拒绝端口(回应 ICMP/TCP RST)
sudo ufw limit 22/tcp             # 限速(3/min,防暴力破解)
sudo ufw delete allow 80/tcp      # 删除规则
sudo ufw delete 3                 # 按编号删除

# IP 级规则
sudo ufw allow from 192.168.1.100                    # 允许特定 IP 访问所有端口
sudo ufw allow from 192.168.1.0/24 to any port 3306  # 允许 IP 段访问特定端口
sudo ufw deny from 203.0.113.50                      # 拒绝特定 IP

# 默认策略
sudo ufw default deny incoming     # 默认拒绝入站(推荐)
sudo ufw default allow outgoing    # 默认允许出站(推荐)

# 应用配置
sudo ufw app list                  # 查看可用应用
sudo ufw app info 'Nginx Full'     # 查看应用详情
sudo ufw allow 'Nginx Full'        # 按应用名开放

下一步:06 — 方案选择决策指南

06 — 方案选择决策指南

本文是防火墙配置与管理教程的终点站:帮助你根据实际场景,在 iptables、nftables、firewalld、UFW 四种方案中做出最优选择。


一、核心决策流程图

                    需要 Linux 防火墙方案
                              │
              ┌───────────────┼───────────────┐
              │               │               │
              ▼               ▼               ▼
         你是个人用户?    你是运维工程师?   你是架构师?
              │               │               │
              ▼               ▼               ▼
         使用什么发行版?   管理什么规模?    性能要求多高?
              │               │               │
    ┌────┐    │    ┌────┐      │      ┌────┐    │
    │Ubuntu│  │    │RHEL│      │      │通用│    │
    │Debian│  │    │系列│      │      │    │    │
    └──┬──┘  │    └──┬┘      │      └──┬┘    │
       │     │       │        │         │     │
       ▼     │       ▼        │         ▼     │
   ┌──────┐  │  ┌────────┐    │    ┌────────┐ │
   │ UFW  │◄─┘  │firewalld│   │    │nftables │ │
   │(→05) │     │(→04)   │    │    │(→03)   │ │
   └──────┘     └────────┘    │    └────────┘ │
       │             │        │         │     │
       ▼             ▼        ▼         ▼     ▼
   需要更灵活?   需要更灵活? 中小规模? 高流量? 容器环境?
       │             │        │         │     │
    ┌──────┐    ┌──────┐   ┌──────┐ ┌──────┐┌──────┐
    │iptab │    │nftab │   │firewa│ │nftab ││iptab │
    │les   │    │les   │   │lld   │ │les   ││les   │
    │(→02) │    │(→03) │   │(→04) │ │(→03) ││(→02) │
    └──────┘    └──────┘   └──────┘ └──────┘└──────┘

注:箭头后的数字(如 → 02)对应本教程的文档编号。


二、全方案对比矩阵(4 方案 x 15 维度)

2.1 综合评分表

维度iptablesnftablesfirewalldUFW
首次发布2001 (Linux 2.4)2014 (Linux 3.13)2011 (RHEL 7)2008 (Ubuntu 8.04)
内核后端xtables(遗留框架)nf_tables(新一代)iptables 或 nftablesiptables
用户空间工具iptables/ip6tables/arptables/ebtables(4 个)单一 nft 命令firewall-cmdufw
配置模型过程式(命令序列)声明式 + 过程式声明式(Zone 模型)声明式(极简语法)
细粒度控制强(四表五链直接操作)强(自定义表/链/集合)中(需富规则)弱(有限)
规则更新方式逐条操作,非原子事务性原子更新部分支持间接支持
规则查找速度O(n) 线性O(1) 集合/映射等于后端(iptables/nftables)等于 iptables
IPv4/IPv6 管理分开管理统一管理(inet family)统一管理统一管理
学习曲线高(陡峭)中(较直观)低(Zone 直观)极低(一条命令)
配置复杂度高(手动编写长命令)中(结构化语法)低(命令抽象)极低(最简命令)
动态规则不支持支持支持(运行时/永久分离)间接支持
性能(高流量)高(JIT 编译 + 集合)中(等于后端)中(等于 iptables)
内核版本要求>= 2.4>= 3.13(推荐 >= 4.9)>= 3.x>= 2.6
主流发行版通用(逐渐被替代)RHEL 8+, Debian 10+, openSUSE 15+RHEL/CentOS/FedoraUbuntu/Debian
Docker 兼容性良好(Docker 原生)需谨慎配置需配置需配置(Docker 绕过 UFW)

2.2 维度详解

细粒度控制能力
级别工具能力说明
iptables四表五链直接操作,支持大量匹配扩展模块(conntrack/tcp/recent/limit 等)
nftables自定义表/链/集合/映射,语法更灵活,一套规则处理 IPv4+IPv6
firewalld富规则可覆盖大多数场景,极端需求需直接规则(操作底层 iptables)
UFW仅覆盖常见端口/IP 放行/拒绝/限速场景,不支持高级 NAT/标记等
性能等级(大规模规则集场景)
级别工具100 条规则下的吞吐量(64 字节包)说明
nftables~1.2M ppsJIT 编译 + 集合 O(1) 查找
iptables~800K ppsO(n) 线性查找,ESTABLISHED 规则可减少实际匹配
firewalld取决于后端(iptables 或 nftables)富规则有一定翻译开销
UFW~800K pps(等于 iptables)自定义链结构增加少量开销
学习曲线
级别工具预估上手时间说明
极低UFW5 分钟ufw allow 80 即可
firewalld30 分钟Zone 概念直观,firewall-cmd 命令简单
nftables2 小时语法比 iptables 直观,但需要理解新概念(set/map/family)
iptables1 天需要理解四表五链、匹配模块、目标动作、规则顺序等

三、场景 → 推荐方案速查表(决策矩阵)

场景关键词第一推荐第二推荐备注
Ubuntu/Debian 桌面UFWiptablesUFW 开箱即用,一条命令保护系统
Ubuntu/Debian 小型 VPSUFWiptables个人 VPS 通常只需开放 SSH + Web 端口
Fedora/RHEL 桌面firewalldnftablesfirewalld 系统默认集成
RHEL/CentOS 7 服务器firewalldiptablesfirewalld 是 RHEL 7 默认,iptables 后端
RHEL/CentOS 8/9 服务器firewalld + nftables 后端nftables(直接)RHEL 8+ 默认 nftables 后端
中型生产环境(需精细控制)firewalld(富规则)nftablesZone 模型 + 富规则满足大部分需求
大型/高流量环境(性能敏感)nftablesiptables集合 O(1) 查找 + JIT 编译,性能最优
Docker/K8s 宿主机iptablesnftables(谨慎)Docker 原生操作 iptables,兼容性最好
NAT 网关/路由器iptablesnftablesfirewalld需要细致的 DNAT/SNAT/MASQUERADE 控制
安全合规要求高nftablesfirewalldnftables 原子更新消除规则变更空窗期
嵌入式/Linux From Scratchiptablesnftablesiptables 依赖最少,内核版本要求最低
Fail2ban/第三方工具联动iptablesnftables(新版支持)fail2ban 默认使用 iptables 封禁
学习 Linux 防火墙iptablesnftables理解底层原理后,上层工具才用得明白
多网络环境切换(笔记本)firewalldUFWZone 切换 + NetworkManager 联动

四、技术演进路线

iptables (2001) ─────────────────────────────────────────► 遗留但广泛部署
     │
     ├──► UFW (2008) ──────────────► 将 iptables 封装为极简接口
     │
     ├──► firewalld (2011) ────────► 将 iptables 封装为 Zone 模型
     │                                    │
     │                                    └──► RHEL 8+ 切换到 nftables 后端
     │
     └──► nftables (2014) ────────► 内核层面的彻底重构,替代 iptables
              │
              └──► 未来趋势 ──────► 逐步成为所有发行版的默认方案

五、常见误区与反模式

误区 1:同时运行多个防火墙工具

错误表现:

# 同时启用 firewalld 和手动 iptables 规则
systemctl start firewalld
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT   # 手动规则

后果:

  • firewalld 重载时会覆盖手动添加的 iptables 规则
  • 两个工具各自管理同一组 netfilter Hook 点,规则互相覆盖
  • 安全状态不可预测

正确做法:

# 只使用一个防火墙管理工具
# 如果使用 firewalld,所有规则通过 firewall-cmd 添加
# 如果直接使用 iptables/nftables,先停掉 firewalld
systemctl stop firewalld
systemctl disable firewalld
systemctl mask firewalld

误区 2:Docker 端口映射绕过 UFW

错误表现:

sudo ufw enable
sudo ufw deny 8080
docker run -p 8080:80 nginx     # 8080 端口仍可从外部访问!

原因: Docker 的 iptables 规则在 UFW 规则之前执行,端口映射直接暴露到公网。

正确做法:

# 绑定到 localhost
docker run -p 127.0.0.1:8080:80 nginx
# 或通过 DOCKER-USER 链添加过滤规则

误区 3:iptables -P INPUT DROP 在远程 SSH 会话中执行

错误表现:

# SSH 连接到服务器后执行
iptables -P INPUT DROP
# 连接立即断开!所有 SSH 流量被丢弃

后果: 被锁在服务器外面,需要物理访问或控制台恢复。

正确做法:

# 先添加允许 SSH 的规则
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 再设置默认策略
iptables -P INPUT DROP

误区 4:firewalld 的 --permanent 以为会立即生效

错误表现:

firewall-cmd --add-service=http --permanent
# 以为 HTTP 服务已经在防火墙上开放了
# 实际上还没有!需要 reload

正确做法:

# 方案 A:先运行时添加(立即生效),再永久保存
firewall-cmd --add-service=http                  # 立即生效
firewall-cmd --add-service=http --permanent      # 永久保存
# 或
firewall-cmd --runtime-to-permanent              # 同步所有运行时到永久

# 方案 B:修改永久配置后 reload
firewall-cmd --add-service=http --permanent
firewall-cmd --reload                            # 使永久配置生效

误区 5:nftables 和 iptables 的规则可以简单混用

错误认知: 以为可以在 nftables 规则的基础上再添加几条 iptables 规则,两者"各管各的"。

实际情况: nftables 和 iptables 都向 netfilter 的同一个 Hook 点注册回调。默认 priority 相同的情况下,执行顺序由内核模块加载顺序决定,结果不可预测。

正确做法:

  • 使用一种工具作为主防火墙
  • 如果必须混用(如 Docker 依赖 iptables),通过 priority 值明确控制执行顺序
  • 使用 nftables 的 iptables 兼容层(内核选项 NF_TABLES_IPV4/NF_TABLES_IPV6

误区 6:UFW 的 limit 规则可以替代专业限速

错误表现:

sudo ufw limit 22/tcp
# 以为这提供了完整的 SSH 防护

现实: UFW 的 limit 规则使用 iptables 的 -m limit 模块,是一个基于全局速率的限制(每分钟 3 次,面向所有 IP 汇总),而不是每 IP 限制。一个攻击者可以耗尽全局限额,导致合法用户无法连接。

需要每 IP 限制时: 直接使用 iptables 的 -m recent 模块,或切换到更专业的限速方案。


六、终极速查卡 Cheat Sheet

操作iptablesnftablesfirewalldUFW
安装系统自带apt install nftablesyum/dnf install firewalldapt install ufw
查看规则iptables -L -n -vnft list rulesetfirewall-cmd --list-allufw status verbose
允许端口iptables -A INPUT -p tcp --dport 80 -j ACCEPTnft add rule inet filter input tcp dport 80 acceptfirewall-cmd --add-port=80/tcpufw allow 80/tcp
拒绝端口iptables -A INPUT -p tcp --dport 80 -j DROPnft add rule inet filter input tcp dport 80 dropfirewall-cmd --remove-port=80/tcpufw deny 80/tcp
允许已建立连接iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTnft add rule inet filter input ct state established,related accept自动处理自动处理
默认拒绝入站iptables -P INPUT DROPnft add chain ... { policy drop; }firewall-cmd --set-default-zone=dropufw default deny incoming
保存规则iptables-save > rules.v4nft list ruleset > rules.nftfirewall-cmd --runtime-to-permanent自动保存
恢复规则iptables-restore < rules.v4nft -f rules.nftfirewall-cmd --reloadufw reload
重置/清空iptables -F; iptables -Xnft flush rulesetfirewall-cmd --complete-reloadufw reset
持久化iptables-persistent 或 systemd 服务/etc/nftables.conf + systemctl enable nftables--permanent 标志自动持久化
允许特定 IPiptables -A INPUT -s 192.168.1.100 -j ACCEPTnft add rule inet filter input ip saddr 192.168.1.100 acceptfirewall-cmd --add-rich-rule='rule source address="192.168.1.100" accept'ufw allow from 192.168.1.100
端口转发iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80nft add rule inet nat prerouting tcp dport 8080 dnat to 192.168.1.100:80富规则 forward-port不支持(需直接 iptables)
限速iptables -A INPUT -m limit --limit 3/min -j ACCEPTnft add rule inet filter input limit rate 3/minute accept富规则 limit value="3/m"ufw limit 22/tcp
日志记录iptables -A INPUT -j LOG --log-prefix "DROP: "nft add rule inet filter input log prefix "DROP: "富规则 log prefix="..."ufw logging on
查看状态iptables -L -n -vnft list rulesetfirewall-cmd --stateufw status

七、总结:选型核心原则

记住这五条金科玉律:

  1. 桌面/个人 VPS → UFW 或 firewalld。Ubuntu/Debian 用 UFW,Fedora/RHEL 用 firewalld,开箱即用,学习成本最低。

  2. 生产服务器(RHEL 系列)→ firewalld。Zone 模型适合多网络环境,运行时/永久配置分离提供安全的变更工作流,D-Bus API 支持自动化运维。

  3. 高流量/高性能 → nftables。集合 O(1) 查找 + JIT 编译 + 原子更新,是 Linux 防火墙的未来标准。如果你的发行版支持(>= RHEL 8 / Debian 10),应优先考虑。

  4. Docker/K8s 宿主机 → iptables。Docker 和 Kubernetes 的 CNI 插件深度依赖 iptables。虽然 nftables 提供了兼容层,但 iptables 仍是容器生态最稳定的选择。

  5. 学习目的 → 先 iptables 再 nftables。iptables 是理解 Linux 防火墙机制的基石(四表五链、匹配模块、conntrack),掌握了它再去学 nftables 会事半功倍。

最后忠告:

一个系统只需要一套防火墙管理方案。 不要让 iptables 和 firewalld 并存,不要让 UFW 和 nftables 竞争同一个 Hook 点。选好一个,关掉其他的,确保防火墙规则可预测、可审计。


教程系列完结。本教程共 6 篇文档,从 netfilter 内核原理到四种防火墙方案的深入讲解,再到方案选择决策指南,覆盖了 Linux 下所有主流防火墙配置方案。建议按需查阅,结合实际场景选择最合适的方案。


返回:README 教程首页