开放一个端口、封禁一个 IP、只允许内网访问数据库------这些需求和防火墙打交道。Linux 防火墙工具层层叠叠:iptables、firewalld、nftables、ufw,新手容易懵:到底用哪个?它们谁管谁?这一篇把四代防火墙的关系理清,重点讲透 iptables 的表/链/规则模型,再给出 firewalld 和 ufw 的实战配置,最后深入 Netfilter 原理。
一、四代防火墙的关系
先画一张家谱,避免混乱:
text
内核层:Netfilter(包过滤框架,所有工具最终都调用它)
↑
├─ iptables(老牌命令行工具,直接操作 Netfilter 规则)
├─ nftables(新一代,取代 iptables,内核 3.13+)
├─ firewalld(CentOS/RHEL 的守护进程,底层用 nftables/iptables,用「区域」抽象)
└─ ufw(Ubuntu 的简易前端,底层调 iptables)
| 工具 | 定位 | 系统 |
|---|---|---|
| Netfilter | 内核框架 | 所有 |
iptables |
直接操作规则 | 通用 |
nftables |
iptables 的替代品 | 新内核 |
firewalld |
动态管理 + 区域抽象 | RHEL/CentOS/Rocky |
ufw |
简易前端 | Ubuntu/Debian |
选择建议 :Ubuntu 用 ufw 最省事;RHEL/CentOS/Rocky 用 firewalld;需要精细控制用 iptables/nftables。它们不要同时用,否则规则互相打架。
二、iptables 基础:表、链、规则
iptables 的模型是「表 → 链 → 规则」,理解了这层结构,一切就顺了。
四张表(按用途分)
| 表 | 用途 |
|---|---|
filter |
默认表,过滤包(放行/拒绝) |
nat |
网络地址转换(端口转发、SNAT/DNAT) |
mangle |
修改包内容(TOS、TTL 等) |
raw |
连接跟踪前处理 |
日常 90% 都在 filter 表操作,默认就是它。
五条链(按包处理的时机)
| 链 | 触发时机 |
|---|---|
INPUT |
进入本机的包 |
OUTPUT |
本机发出的包 |
FORWARD |
经过本机转发的包 |
PREROUTING |
路由决策前(nat 常用) |
POSTROUTING |
路由决策后(nat 常用) |
filter 表里有 INPUT、OUTPUT、FORWARD 三条链,这是最常打交道的。
查看规则
bash
sudo iptables -L -n -v --line-numbers
text
Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
2 DROP all -- 10.0.0.99 0.0.0.0/0
-L列出,-n不解析(显示数字),-v详细,--line-numbers显示行号
规则的基本操作
bash
# 追加规则(加到末尾)
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# 插入到第 1 条(最前面)
sudo iptables -I INPUT 1 -p tcp --dport 443 -j ACCEPT
# 删除第 3 条规则
sudo iptables -D INPUT 3
# 清空所有规则
sudo iptables -F
# 设置默认策略(先放行,再改 DROP,别锁死自己)
sudo iptables -P INPUT DROP
参数速查
| 参数 | 含义 |
|---|---|
-A/-I/-D |
追加/插入/删除 |
-p |
协议(tcp/udp/icmp) |
--dport / --sport |
目标/源端口 |
-s / -d |
源/目标 IP |
-i / -o |
入/出网卡 |
-j |
动作(ACCEPT/DROP/REJECT) |
-m state --state |
连接状态匹配 |
-j 的动作区别
| 动作 | 行为 |
|---|---|
ACCEPT |
放行 |
DROP |
丢弃(不回应,对方超时) |
REJECT |
拒绝(回一个拒绝包,对方秒知) |
LOG |
记录日志后继续匹配 |
DROP vs REJECT:DROP 静默丢弃,对方要等到超时;REJECT 主动拒绝,对方立即收到错误。安全上倾向 DROP(不暴露信息),体验上 REJECT 更快。
三、iptables 常用规则
放行 SSH、HTTP、HTTPS
bash
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
放行已建立的连接(关键)
bash
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
这条必须加,否则出站的响应包进不来,会导致「能发不能收」。
只允许内网访问数据库
bash
sudo iptables -A INPUT -p tcp --dport 3306 -s 10.0.0.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 3306 -j DROP
只允许特定 IP 访问 SSH
bash
sudo iptables -A INPUT -p tcp --dport 22 -s 203.0.113.5 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j DROP
封禁某 IP
bash
sudo iptables -A INPUT -s 10.0.0.99 -j DROP
允许本机回环和 ICMP
bash
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -p icmp -j ACCEPT
完整的一套基础规则(放最后 DROP)
bash
# 清空
sudo iptables -F
# 默认策略
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT
# 放行回环
sudo iptables -A INPUT -i lo -j ACCEPT
# 放行已建立连接
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 放行常用端口
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 放行 ICMP
sudo iptables -A INPUT -p icmp -j ACCEPT
⚠️ 改防火墙的保命姿势
远程改 iptables 最怕把自己锁死。 两个技巧:
bash
# 1. 加一条定时任务,5 分钟后清空规则(救命稻草)
sudo sh -c 'echo "iptables -F" | at now + 5 minutes'
# 2. 确认规则无误后,再取消这个定时任务
或者用 iptables-apply(部分发行版提供),它会在超时后自动回滚。
四、firewalld:区域与服务的抽象
firewalld 是 RHEL/CentOS/Rocky 7+ 的默认防火墙,用「区域(zone)」和「服务(service)」的概念,比裸 iptables 友好。
核心概念
- 区域(zone):一套信任级别的规则集。不同网卡可绑不同区域。
- 服务(service) :预定义的一组端口,如
http= 80/tcp。 - 运行时 vs 永久 :
--permanent的规则才持久,改完要reload。
常用区域
| 区域 | 信任级别 |
|---|---|
trusted |
全放行 |
public |
默认,只放行明示的服务 |
internal |
内网,较信任 |
drop |
丢弃所有入站 |
常用命令
bash
# 查看状态
sudo firewall-cmd --state
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --list-all
# 放行服务(永久)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
# 放行端口
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
# 删除规则
sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reload
# 限制源 IP 访问某服务
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload
⚠️ 运行时与永久的区别
bash
# 这条立即生效,但重启后丢失
sudo firewall-cmd --add-port=8080/tcp
# 这条永久保存,但需要 reload 才生效
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
图省事就都加 --permanent + reload,避免「重启后规则没了」。
五、ufw:Ubuntu 的简易防火墙
ufw(Uncomplicated Firewall)是 Ubuntu/Debian 上的简易前端,命令极简,适合日常。
基本操作
bash
# 查看状态
sudo ufw status verbose
# 启用(先放行 SSH,否则断连)
sudo ufw allow 22/tcp
sudo ufw enable
# 放行服务
sudo ufw allow http
sudo ufw allow https
# 放行端口
sudo ufw allow 8080/tcp
# 允许某 IP 访问某端口
sudo ufw allow from 10.0.0.5 to any port 3306
# 拒绝
sudo ufw deny 23/tcp
# 删除规则
sudo ufw delete allow 8080/tcp
# 关闭
sudo ufw disable
# 重置
sudo ufw reset
默认策略
bash
sudo ufw default deny incoming # 默认拒绝入站
sudo ufw default allow outgoing # 默认允许出站
ufw enable 之前务必先 allow 22/tcp,否则 SSH 一断你就进不去了。
六、nftables:新一代
nftables 取代 iptables,语法更统一(一张表管所有),性能更好。新系统上 iptables 命令其实往往是 nftables 的兼容包装。
基本用法
bash
# 查看规则
sudo nft list ruleset
# 创建表和链
sudo nft add table inet filter
sudo nft add chain inet filter input '{ type filter hook input priority 0; policy drop; }'
# 添加规则
sudo nft add rule inet filter input tcp dport 22 accept
sudo nft add rule inet filter input tcp dport { 80, 443 } accept
sudo nft add rule inet filter input ct state established,related accept
nftables 语法比 iptables 更接近自然语言,且一套语法同时处理 IPv4/IPv6 ,无需 iptables/ip6tables 两套。
七、持久化:规则保存
iptables 规则默认重启即失,必须持久化:
bash
# Debian/Ubuntu
sudo apt install iptables-persistent
sudo netfilter-persistent save
sudo netfilter-persistent reload
# CentOS/RHEL
sudo service iptables save
# 手动方式
sudo iptables-save > /etc/iptables/rules.v4
sudo iptables-restore < /etc/iptables/rules.v4
firewalld 和 ufw 自带持久化,不用额外处理。
八、⚠️ 常见错误
- 改 iptables 把自己锁死 。远程改之前先放行 SSH,或用
at定时任务加救命回滚。 - 忘了放行 ESTABLISHED,RELATED。导致「能连出去但收不到响应」。
- DROP 和 REJECT 混用不当。排查困难时先用 REJECT 看是否有包到达。
- firewalld 忘加
--permanent。以为放行了,重启后发现规则没了。 - 同时用 firewalld 和 iptables 。规则互相覆盖,行为诡异。选一个用。
- 默认策略改 DROP 前没放行 SSH。等价于切断自己的远程通道。
- 规则顺序错误 。iptables 从上往下匹配,匹配到就执行,宽泛的规则放前面会覆盖后面精确的规则。
- 容器/云环境还要看安全组。云服务器有「安全组」这一层,Netfilter 放行了不代表能访问,安全组也要放行。
九、原理探究:Netfilter 与包的处理流程
所有 Linux 防火墙工具,最终都作用于内核的 Netfilter 框架。理解包的流转,就理解了一切。
包进入本机的路径
text
数据包到达网卡
↓
PREROUTING(raw → mangle → nat)
↓
路由决策:去本机 还是 转发?
├─ 去本机 → INPUT(filter)→ 本机进程
└─ 转发 → FORWARD(filter)→ POSTROUTING(mangle → nat)→ 出去
↓
本机进程发出 → OUTPUT(filter)→ POSTROUTING → 出去
关键记忆:
- 进入本机服务:走
PREROUTING → INPUT - 本机发出:走
OUTPUT → POSTROUTING - 转发(做网关/NAT):走
PREROUTING → FORWARD → POSTROUTING
为什么 filter 表只有三条链
filter 表只管「过滤」,需要过滤的时机只有三处:进本机(INPUT)、出本机(OUTPUT)、过路(FORWARD)。nat 表要在路由前后改地址,所以用 PREROUTING/POSTROUTING。
规则匹配与优先级
在同一张表内,链中的规则从上到下依次匹配,匹配到就执行动作(ACCEPT/DROP),不再往下看。所以:
- 越具体的规则放越前面
- 默认策略(policy)是「都不匹配」时的兜底
iptables -L 看到的规则顺序,就是匹配顺序,-I(插入到前面)就是用来调顺序的。
连接跟踪(conntrack)
iptables 的 -m state 靠内核的连接跟踪(conntrack)实现:它记住每条连接的状态(NEW/ESTABLISHED/RELATED),所以你能写「放行已建立的连接」这样基于状态的规则,而不是仅仅基于端口。这是现代防火墙「状态化」的核心。
firewalld 和 ufw 在底层也是把这套东西封装成「区域」和「简单命令」,方便凡人使用。理解了 Netfilter 的钩子(hook)和链的流转,无论上层换哪种工具,你都能看穿它到底在做什么。
十、防火墙速查表
| 需求 | 命令(iptables / ufw / firewalld) |
|---|---|
| 放行 80 | iptables -A INPUT -p tcp --dport 80 -j ACCEPT |
| 放行 http | ufw allow http / firewall-cmd --permanent --add-service=http |
| 只放行某 IP | -s 10.0.0.5 |
| 封禁 IP | -s 10.0.0.99 -j DROP |
| 查看规则 | iptables -L -n -v / ufw status / firewall-cmd --list-all |
| 保存 | iptables-save / (自带) / (自带) |
一句话收尾:Ubuntu 用 ufw、CentOS/Rocky 用 firewalld、要精细控制用 iptables/nftables;三者最终都作用于 Netfilter;永远先放行 SSH 再动手 。理解了「表-链-规则」和包的流转路径,防火墙就从「玄学」变成了「可控的开关」。下一篇讲端口与连接排查,把 ss、lsof、nc、telnet、curl 串成一套诊断流水线。