2026年7月

firewall-cmd 使用手册

适用环境:RHEL 7+ / CentOS 7+ / Fedora 18+ / Rocky Linux / AlmaLinux
底层机制firewalld 动态防火墙守护进程,基于 nftables(新版)或 iptables(旧版)
命令格式firewall-cmd [选项...]

目录

  1. 基本概念
  2. 启动与状态管理
  3. 区域(Zone)管理
  4. 服务(Service)管理
  5. 端口(Port)管理
  6. 富规则(Rich Rules)
  7. IP 封禁与访问控制
  8. NAT 与端口转发
  9. 直接规则(Direct Rules)
  10. IP 集(IPSet)管理
  11. 辅助与查询命令
  12. --permanent 与临时规则
  13. 注意事项
  14. 常见问题排查

1. 基本概念

1.1 什么是 firewalld

firewalld 是一个动态防火墙管理工具,由红帽主导开发。它与传统的 iptables 静态规则不同,支持运行时修改规则而无需断开已有连接或重启服务。firewall-cmd 是其命令行管理前端。

1.2 核心概念

概念说明
Zone(区域)预定义的安全信任级别集合,每个网络接口被分配到一个 zone
Service(服务)一组预定义的端口/协议组合,如 sshhttphttps
Port(端口)直接通过端口号 + 协议开放访问,如 80/tcp
Rich Rule(富规则)更精细的过滤规则,支持源/目标 IP、日志记录、限速等
Direct Rule(直接规则)近乎原生的 iptables/nftables 规则,提供最大灵活性
IPSet(IP 集)一组 IP 地址的集合,用于批量管理
Runtime / Permanentruntime = 当前生效;permanent = 永久存储,重载后生效

1.3 预定义 Zone 及信任级别

Zone信任级别默认行为
drop最低所有入站包静默丢弃,仅允许出站
block极低拒绝入站并返回 icmp-host-prohibited
public低(默认)只允许被明确放行的入站连接
external中低用于 NAT 网关,启用 IP 伪装
dmz中等用于 DMZ 区,只允许选定的入站连接
work中高信任工作网络中的大多数计算机
home信任家庭网络中的大多数计算机
internal信任内部网络中的大多数计算机
trusted最高所有网络连接都被接受

2. 启动与状态管理

firewalld 是一个守护进程(daemon),由 systemd 管理生命周期。启动、停止、重载规则是日常运维最基本的操作。理解 --reload--complete-reload--check-config 三者的区别,可以避免在远程操作时因规则错误把自己"锁在门外"。本章还介绍了 panic 模式,作为应急开关可在遭遇攻击时一键封锁所有入站流量。

2.1 服务启停

# 启动 firewalld
systemctl start firewalld

# 停止 firewalld
systemctl stop firewalld

# 重启 firewalld
systemctl restart firewalld

# 设置开机自启
systemctl enable firewalld

# 禁用开机自启
systemctl disable firewalld

2.2 状态查看

# 查看防火墙运行状态
firewall-cmd --state

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

# 查看所有已激活的 zone
firewall-cmd --get-active-zones

# 查看全部支持的服务列表
firewall-cmd --get-services

# 查看当前 zone 的完整配置
firewall-cmd --list-all

# 查看指定 zone 的完整配置
firewall-cmd --zone=public --list-all

2.3 规则重载

# 重载防火墙(不中断现有连接,推荐)
firewall-cmd --reload

# 完全重载(会短暂中断连接,仅用于状态异常时)
firewall-cmd --complete-reload

# 仅检查配置是否有效(不执行变更)
firewall-cmd --check-config
重要--reload 会删除所有仅存在于 runtime 的临时规则;--complete-reload 会重置整个防火墙状态。

3. 区域(Zone)管理

Zone(区域)是 firewalld 最核心的抽象概念——它把网络环境按"信任级别"划分为不同等级,每个等级对应一套独立的安全策略。比如你笔记本电脑连到公司网络时可以信任大多数连接,连到咖啡馆 Wi-Fi 时则应该严格限制入站流量。Zone 让这种场景切换变得简单:只需将一个网卡绑定到对应 zone,该 zone 下所有服务、端口、富规则就会自动生效。一台服务器通常只用 1~2 个 zone,默认 public 足以覆盖绝大多数场景。

3.1 查看 Zones

# 列出所有可用 zone
firewall-cmd --get-zones

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

# 查看网卡接口被分配到哪个 zone
firewall-cmd --get-zone-of-interface=eth0

# 查看指定 zone 的所有配置(runtime)
firewall-cmd --zone=public --list-all

3.2 设置默认 Zone

# 设置默认 zone(永久生效)
firewall-cmd --set-default-zone=dmz
# 修改的是 permanent 配置,需 reload 才生效
firewall-cmd --reload

3.3 将网卡绑定到 Zone

