Linux 防火墙三国杀:ufw / firewalld / iptables / nftables 怎么选

目录

很多人在 Linux 上配防火墙时是懵的:装了 ufw,又听说要用 iptables,新系统又说要换 nftables,红帽系上还有个 firewalld,到底是什么关系?

这篇先把它们的层级关系画清楚,然后再讲怎么选。

一、四层结构:从内核到用户

流程图流程图
层级 角色 是什么
内核 Netfilter Linux 内核自带的包过滤框架,所有发行版都有
管理 iptables / nftables 命令 用户态命令行工具,把你的规则翻译成内核能懂的语言
前端 ufw / firewalld 更友好的封装,帮你写 iptables/nftables 规则

核心结论:

  • Netfilter 是内核功能,不可选,永远在。
  • iptables vs nftables:iptables 是老工具,nftables 是 2014 年出现的替代品(语法更统一、性能更好)。新发行版默认都是 nftables(Ubuntu 20.04+、Debian 10+、CentOS 8+)。
  • ufw vs firewalld:两者都是“前端”,只是不同发行版预装的不同:
发行版 默认预装
Ubuntu / Debian 系 ufw
CentOS / RHEL / Fedora 系 firewalld
Arch 都不装,用户自己选

💡 注意:ufw 和 firewalld 不绑定发行版,Ubuntu 上也能装 firewalld,Fedora 上也能装 ufw。

二、“假 iptables”:兼容层是怎么回事

新发行版上敲 iptables,它其实是 iptables-nft——把 iptables 语法翻译成 nftables 规则执行,底层根本不是真的 iptables。

iptables --version
# iptables v1.8.7 (nf_tables)   ← 兼容层(背后是 nftables)
# iptables v1.8.7 (legacy)      ← 真正的 iptables

🪤 这就是为什么有时候用老教程的 iptables 命令在新系统上行为不一致——你以为是 iptables,其实是 nftables 翻译过来的。

三、“为什么 ufw 显示没规则,但还是有规则在拦截?”

这是新手最高频的疑问,答案是:

✅ ufw 只管 ufw 自己写入的规则。Docker、fail2ban 等程序会直接往 iptables/nftables 里写规则,ufw 看不到。

# ufw 看到的(只是 ufw 自己的)
sudo ufw status

# iptables 看到的(所有规则,包括 docker、fail2ban 写的)
sudo iptables -L -n -v
sudo iptables -t nat -L -n -v

# nftables 看到的(新发行版)
sudo nft list ruleset

实战中最常踩的两个例子:

现象 原因
ufw deny 80 了,docker 容器还能从公网访问 Docker 自己在 nat 表里写了 DNAT 规则,绕开了 ufw 的 INPUT 链
fail2ban 封了 IP,但 ufw status 看不到 fail2ban 走的是自己创建的链

四、规则的持久化

工具 持久化方式
ufw 自带(重启自动恢复)
firewalld 自带(runtime + permanent 双套规则)
裸 iptables 默认重启清空,需装 iptables-persistent
裸 nftables 默认重启清空,需写入 /etc/nftables.conf

iptables-persistent 的配置文件:

/etc/iptables/rules.v4     # IPv4 规则
/etc/iptables/rules.v6     # IPv6 规则
  • apt purge iptables-persistent 会删除上面的配置文件。
  • apt remove iptables-persistent 会保留配置文件,下次重装能复用。

五、ufw 速查(最推荐新手用)

ufw(Uncomplicated Firewall)是 Debian/Ubuntu 上最简单的防火墙前端,但它在所有 Linux 上都能装。

5.1 基础操作

# 启用 / 关闭
sudo ufw enable
sudo ufw disable

# 看状态
sudo ufw status
sudo ufw status numbered    # 带规则编号(删除时用)

# 重新加载
sudo ufw reload

# 重置(清空所有规则)
sudo ufw --force reset

5.2 默认策略

# 默认拒绝所有入站(强烈推荐)
sudo ufw default deny incoming

# 默认允许所有出站
sudo ufw default allow outgoing

5.3 添加规则

# 按端口
sudo ufw allow 22
sudo ufw allow 80/tcp
sudo ufw allow 443

# 按服务名(看 /etc/services)
sudo ufw allow ssh

# 按 IP 限制
sudo ufw allow from 192.168.1.100
sudo ufw allow from 192.168.1.100 to any port 22

# 拒绝
sudo ufw deny 22

