Linux上的Firewalld简介:动态防火墙管理
Firewalld 是一个用于 Linux 的用户空间防火墙管理守护进程,它通过内核级数据包过滤后端 iptables 和 nftables 提供动态的、基于区域的接口。与需要完全重启服务才能应用规则更改的静态防火墙工具不同,Firewalld 可以动态修改 netfilter 规则——在策略更新期间保持活跃的 TCP 会话并消除停机时间。
本指南涵盖 Firewalld 的每个操作层:其架构模型、区域和服务抽象、富规则、运行时与永久配置,以及安全管理生产服务器所需的确切命令。
为什么 Firewalld 取代了静态 iptables 工作流
传统的 iptables 管理意味着将规则写入 shell 脚本或平面配置文件,然后在每次需要更改时刷新并重新加载整个规则集。在繁忙的服务器上,这种刷新和重新加载的循环会中断正在进行的连接,并引入一个短暂的无过滤窗口期。
Firewalld 通过一个 D-Bus 激活的守护进程(firewalld)解决了这个问题,该守护进程持有权威的规则状态并以增量方式将更改传递给内核。结果是原子性的规则更新,零连接中断——这对于运行持久工作负载(如数据库、VPN 隧道或长期 API 连接)的任何服务器都至关重要。
当您配置 VPS 托管环境并需要在不重启或中断服务的情况下对其进行加固时,Firewalld 是 RHEL 系列和许多 Debian 系列发行版上的自然操作选择。
核心架构:Firewalld 如何与内核交互
了解 Firewalld 底层的技术栈可以防止配置错误,并帮助您调试意外行为。
User Space
┌─────────────────────────────────────────────┐
│ firewall-cmd / firewall-config / D-Bus API │
└────────────────────┬────────────────────────┘
│ D-Bus
┌────────────────────▼────────────────────────┐
│ firewalld daemon │
│ (zone engine, service definitions, rich │
│ rule parser, direct rule passthrough) │
└────────────────────┬────────────────────────┘
│ nftables / iptables backend
Kernel Space
┌────────────────────▼────────────────────────┐
│ netfilter (kernel module) │
└─────────────────────────────────────────────┘自 RHEL 8 和 Fedora 32 起,Firewalld 默认使用 nftables 后端。在较旧的系统或明确配置的环境中,它使用 iptables 后端。您可以通过 FirewallBackend 指令在 /etc/firewalld/firewalld.conf 中检查或覆盖活跃后端。
关键陷阱:切勿在同一接口上将直接的 iptables 或 nft 命令与 Firewalld 混合使用。Firewalld 拥有它创建的 netfilter 表;在守护进程外部插入的手动规则将在下次重新加载时被静默覆盖。
Firewalld 的关键概念
区域
区域是应用于网络接口或源地址范围的命名信任级别。进入系统的每个数据包都会与分配给其入口接口的区域进行匹配,区域的规则集决定允许什么。
Firewalld 附带以下预定义区域,按从最宽松到最严格的顺序排列:
| 区域 | 默认策略 | 典型用例 |
|---|---|---|
| — | — | — |
| `trusted` | 接受所有 | 内部实验室网络、回环 |
| `home` | 拒绝,允许选定服务 | 具有已知设备的家庭局域网 |
| `internal` | 拒绝,允许选定服务 | 内部企业网络段 |
| `work` | 拒绝,允许选定服务 | 办公室网络,中等信任 |
| `public` | 拒绝,允许 SSH/DHCPv6 | 面向互联网的接口 |
| `external` | 拒绝,启用伪装 | 路由器/NAT 网关外部链路 |
| `dmz` | 拒绝,允许 SSH | 非军事区服务器 |
| `block` | 以 ICMP 管理员禁止拒绝 | 带响应的显式拒绝 |
| `drop` | 静默丢弃 | 恶意来源,最大隐蔽性 |
架构细节:区域可以绑定到网络接口(例如 eth0)或源 CIDR(例如 192.168.1.0/24)。基于源的绑定优先于基于接口的绑定,这使您可以对来自不同子网但通过同一物理接口到达的流量应用不同的策略——这在多租户或容器化环境中是常见模式。
服务
Firewalld 中的服务是存储在 /usr/lib/firewalld/services/(系统默认值)或 /etc/firewalld/services/(用户覆盖)下的 XML 定义文件。每个文件声明命名应用程序所需的端口、协议和可选的辅助模块。
例如,https 服务定义打开 TCP 端口 443 且不加载额外的内核辅助程序。ftp 服务打开 TCP 端口 21 并加载 nf_conntrack_ftp 辅助程序以处理 FTP 数据通道的动态端口协商。
使用服务名称而不是原始端口号可以使您的防火墙策略自我记录,并降低因拼写错误而无意中打开或关闭端口的风险。
列出系统上所有可用的服务定义:
firewall-cmd --get-services检查特定服务的 XML 定义:
cat /usr/lib/firewalld/services/https.xml富规则
富规则通过简单的服务/端口抽象无法表达的条件逻辑扩展了区域模型。它们支持对源和目标地址、端口范围、协议、时间窗口和连接状态进行匹配,并且可以触发包括 accept、drop、reject、log 和 audit 在内的操作。
富规则语法遵循结构化语法:
rule [family="ipv4|ipv6"]
[source address="addr[/mask]" [invert="true"]]
[destination address="addr[/mask]" [invert="true"]]
[service name="service-name"] | [port port="port" protocol="tcp|udp"]
[log [prefix="prefix"] [level="level"] [limit value="rate/duration"]]
[audit]
[accept|drop|reject [type="reject-type"]]一个实际示例——将来自任何单个 IPv4 源的 SSH 登录尝试限制为每分钟 3 次,然后记录并丢弃超出的请求:
firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
service name="ssh"
log prefix="SSH_RATELIMIT " level="warning" limit value="3/m"
accept' --permanentfirewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
service name="ssh"
drop' --permanent边缘情况:富规则的顺序很重要。Firewalld 按照在区域内添加的顺序评估富规则。如果您在特定的 accept 规则之前添加了宽泛的 drop 规则,则 accept 将永远不会被触及。始终先添加更具体的规则。
运行时与永久配置
这是 Firewalld 中最重要的操作区别,也是最常见的生产错误来源。
| 维度 | 运行时 | 永久 |
|---|---|---|
| — | — | — |
| 存储位置 | 内存中(守护进程状态) | `/etc/firewalld/` XML 文件 |
| 何时应用 | 立即 | `–reload` 或重启后 |
| 重启后保留 | 否 | 是 |
| 可安全测试 | 是 | 需要重新加载才能验证 |
| 风险 | 重启后丢失 | 跨重启持久保留 |
生产变更的最佳实践工作流:
- 仅在运行时应用规则(不带
--permanent标志)并验证其行为是否符合预期。 - 如果正确,使用
--permanent应用相同的规则将其写入磁盘。 - 运行
firewall-cmd --reload将永久配置同步到运行时状态并确认一致性。
此工作流可防止经典场景:管理员添加 --permanent 规则、重新加载后发现自己被锁定在 SSH 之外——因为运行时测试会在问题变为永久性之前揭示问题。
安装和启用 Firewalld
Firewalld 默认安装在 RHEL、CentOS Stream、Fedora、AlmaLinux 和 Rocky Linux 上。在 Debian 和 Ubuntu 上必须显式安装。
RHEL / CentOS Stream / AlmaLinux / Rocky Linux:
sudo dnf install firewalldDebian / Ubuntu:
sudo apt-get update && sudo apt-get install firewalldUbuntu 用户注意:如果 ufw 处于活跃状态,请在启用 Firewalld 之前禁用它,以避免冲突的 netfilter 规则:
sudo ufw disable
sudo systemctl disable ufw启动并启用守护进程:
sudo systemctl start firewalld
sudo systemctl enable firewalld验证守护进程正在运行且内核后端处于活跃状态:
sudo firewall-cmd --state
sudo firewall-cmd --info-zone=public基本 Firewalld 命令
检查当前状态
检查守护进程状态:
sudo firewall-cmd --state列出所有活跃区域及其分配的接口和源:
sudo firewall-cmd --get-active-zones显示特定区域的完整规则集:
sudo firewall-cmd --zone=public --list-all同时显示所有区域的完整规则集:
sudo firewall-cmd --list-all-zones管理默认区域
默认区域应用于未明确分配给其他区域的任何接口:
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --set-default-zone=public将接口分配给区域
sudo firewall-cmd --zone=internal --change-interface=eth1 --permanent
sudo firewall-cmd --reload陷阱:在使用 NetworkManager 的系统上,通过 firewall-cmd 进行的接口到区域分配可能会在重新连接时被 NetworkManager 连接配置文件覆盖。在 NetworkManager 连接中持久设置区域:
nmcli connection modify "Wired connection 1" connection.zone internal添加和删除服务
在运行时允许公共区域中的 HTTP:
sudo firewall-cmd --zone=public --add-service=http使其永久生效:
sudo firewall-cmd --zone=public --add-service=http --permanent删除服务:
sudo firewall-cmd --zone=public --remove-service=http --permanent打开和关闭特定端口
在运行时打开 TCP 端口 8080:
sudo firewall-cmd --zone=public --add-port=8080/tcp永久打开 UDP 端口范围:
sudo firewall-cmd --zone=public --add-port=60000-61000/udp --permanent删除端口:
sudo firewall-cmd --zone=public --remove-port=8080/tcp --permanentIP 地址允许列表和阻止列表
允许来自受信任管理子网的所有流量:
sudo firewall-cmd --zone=trusted --add-source=10.0.0.0/24 --permanent阻止来自特定 IP 的所有流量(基于源的丢弃):
sudo firewall-cmd --zone=drop --add-source=203.0.113.45/32 --permanent端口转发
将外部 TCP 端口 2222 转发到内部 SSH 端口 22(用于在不更改 sshd_config 的情况下隐藏默认 SSH 端口):
sudo firewall-cmd --zone=public --add-forward-port=port=2222:proto=tcp:toport=22 --permanent
sudo firewall-cmd --reloadIP 伪装(NAT)
在外部区域启用伪装,以允许充当网关的 VPS 对来自私有子网的流量进行 NAT:
sudo firewall-cmd --zone=external --add-masquerade --permanent
sudo firewall-cmd --reload重新加载和同步配置
将所有永久更改应用到运行状态而不中断连接:
sudo firewall-cmd --reload执行完全重启(中断所有连接——仅在紧急情况下使用):
sudo systemctl restart firewalld创建自定义区域和服务定义
自定义区域
sudo firewall-cmd --new-zone=management --permanent
sudo firewall-cmd --zone=management --add-source=10.10.0.0/16 --permanent
sudo firewall-cmd --zone=management --add-service=ssh --permanent
sudo firewall-cmd --zone=management --add-service=cockpit --permanent
sudo firewall-cmd --reload自定义服务定义
为监听 TCP 9200 的自定义应用程序(例如 Elasticsearch)创建服务文件:
sudo nano /etc/firewalld/services/elasticsearch.xml<?xml version="1.0" encoding="utf-8"?>
<service>
<short>Elasticsearch</short>
<description>Elasticsearch HTTP API and transport port</description>
<port protocol="tcp" port="9200"/>
<port protocol="tcp" port="9300"/>
</service>sudo firewall-cmd --reload
sudo firewall-cmd --zone=internal --add-service=elasticsearch --permanent
sudo firewall-cmd --reloadFirewalld 与替代方案:选择合适的工具
| 功能 | Firewalld | UFW | iptables(直接) | nftables(直接) |
|---|---|---|---|---|
| — | — | — | — | — |
| 动态规则更新 | 是 | 否(需要重新加载) | 否 | 否 |
| 基于区域的模型 | 是 | 否 | 否 | 否 |
| D-Bus / API 集成 | 是 | 否 | 否 | 否 |
| 富规则 / 条件逻辑 | 是 | 有限 | 是 | 是 |
| RHEL 系列默认 | 是 | 否 | 旧版 | 是(后端) |
| Ubuntu 默认 | 否 | 是 | 旧版 | 是(后端) |
| 学习曲线 | 中等 | 低 | 高 | 高 |
| 适合脚本编写 | 是 | 是 | 是 | 是 |
| GUI 管理工具 | 是(firewall-config) | 否 | 否 | 否 |
对于大规模管理独立服务器的团队,Firewalld 的 D-Bus API 支持通过 Ansible(ansible.posix.firewalld 模块)或 Puppet 等配置管理工具进行程序化规则管理,这相比维护原始 iptables 脚本具有显著的操作优势。
实用安全加固模式
锁定 Web 服务器
运行 HTTPS 且 SSH 限制为管理 IP 的服务器的典型配置:
# Set the default zone
sudo firewall-cmd --set-default-zone=public --permanent
# Allow HTTPS globally
sudo firewall-cmd --zone=public --add-service=https --permanent
# Allow HTTP only to redirect to HTTPS (optional)
sudo firewall-cmd --zone=public --add-service=http --permanent
# Restrict SSH to a specific management IP only
sudo firewall-cmd --zone=public --remove-service=ssh --permanent
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
service name="ssh"
source address="198.51.100.10/32"
accept' --permanent
sudo firewall-cmd --reload如果您在运行 Web 服务器的同时部署了 SSL 证书,确保在证书颁发之前(尤其是对于 ACME HTTP-01 或 TLS-ALPN-01 挑战)在正确的区域中打开端口 443 是一个经常被忽视的先决步骤。
保护邮件服务器
对于运行电子邮件托管堆栈(Postfix、Dovecot)的服务器,所需的服务集为:
sudo firewall-cmd --zone=public --add-service=smtp --permanent
sudo firewall-cmd --zone=public --add-service=smtps --permanent
sudo firewall-cmd --zone=public --add-service=imap --permanent
sudo firewall-cmd --zone=public --add-service=imaps --permanent
sudo firewall-cmd --zone=public --add-service=pop3s --permanent
sudo firewall-cmd --reload记录丢弃的数据包以进行取证
sudo firewall-cmd --zone=public --add-rich-rule='
rule family="ipv4"
log prefix="DROPPED_PUBLIC " level="warning" limit value="10/m"
drop' --permanent
sudo firewall-cmd --reload日志出现在 /var/log/messages 或 systemd 日志(journalctl -k -g DROPPED_PUBLIC)中。限制日志速率(如上所示)以防止在 DDoS 场景下日志泛滥。
cPanel 管理的 VPS 上的 Firewalld
如果您使用的是带 cPanel 的 VPS,请注意 cPanel 会安装并管理其自己的防火墙层(默认为 CSF/LFD)。在没有明确协调的情况下同时运行 Firewalld 和 CSF 将产生冲突的 netfilter 规则。推荐的方法是为每台服务器选择一个防火墙管理层并禁用另一个,或者使用 Firewalld 的直接规则透传接口与 cPanel 的要求集成。
排查常见 Firewalld 问题
问题:使用 --permanent 添加的规则未生效。
原因:永久规则需要重新加载才能进入运行时状态。
解决方法:
sudo firewall-cmd --reload问题:防火墙更改后 SSH 连接中断。
原因:SSH 服务已从活跃区域中删除,或者在 accept 规则之前添加了 drop 富规则。
解决方法:通过带外控制台(您的托管服务提供商的 VNC/KVM 控制台)访问服务器,然后恢复 SSH 服务:
sudo firewall-cmd --zone=public --add-service=ssh --permanent
sudo firewall-cmd --reload问题:firewall-cmd 返回 FirewallD is not running。
原因:守护进程崩溃或从未启动。
解决方法:
sudo systemctl start firewalld
sudo journalctl -u firewalld -n 50问题:规则看起来正确但流量仍被阻止。
原因:在 Firewalld 管理的表之外存在冲突的 iptables 或 nft 规则,或者 SELinux/AppArmor 在应用层阻止了连接。
解决方法:检查手动规则和 SELinux 拒绝:
sudo iptables -L -n -v
sudo ausearch -m avc -ts recent问题:重启后接口未分配到预期区域。
原因:NetworkManager 连接配置文件覆盖了 Firewalld 的接口分配。
解决方法:如上面接口分配部分所述,在 NetworkManager 配置文件中设置区域。
决策矩阵和技术检查清单
在生产服务器上部署 Firewalld 之前,请使用此检查清单:
- 确认活跃防火墙后端(
/etc/firewalld/firewalld.conf中的FirewallBackend)与您的内核的 nftables/iptables 支持相匹配。 - 验证同一接口上没有冲突的防火墙工具(UFW、CSF、直接
iptables脚本)处于活跃状态。 - 将每个网络接口明确分配给一个区域——对于多宿主服务器,切勿仅依赖默认区域。
- 首先在运行时应用所有更改,验证连接性,然后使用
--permanent和--reload提交。 - 在从公共区域删除 SSH 服务之前,通过富规则将 SSH 访问限制为特定源 IP。
- 为所有公开暴露的身份验证服务(SSH、SMTP、HTTPS 登录端点)添加速率限制富规则。
- 为
drop区域启用带速率限制的日志记录,以捕获威胁情报而不会淹没存储。 - 对于通过VPS 控制面板管理的服务器,在应用限制性默认策略之前,确认控制面板所需的端口已列入白名单。
- 通过运行
firewall-cmd --reload测试永久配置,并立即验证所有关键服务仍可访问。 - 将每个自定义区域、富规则和服务定义与基础设施即代码一起记录在版本控制中。
常见问题
--reload 和 sudo systemctl restart firewalld 有什么区别?
--reload 将永久配置更改应用到运行中的守护进程,而不中断已建立的连接。systemctl restart 完全重启守护进程,这会刷新所有 netfilter 规则并短暂中断活跃连接。在生产系统上始终优先使用 --reload。
Firewalld 和 iptables 可以在同一台服务器上共存吗?
在同一接口上不能安全共存。Firewalld 管理其自己的 netfilter 链;修改相同链的直接 iptables 命令将在下次 Firewalld 重新加载时被覆盖。如果您需要注入自定义规则,请改用 Firewalld 的 --direct 接口或富规则。
如何在不重新输入的情况下将运行时规则变为永久规则?
没有内置命令可以一步将所有运行时规则提升为永久规则。正确的工作流是每条规则应用两次——一次不带 --permanent 用于测试,然后带 --permanent 用于持久化——然后执行 --reload。或者,使用 firewall-cmd --runtime-to-permanent(Firewalld 0.9+ 中可用)将当前运行时状态快照到磁盘。
为什么我的区域分配在 NetworkManager 重新连接后被重置?
NetworkManager 将区域分配存储在其自己的连接配置文件中。firewall-cmd --change-interface 分配是 NetworkManager 可以覆盖的运行时覆盖。使用 nmcli connection modify <profile-name> connection.zone <zone> 持久化分配。
Firewalld 支持 IPv6 吗?
是的。Firewalld 同时管理 IPv4 和 IPv6 netfilter 规则。富规则需要 family="ipv6" 属性来专门针对 IPv6 流量。除非服务定义或富规则限制了范围,否则区域和服务规则默认适用于两种地址族。