# 将 eth0 绑定到 external zone(永久生效)
firewall-cmd --permanent --zone=external --change-interface=eth0

# 临时绑定(runtime,重启/重载后丢失)
firewall-cmd --zone=external --change-interface=eth0

# 查看网卡所属 zone
firewall-cmd --get-zone-of-interface=eth0

3.4 创建自定义 Zone

# 创建自定义 zone(永久)
firewall-cmd --permanent --new-zone=custom-app

# 重载使其可用
firewall-cmd --reload

# 删除自定义 zone
firewall-cmd --permanent --delete-zone=custom-app
firewall-cmd --reload

3.5 Zone 目标(Target)

每个 zone 可设置默认目标,决定未匹配流量的处理方式:

# 查看 zone 的 target
firewall-cmd --zone=public --get-target

# 设置 target(DEFAULT / ACCEPT / DROP / %%REJECT%%)
firewall-cmd --permanent --zone=public --set-target=DROP
firewall-cmd --reload
DEFAULT 表示沿用该 zone 的默认行为(不操作 chain 策略)。
设置为 DROP 后,该 zone 下所有未明确放行的流量都会被静默丢弃。

4. 服务(Service)管理

firewalld 中,"服务"(Service)不是指正在运行的进程,而是一个预定义的端口/协议组合模板。比如 http 服务等于 80/tcphttps 等于 443/tcpssh 等于 22/tcp。使用服务有两个好处:① 可读性强——--add-service=http--add-port=80/tcp 更直观;② 可维护性高——服务定义集中在 XML 文件中,修改一处即可全局生效。系统预置了上百种常见服务(firewall-cmd --get-services 查看全部),你也可以为自有应用创建自定义服务定义。

4.1 查看预定义服务

# 列出所有预定义服务
firewall-cmd --get-services

# 查看某个服务的详细信息(端口、协议、模块等)
firewall-cmd --info-service=ssh

输出示例:

ssh
  ports: 22/tcp
  protocols:
  source-ports:
  modules:
  destination: ipv4:  ipv6:

4.2 添加 / 移除服务

# 临时添加 HTTP 服务到 public zone
firewall-cmd --zone=public --add-service=http

# 永久添加 HTTPS 服务到 public zone
firewall-cmd --permanent --zone=public --add-service=https
firewall-cmd --reload

# 同时添加多个服务(永久)
firewall-cmd --permanent --zone=public --add-service={http,https,ssh}
firewall-cmd --reload

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

4.3 服务开放组合示例:Web 服务器

# 开放 Web 服务器所需的全部服务(永久)
firewall-cmd --permanent --zone=public --add-service={http,https,ssh}
firewall-cmd --reload

# 验证配置
firewall-cmd --zone=public --list-services

4.4 自定义服务配置文件

预置服务定义文件位于 /usr/lib/firewalld/services/,自定义服务放在 /etc/firewalld/services/

# 创建自定义服务:my-app(监听 8080 和 8443)
cat > /etc/firewalld/services/my-app.xml << 'EOF'
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>My Application</short>
  <description>Custom application service on ports 8080 and 8443</description>
  <port protocol="tcp" port="8080"/>
  <port protocol="tcp" port="8443"/>
</service>
EOF

# 重载使自定义服务可用
firewall-cmd --reload

# 使用自定义服务
firewall-cmd --permanent --zone=public --add-service=my-app
firewall-cmd --reload

# 验证自定义服务是否存在
firewall-cmd --info-service=my-app

5. 端口(Port)管理

端口规则是最直接的放行方式——指定"哪个端口 + 什么协议"就完了,不依赖任何预定义模板。当你要开放的服务不在预置服务列表中(如非标准端口上的自定义应用),或者你只需要临时开一个端口做测试,用端口规则比去写 XML 服务定义快得多。但要注意:端口规则是"裸"的,缺少语义信息,维护起来不如服务规则直观。实用经验:常见标准服务优先用 --add-service;非标准端口或一次性场景用 --add-port

5.1 查看已开放端口

# 查看当前 zone 下的已开放端口
firewall-cmd --zone=public --list-ports

# 查看永久配置中的端口
firewall-cmd --permanent --zone=public --list-ports

5.2 添加 / 移除端口

# 临时开放 8080/TCP 端口
firewall-cmd --zone=public --add-port=8080/tcp

# 永久开放 443/TCP 端口
firewall-cmd --permanent --zone=public --add-port=443/tcp
firewall-cmd --reload

# 开放端口范围
firewall-cmd --permanent --zone=public --add-port=8000-8099/tcp
firewall-cmd --reload

# 同时开放 TCP 和 UDP
firewall-cmd --permanent --zone=public --add-port=53/tcp
firewall-cmd --permanent --zone=public --add-port=53/udp
firewall-cmd --reload

# 移除端口
firewall-cmd --permanent --zone=public --remove-port=8080/tcp
firewall-cmd --reload