5.4 删除规则

# 按编号删(先用 numbered 查编号)
sudo ufw status numbered
sudo ufw delete 3

# 按内容删
sudo ufw delete allow 80
sudo ufw delete deny 22

5.5 典型 VPS 配置

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22000/tcp        # 改了的 SSH 端口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow from <办公室 IP> to any port 9090   # 面板限制 IP
sudo ufw enable

六、firewalld 速查

红帽系上常见,特点是“区域(zone)“概念:

# 状态
sudo firewall-cmd --state
sudo systemctl status firewalld

# 看当前 zone
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zones

# 看 zone 里的规则
sudo firewall-cmd --list-all

# 加端口(runtime + 永久)
sudo firewall-cmd --add-port=8080/tcp
sudo firewall-cmd --add-port=8080/tcp --permanent

# 加服务
sudo firewall-cmd --add-service=http --permanent

# 重新加载
sudo firewall-cmd --reload

# 删除规则
sudo firewall-cmd --remove-port=8080/tcp --permanent

⚠️ firewalld 区分 runtime 和 permanent,不加 --permanent 重启就丢,改完都要 firewall-cmd --reload。

七、原生 nftables 速查

新系统的“正经”方式:

# 看所有规则
sudo nft list ruleset

# 看某个表
sudo nft list table inet filter

# 加一个 inet 类型的 filter 表
sudo nft add table inet filter

# 加 input 链(默认 drop)
sudo nft add chain inet filter input { type filter hook input priority 0\; policy drop\; }

# 允许已建立的连接
sudo nft add rule inet filter input ct state established,related accept

# 允许 SSH
sudo nft add rule inet filter input tcp dport 22 accept

# 保存到 /etc/nftables.conf(持久化)
sudo nft list ruleset > /etc/nftables.conf
sudo systemctl enable --now nftables

/etc/nftables.conf 完整模板:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        ct state established,related accept
        iifname lo accept
        tcp dport 22 accept
        tcp dport { 80, 443 } accept
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

八、原生 iptables 速查

老系统 / 兼容层都能用:

# 看规则
sudo iptables -L -n -v --line-numbers

# 清空规则
sudo iptables -F
sudo ip6tables -F

# 设置默认策略
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT

# 允许 loopback
sudo iptables -A INPUT -i lo -j ACCEPT

# 允许已建立连接
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 允许 SSH / HTTP / HTTPS
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

# 保存(需要 iptables-persistent)
sudo netfilter-persistent save

九、如何选

场景 推荐
新手 / 简单 VPS ufw
红帽系生产环境 firewalld
容器 / 复杂规则 / 写脚本 直接 nftables
维护老系统 iptables
想要可视化 / 配合面板 装 ufw 让 1Panel/宝塔托管

💡 不要同时启用 ufw 和 firewalld。两者会互相冲突,规则乱掉之后排查是噩梦。

十、清掉所有“奇怪规则”的标准动作

VPS 拿到手发现防火墙规则乱七八糟想从头开始时:

# 1. 停掉 / 卸载所有防火墙前端
sudo systemctl stop ufw
sudo systemctl disable ufw
sudo apt purge -y iptables-persistent ufw

# 2. 清空内存中的规则
sudo iptables -F
sudo iptables -X
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo ip6tables -F
sudo ip6tables -X

# 3. 重置默认策略为 ACCEPT(避免清空后断 SSH)
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

# 4. 重启确认
sudo reboot

# 5. 重启后再装回想用的工具
sudo apt install -y ufw

⚠️ 清空策略前先把 INPUT 默认改成 ACCEPT,否则一清空就断 SSH,下场是去厂商面板用 VNC 救机器。

十一、几条实用经验

  • VPS 上配防火墙顺序:先 allow SSH 再 enable,反着来就是自己锁外面了。
  • 改 SSH 端口务必同步加防火墙规则,否则改完 ssh -p 新端口连不上。
  • Docker 容器端口对外可达是 Docker 自己写的 iptables 规则,不归 ufw 管——要限制 Docker 容器对外暴露,得改 docker-compose 的 ports 写法(绑 127.0.0.1)。
  • fail2ban 的封禁也是直接写 iptables,看封禁状态用 fail2ban-client status sshd,不是 ufw status。
  • 新系统直接学 nftables 就行,iptables 是历史包袱。

理解到这个层级,再去看任何“防火墙规则没生效”的问题,都能直接定位到底是哪一层在拦。