5.3 常见运维示例

示例 1:Web + SSH 最小化开放

firewall-cmd --permanent --zone=public --add-service={http,https,ssh}
firewall-cmd --reload

示例 2:MySQL / PostgreSQL 数据库服务器

# MySQL(仅允许从内网网段访问 —— 见富规则章节)
firewall-cmd --permanent --zone=internal --add-port=3306/tcp

# PostgreSQL
firewall-cmd --permanent --zone=internal --add-port=5432/tcp

firewall-cmd --reload

示例 3:Redis 服务器

# Redis 默认端口
firewall-cmd --permanent --zone=internal --add-port=6379/tcp
firewall-cmd --reload

示例 4:Docker 常用端口

# Docker 通过 firewalld 管理端口(如果关闭了 docker 的 iptables 接管)
firewall-cmd --permanent --zone=public \
  --add-port=2375/tcp \
  --add-port=2376/tcp
firewall-cmd --reload

示例 5:Kubernetes 控制平面端口

firewall-cmd --permanent --zone=public \
  --add-port=6443/tcp \    # kube-apiserver
  --add-port=2379-2380/tcp \  # etcd
  --add-port=10250/tcp \   # kubelet
  --add-port=10257/tcp \   # kube-controller-manager
  --add-port=10259/tcp     # kube-scheduler
firewall-cmd --reload

6. 富规则(Rich Rules)

如果说服务和端口规则是"开关"——要么全开要么全关,那么富规则就是"调节阀"——你可以精确控制(源 IP)、访问什么(端口/服务)、怎么处理(接受/拒绝/丢弃)、是否记录日志、甚至限制连接速率。富规则的语言(Rich Language)是 firewalld 中表达能力最强的安全策略层,大多数生产环境的精细化访问控制都靠它实现。富规则的语法乍看复杂,本质就是一条"条件-动作"语句,读完本章的场景示例就能掌握。

富规则是 firewalld 最强大的功能之一,支持比基本服务/端口管理更细粒度的流量控制。

6.1 富规则基本语法

firewall-cmd --zone=<zone> --add-rich-rule='rule [family="ipv4|ipv6"]
  [source address="<CIDR>" [invert="true"]]
  [destination address="<CIDR>" [invert="true"]]
  [service|port|protocol|icmp-block|masquerade|forward-port]
  [log [prefix="<text>"] [level="<level>"] [limit value="<rate>/<unit>"]]
  [audit]
  [accept|reject|drop|mark]
  [limit value="<rate>/<unit>"]'

6.2 常用场景与示例

场景 1:仅允许特定 IP 访问 SSH

# 只允许 192.168.1.100 访问 SSH(22 端口),拒绝其他所有来源
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="192.168.1.100/32"
  service name="ssh"
  accept'

firewall-cmd --reload

场景 2:允许某网段访问特定端口

# 允许 10.0.0.0/8 内网访问 MySQL
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="10.0.0.0/8"
  port port="3306" protocol="tcp"
  accept'

firewall-cmd --reload

场景 3:拒绝特定 IP 访问 Web 服务

# 拒绝 203.0.113.50 访问 HTTP/HTTPS
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="203.0.113.50/32"
  service name="http"
  reject'

firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="203.0.113.50/32"
  service name="https"
  reject'

firewall-cmd --reload

场景 4:带日志记录的规则

# 记录所有被拒绝的 8080 端口访问尝试,日志前缀 "REJECTED-8080"
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  port port="8080" protocol="tcp"
  log prefix="REJECTED-8080: " level="info" limit value="10/m"
  reject'

firewall-cmd --reload

# 查看日志
journalctl -f | grep REJECTED-8080

场景 5:连接速率限制

# 限制每个 IP 每分钟最多新建 5 个 SSH 连接
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  service name="ssh"
  limit value="5/m"
  accept'

firewall-cmd --reload

场景 6:同时允许 TCP + UDP

# 允许 192.168.1.0/24 网段访问 DNS(TCP+UDP)
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="192.168.1.0/24"
  port port="53" protocol="tcp"
  accept'

firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="192.168.1.0/24"
  port port="53" protocol="udp"
  accept'

firewall-cmd --reload

场景 7:基于时间的规则(需 firewalld >= 0.4.3)

# 工作时间(周一至周五 09:00-18:00)才允许访问,其他时间拒绝
# 注意:此功能依赖 nftables 支持

大多数生产环境建议通过 cron + firewall-cmd 组合实现基于时间的控制:

# 09:00 开放
0 9 * * 1-5 firewall-cmd --zone=public --add-rich-rule='...' 
# 18:00 移除
0 18 * * 1-5 firewall-cmd --zone=public --remove-rich-rule='...'

6.3 查看与移除富规则

# 列出所有富规则
firewall-cmd --zone=public --list-rich-rules

# 移除某条富规则(需要与添加时的规则完全一致)
firewall-cmd --permanent --zone=public --remove-rich-rule='
  rule family="ipv4"
  source address="203.0.113.50/32"
  service name="http"
  reject'

firewall-cmd --reload

7. IP 封禁与访问控制

IP 封禁是运维人员最常用的防御手段之一——当发现某个 IP 在暴力破解 SSH、恶意扫描端口或发起 CC 攻击时,直接将其流量全部丢弃。firewalld 提供了从临时封禁(runtime,不持久化)、永久封禁(permanent)、到批量封禁(IPSet)的完整工具链。本章还会讲解 drop(静默丢弃)与 reject(明确拒绝)的区别——选错的话要么暴露服务存在、要么让合法用户误以为网络不通。

7.1 使用富规则封禁 IP

# 封禁单个 IP(直接 drop,不给任何回应)
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="198.51.100.10/32"
  drop'

firewall-cmd --reload

7.2 封禁整个网段

# 封禁整个 /24 子网
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="198.51.100.0/24"
  drop'

firewall-cmd --reload

7.3 使用 reject 还是 drop

动作效果适用场景
drop静默丢弃数据包,不回应对外暴露的服务,降低被扫描识别的概率
reject拒绝并返回 ICMP 不可达内部服务,方便故障排查
# drop:静默丢弃
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" source address="1.2.3.4/32" drop'

# reject:明确拒绝
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" source address="1.2.3.4/32" reject'

7.4 批量管理被封禁 IP —— 使用 IPSet

当需要封禁大量 IP 时,使用 IPSet 比逐条添加富规则更高效(详见 第 10 章)。

7.5 临时封禁(不持久化)

# 直接 drop,不加 --permanent,重载或重启后自动清除
firewall-cmd --zone=public --add-rich-rule='
  rule family="ipv4" source address="198.51.100.99/32" drop'

# 查看当前 runtime 中的封禁
firewall-cmd --zone=public --list-rich-rules

7.6 使用 fail2ban 配合 firewalld

对于自动化暴力破解防御,推荐使用 fail2ban + firewalld 组合:

# 安装 fail2ban
yum install -y fail2ban-firewalld    # RHEL/CentOS 7
dnf install -y fail2ban-firewalld    # RHEL 8+/Fedora

# 配置 fail2ban 使用 firewalld
# /etc/fail2ban/jail.local
[sshd]
enabled  = true
banaction = firewallcmd-ipset
bantime  = 3600
findtime = 600
maxretry = 5

systemctl enable --now fail2ban

8. NAT 与端口转发

NAT(Network Address Translation,网络地址转换)是网关/路由场景的核心功能,分为两个方向:SNAT(源地址转换)让内网机器通过防火墙的公网 IP 出站上网——firewalld 中叫 Masquerade(IP 伪装);DNAT(目标地址转换)把外部访问防火墙某个端口的流量转发到内网某台机器的某个端口——这就是端口转发/端口映射。典型场景:公司只有一台公网服务器,通过端口转发将 80/443 端口流量送给内网的 Web 服务器集群。

8.1 启用 IP 伪装(Masquerade)

IP 伪装是源地址 NAT(SNAT),让内网机器通过防火墙访问外网。

# 检查当前是否已启用 masquerade
firewall-cmd --zone=external --query-masquerade

# 启用 masquerade(external zone 常用)
firewall-cmd --permanent --zone=external --add-masquerade

# 如果 external zone 没有绑定网卡,先绑定外网网卡
firewall-cmd --permanent --zone=external --change-interface=eth0

firewall-cmd --reload

# 同时需确保 IP 转发已开启
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
sysctl -p

8.2 端口转发(DNAT / Port Forwarding)

将到达防火墙某个端口的流量转发到内网其他主机。

# 语法
firewall-cmd --zone=<zone> --add-forward-port='
  port=<port>:proto=<tcp|udp>:toport=<to-port>:toaddr=<to-addr>'

# 示例:将外部 8080 端口的 TCP 流量转发到 192.168.1.10 的 80 端口
firewall-cmd --permanent --zone=external \
  --add-forward-port='port=8080:proto=tcp:toport=80:toaddr=192.168.1.10'

# 需要同时启用 masquerade
firewall-cmd --permanent --zone=external --add-masquerade

firewall-cmd --reload

8.3 常见端口转发示例

示例 1:同机端口重定向

# 将 80 端口流量重定向到本机 8080(常用于非 root 用户运行应用)
firewall-cmd --permanent --zone=public \
  --add-forward-port='port=80:proto=tcp:toport=8080'
firewall-cmd --reload

示例 2:多端口转发

# HTTP 转发到内网 Web 服务器
firewall-cmd --permanent --zone=external \
  --add-forward-port='port=80:proto=tcp:toport=80:toaddr=10.0.0.5'

# HTTPS 转发到同一台内网服务器
firewall-cmd --permanent --zone=external \
  --add-forward-port='port=443:proto=tcp:toport=443:toaddr=10.0.0.5'

# SSH 转发到内网跳板机
firewall-cmd --permanent --zone=external \
  --add-forward-port='port=2222:proto=tcp:toport=22:toaddr=10.0.0.10'

firewall-cmd --permanent --zone=external --add-masquerade
firewall-cmd --reload

示例 3:端口范围转发

# 将外部 8000-8099 转发到内网同一端口范围
firewall-cmd --permanent --zone=external \
  --add-forward-port='port=8000-8099:proto=tcp:toport=8000-8099:toaddr=192.168.1.100'

firewall-cmd --permanent --zone=external --add-masquerade
firewall-cmd --reload

8.4 使用富规则进行端口转发

富规则提供更灵活的转发方式:

# 仅转发来自特定来源的流量
firewall-cmd --permanent --zone=external --add-rich-rule='
  rule family="ipv4"
  source address="203.0.113.0/24"
  forward-port port="80" protocol="tcp" to-port="8080" to-addr="192.168.1.10"'

firewall-cmd --reload

8.5 查看与删除转发规则

# 查看所有转发规则
firewall-cmd --zone=external --list-forward-ports

# 删除转发规则(语法与添加时一致)
firewall-cmd --permanent --zone=external \
  --remove-forward-port='port=8080:proto=tcp:toport=80:toaddr=192.168.1.10'
firewall-cmd --reload

9. 直接规则(Direct Rules)

直接规则是 firewalld 的"逃生舱"——当你需要的功能超出了富规则甚至服务/端口规则的表达能力时,可以绕过 firewalld 的抽象层,直接往底层 iptablesnftables 写入原生规则。比如自定义链、连接追踪标记(connmark)、复杂的 NAT 逻辑等。代价是你失去了 firewalld 对规则的生命周期管理和冲突检测,需要自行确保不与 firewalld 自带的规则互相干扰。大多数场景用富规则就够了,直接规则应作为最后手段。

直接规则允许你直接操作 iptables / nftables 规则,适合 firewalld 原生功能覆盖不到的场景。

9.1 查看与添加直接规则

# 查看所有直接规则
firewall-cmd --direct --get-all-rules

# 添加一条 iptables raw 表规则(禁用特定 IP 的连接追踪)
firewall-cmd --permanent --direct --add-rule \
  ipv4 raw PREROUTING 0 \
  -s 192.168.1.100/32 -j NOTRACK

# 添加一条 filter 表规则
firewall-cmd --permanent --direct --add-rule \
  ipv4 filter INPUT 0 \
  -p tcp --dport 22 -m state --state NEW -m recent --set

firewall-cmd --reload

9.2 使用链(Chain)

# 添加自定义链
firewall-cmd --permanent --direct --add-chain ipv4 filter MY_CHAIN

# 向自定义链添加规则
firewall-cmd --permanent --direct --add-rule ipv4 filter MY_CHAIN 0 \
  -p tcp --dport 8443 -j ACCEPT

# 将自定义链挂载到 INPUT 链
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 10 \
  -j MY_CHAIN

firewall-cmd --reload

9.3 移除直接规则

firewall-cmd --permanent --direct --remove-rule \
  ipv4 raw PREROUTING 0 \
  -s 192.168.1.100/32 -j NOTRACK

firewall-cmd --permanent --direct --remove-chain ipv4 filter MY_CHAIN

firewall-cmd --reload
注意:直接规则绕过了 firewalld 的抽象层,需自行确保规则不与 firewalld 自带规则冲突。

10. IP 集(IPSet)管理

如果只封禁两三个 IP,逐条写富规则没问题;但如果要封禁成百上千个 IP(比如来自威胁情报的恶意 IP 列表),几百条富规则会让规则列表膨胀到不可维护,而且匹配效率低下。IPSet 正是为解决这个问题而生——它像一个"IP 地址容器",你可以把数百个 IP 或网段装进去,然后用一条富规则引用这个容器。底层内核也会对 IPSet 做哈希索引优化,匹配效率远超逐条遍历。配合 fail2ban 等自动化工具,IPSet 是实现动态黑名单的标准方案。

IPSet 可高效管理大量 IP 地址,适合 IP 黑白名单场景。

10.1 创建与管理 IPSet

# 创建一个名为 blacklist 的 IP 集
firewall-cmd --permanent --new-ipset=blacklist --type=hash:ip

# 向 IP 集中添加成员
firewall-cmd --permanent --ipset=blacklist --add-entry=198.51.100.10
firewall-cmd --permanent --ipset=blacklist --add-entry=198.51.100.11
firewall-cmd --permanent --ipset=blacklist --add-entry=203.0.113.0/24

# 查看 IP 集内容
firewall-cmd --permanent --info-ipset=blacklist

firewall-cmd --reload

10.2 使用 IPSet 配合富规则

# 使用 IPSet 批量封禁
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source ipset="blacklist"
  drop'

firewall-cmd --reload

10.3 常见的 IPSet 类型

类型说明示例用途
hash:ip单个 IP 地址IP 黑白名单
hash:netIP 子网/CIDR网段黑白名单
hash:ip,portIP + 端口组合精细访问控制
hash:net,port子网 + 端口组合区域端口控制

10.4 删除 IPSet

firewall-cmd --permanent --ipset=blacklist --remove-entry=198.51.100.10
firewall-cmd --permanent --delete-ipset=blacklist
firewall-cmd --reload

11. 辅助与查询命令

除了增删改规则,日常运维中还有大量辅助操作——备份配置、恢复出厂、对比运行时与永久配置差异、提高日志级别排查问题、以及在紧急情况下用 panic-on 一键封死所有入站流量。这些命令虽然不直接涉及规则管理,但在故障排查和灾难恢复时往往是救命稻草。建议:在对防火墙做大改动前,先 cp -a /etc/firewalld 做好备份。

11.1 导出与备份配置

# 备份当前 permanent 配置(整个 firewalld 配置目录)
cp -a /etc/firewalld /etc/firewalld.bak.$(date +%Y%m%d)

# 备份到压缩包
tar czf firewalld-backup-$(date +%Y%m%d).tar.gz -C /etc firewalld/

11.2 恢复默认配置

# 恢复到 firewalld 出厂默认配置
\cp -a /usr/lib/firewalld /etc/firewalld
firewall-cmd --reload

11.3 查看运行时 vs 永久配置差异

# 方法一:分别查看
echo "=== Runtime ===" && firewall-cmd --list-all
echo "=== Permanent ===" && firewall-cmd --permanent --list-all

# 方法二:将运行时配置同步到永久(相当于让所有临时规则持久化)
firewall-cmd --runtime-to-permanent

11.4 查看统计信息

# 查看防火墙统计(需要 firewalld >= 0.6.0)
firewall-cmd --zone=public --get-log-denied

11.5 调试模式

# 以更高日志级别运行 firewalld
firewall-cmd --debug=10    # 0-10,越高越详细

# 或直接修改 firewalld 日志级别
vim /etc/firewalld/firewalld.conf
# LogDenied=all
# 重启生效
systemctl restart firewalld

11.6 应急开关 —— 紧急关闭防火墙

# 紧急模式:丢弃所有入站流量(除已有连接和 SSH 外)
firewall-cmd --panic-on

# 检查应急模式状态
firewall-cmd --query-panic    # yes=开启 / no=未开启

# 关闭应急模式
firewall-cmd --panic-off

12. --permanent 与临时规则

这是 firewalld 最容易踩坑的地方。firewalld 维护了两套规则:runtime(当前在内存中生效)和 permanent(存储在 /etc/firewalld/ 的 XML 文件中)。默认不加 --permanent 的操作只影响 runtime——重启或 --reload 后消失;加了 --permanent 的操作写入磁盘,但需要 --reload 才能实际生效。如果你在远程 SSH 操作时改了永久规则但还没 reload 就被踢了,改的规则不会丢;反之,如果你临时加了条规则测试,忘了持久化就 reload,规则就没了。本章的三种工作流覆盖了所有常见操作模式。

12.1 两种规则类型

类型存储位置生效时机生命周期
Runtime(临时)内存立即生效服务重启或 --reload 后消失
Permanent(永久)/etc/firewalld/--reload 后生效持久化,重启不丢失

12.2 典型工作流

方式一:永久生效(推荐生产环境使用)

# 第一步:写入永久配置
firewall-cmd --permanent --zone=public --add-port=8080/tcp

# 第二步:重载使配置生效(同时也清空了 runtime 临时规则)
firewall-cmd --reload

# 第三步:验证
firewall-cmd --zone=public --list-ports

方式二:先临时测试,确认后持久化

# 第一步:临时添加(立即生效,方便测试)
firewall-cmd --zone=public --add-port=8080/tcp

# 第二步:测试服务是否正常
curl -I http://localhost:8080

# 第三步:确认无误后持久化
firewall-cmd --runtime-to-permanent

方式三:同时生效(一步到位)

# 同时添加到 runtime 和 permanent
firewall-cmd --permanent --zone=public --add-port=8080/tcp
firewall-cmd --zone=public --add-port=8080/tcp
# 或更简洁的方式:先 permanent,再 reload
firewall-cmd --permanent --zone=public --add-port=8080/tcp
firewall-cmd --reload

12.3 注意事项

  • --reload丢弃所有仅存在于 runtime 的规则,只保留 permanent 配置。
  • --runtime-to-permanent 会将当前所有 runtime 规则写入 permanent 存储。
  • 部分修改命令(如 --set-default-zone)只修改 permanent 配置,需要显式 --reload
  • 添加 permanent 规则时如果忘了 --permanent 并执行 --reload,刚加的临时规则会丢失。

13. 注意事项

firewalld 不是孤立运行的——它需要与 Docker、Podman、NetworkManager 等组件和平共处。这些组件各自都可能操作底层的 iptables/nftables 规则,稍有不慎就会产生冲突,导致"明明 firewalld 放行了,流量还是过不去"。本章汇总了常见兼容性场景的解决方案,以及 IPv6、日志记录、规则优先级、安全操作顺序等容易被忽视的细节。尤其注意远程操作时要用 at 设置自动回滚,防止把自己锁在服务器外面。

13.1 与 Docker 的兼容性

Docker 默认会直接操作 iptables,可能与 firewalld 产生冲突:

# 方案一:让 Docker 使用 firewalld 管理的 zone
# /etc/docker/daemon.json
{
  "iptables": false
}

# 方案二:将 docker0 网桥绑定到 trusted zone
firewall-cmd --permanent --zone=trusted --change-interface=docker0
firewall-cmd --reload

# 方案三:在 firewalld 中为 Docker 所需的端口显式放行
firewall-cmd --permanent --zone=public --add-port=80/tcp
firewall-cmd --permanent --zone=public --add-port=443/tcp
firewall-cmd --reload
推荐:不要让 Docker 直接修改 iptables,改用 firewalld 统一管理所有规则。

13.2 与 Podman 的兼容性

Podman 与 firewalld 兼容性较好,但仍需注意:

# 安装 podman-plugins 以支持 firewalld
dnf install -y podman-plugins    # RHEL 8+

# 确保 CNI 网络使用 firewalld 后端

13.3 与 NetworkManager 的交互

NetworkManager 可以与 firewalld 集成,自动根据连接类型切换 zone:

# 查看当前连接
nmcli connection show

# 为某个连接指定 firewalld zone
nmcli connection modify "Wired connection 1" connection.zone external

# 重启连接使其生效
nmcli connection down "Wired connection 1"
nmcli connection up "Wired connection 1"

13.4 日志记录配置

默认情况下 firewalld 不记录被拒绝的连接,建议在生产环境开启:

# 编辑 firewalld 配置
# /etc/firewalld/firewalld.conf
# LogDenied=all    # all / unicast / broadcast / multicast / off
systemctl restart firewalld

# 查看被拒绝的日志
journalctl -u firewalld -f

13.5 IPv6 注意事项

firewalld 支持 IPv4 和 IPv6,但部分规则需分别配置:

# 同时配置 IPv4 和 IPv6 的富规则
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" service name="http" accept'
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv6" service name="http" accept'
firewall-cmd --reload

部分命令(如 --add-service)会同时影响 IPv4 和 IPv6,无需分别操作。

13.7 规则优先级

firewalld 中,规则评估顺序如下:

  1. 直接规则(Direct Rules)—— 最高优先级
  2. 富规则(Rich Rules)
  3. 服务/端口规则(Service / Port Rules)
  4. Zone 的 target(默认策略)

同一类别中,规则按添加顺序依次匹配(先匹配先生效)。

13.8 执行顺序建议

在修改防火墙规则时,始终遵循以下顺序以降低把自己锁在外面的风险:

  1. 先在测试环境验证,或使用临时规则(不加 --permanent
  2. 如果是远程操作,使用 atcron 设置回滚任务
  3. 确认无误后再 --permanent + --reload
# 安全操作 SSH 远程时的回滚方案
at now + 2 minutes << 'EOF'
firewall-cmd --zone=public --remove-service=ssh
EOF
# 现在放心修改规则,如果被锁,2 分钟后自动恢复

14. 常见问题排查

防火墙出问题的表现通常一致——"端口不通",但根因千差万别:可能是规则没加对 zone、可能是 --reload 丢了临时规则、可能是 Docker 绕过了 firewalld、可能只是服务根本没监听……本章按"症状 → 排查 → 修复"的结构,把运维中最头疼的几种防火墙故障整理成了标准化排障流程。如果你赶时间,直接跳到 14.7 诊断工具速查 拿命令就走。

14.1 命令行报错

错误 1:FirewallD is not running

# 症状
firewall-cmd --state
# not running

# 排查与修复
systemctl status firewalld
systemctl start firewalld
systemctl enable firewalld

错误 2:ALREADY_ENABLED

# 症状:添加规则时报此错误

# 原因:规则已存在于 permanent 配置中

# 解决:先查看已有规则
firewall-cmd --permanent --zone=public --list-all

# 确认后忽略即可,或先移除再添加

错误 3:INVALID_ZONE

# 症状:使用了不存在的 zone 名称

# 排查
firewall-cmd --get-zones

# 确保 zone 名称拼写正确

错误 4:INVALID_PORT

# 常见错误写法
firewall-cmd --zone=public --add-port=8080      # 缺少协议
firewall-cmd --zone=public --add-port=8080/TCP  # 协议必须小写

# 正确写法
firewall-cmd --zone=public --add-port=8080/tcp

14.2 规则已添加但服务仍不可达

排查步骤

# 1. 确认规则已生效
firewall-cmd --zone=public --list-all | grep <port-or-service>

# 2. 确认服务是否在监听
ss -tlnp | grep <port>
# 或
netstat -tlnp | grep <port>

# 3. 确认 zone 是否正确
# 检查流量入口网卡所属的 zone
firewall-cmd --get-active-zones
firewall-cmd --get-zone-of-interface=eth0

# 4. 检查 NAT/端口转发是否启用 IP 转发
sysctl net.ipv4.ip_forward

# 5. 检查是否有其他防火墙(如 iptables)也在运行
systemctl status iptables
systemctl status nftables

14.3 Docker 容器端口无法访问

# 常见场景:Docker 使用 iptables,绕过 firewalld

# 排查
iptables -L DOCKER -n -v             # 查看 Docker 链
firewall-cmd --zone=public --list-all  # 对比 firewalld 配置
iptables -L FORWARD -n -v            # 检查 FORWARD 链

# 修复:确保 FORWARD 链没有 DROP
firewall-cmd --permanent --zone=public --add-masquerade
firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 \
  -i docker0 -o docker0 -j ACCEPT
firewall-cmd --reload

14.4 --reload 后规则丢失

# 原因:规则仅添加到了 runtime,未写入 permanent

# 排查
diff <(firewall-cmd --list-all) <(firewall-cmd --permanent --list-all)

# 修复:将 runtime 规则持久化
firewall-cmd --runtime-to-permanent

14.5 NAT/端口转发不生效

# 必备检查清单:
# 1. masquerade 是否已启用
firewall-cmd --zone=external --query-masquerade

# 2. IP 转发是否已开启
sysctl net.ipv4.ip_forward
# 如果为 0,执行:
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
sysctl -p

# 3. 转发规则是否已正确添加到正确的 zone
firewall-cmd --zone=external --list-forward-ports

# 4. 检查 iptables nat 表
iptables -t nat -L -n -v | grep DNAT

# 5. 确保转发目标可达
ping -c 3 192.168.1.10

14.6 富规则语法错误

# 常见错误 1:引号不匹配
# 错误
firewall-cmd --add-rich-rule='rule family="ipv4" source address="1.2.3.4" drop'
# 问题:外层单引号与属性单引号冲突

# 正确:多层引号
firewall-cmd --add-rich-rule='rule family="ipv4" source address="1.2.3.4" drop'

# 通常建议将富规则写在多行(使用换行)以避免引号错误
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4"
  source address="1.2.3.4/32"
  port port="80" protocol="tcp"
  accept'

14.7 诊断工具速查

命令用途
firewall-cmd --list-all-zones列出所有 zone 的完整配置
firewall-cmd --zone=public --list-all查看特定 zone 的全部配置
ss -tlnp查看所有监听中的 TCP 端口
iptables -L -n -v查看 iptables 规则(底层)
nft list ruleset查看 nftables 规则(底层,新版)
journalctl -u firewalld -f实时查看 firewalld 日志
firewall-cmd --check-config检查配置文件语法
nc -zv <host> <port>测试端口连通性
tcpdump -i any port <port> -n抓包分析流量

附录 A:快速参考卡片

常用操作速查

# === 服务管理 ===
firewall-cmd --permanent --zone=public --add-service=http
firewall-cmd --permanent --zone=public --remove-service=http

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

# === 富规则 ===
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" source address="10.0.0.0/8" port port="3306" protocol="tcp" accept'

# === IP 封禁 ===
firewall-cmd --permanent --zone=public --add-rich-rule='
  rule family="ipv4" source address="1.2.3.4/32" drop'

# === 端口转发 ===
firewall-cmd --permanent --zone=external \
  --add-forward-port='port=80:proto=tcp:toport=8080:toaddr=192.168.1.10'
firewall-cmd --permanent --zone=external --add-masquerade

# === 重载 ===
firewall-cmd --reload

# === 查看 ===
firewall-cmd --list-all
firewall-cmd --zone=public --list-all
firewall-cmd --list-rich-rules

# === 应急 ===
firewall-cmd --panic-on    # 紧急封锁
firewall-cmd --panic-off   # 解除封锁

配置文件路径

路径用途
/etc/firewalld/firewalld.conf主配置文件
/etc/firewalld/zones/自定义 zone 配置
/usr/lib/firewalld/zones/系统默认 zone 配置
/etc/firewalld/services/自定义服务定义
/usr/lib/firewalld/services/系统默认服务定义
/etc/firewalld/ipsets/自定义 IPSet
/etc/firewalld/direct.xml直接规则配置