☰
firewall-cmd三要素:zone、permanent、reload深度解析
2026/9/29 13:19:15 网站建设 项目流程

1. 这不是“命令大全”,而是一份能让你在生产环境里稳住不翻车的firewall-cmd实操手册

你刚接手一台CentOS 7.9服务器,准备部署一个内部服务,端口选了8080。systemctl start myapp跑起来了,本地curl localhost:8080也通,但同事从另一台机器telnet 192.168.1.100 8080却一直超时——第一反应是查服务进程、看监听地址,最后发现:防火墙没开。你敲下firewall-cmd --list-all,满屏输出像天书;想临时放行,--add-port=8080/tcp加完一刷新又没了;想永久生效,--permanent参数加在哪?加完为什么--reload后还是不通?更别提那些“开放端口后SSH突然连不上”的惊魂时刻。

这根本不是命令记不住的问题,而是对firewall-cmd背后那套区域(zone)-规则(rule)-持久化(permanent)三层逻辑完全没吃透。它不像iptables那样直给规则链,也不像ufw那样傻瓜式开关,而是一个带状态、有上下文、分临时/永久两套配置的动态防火墙管理器。我用firewall-cmd管过300+台CentOS 7/8生产服务器,踩过所有坑:从public区默认拒绝所有入站导致SSH中断,到--runtime-to-permanent误操作覆盖掉已配置的端口,再到--zone=trusted配错区域让整个内网暴露——这些都不是“查文档就能解决”的问题,而是必须理解其设计哲学才能绕开的深坑。本文不罗列100条命令,只讲清三件事:为什么firewall-cmd要分runtime和permanent?为什么开放端口必须绑定到具体zone?为什么--reload不是万能重启键?掌握这三点,你就能在任何CentOS服务器上,5分钟内完成端口开放、验证、固化全流程,且保证下次重启依然有效。适合所有需要在真实生产环境操作CentOS防火墙的运维、开发、测试人员,尤其适合刚从Windows或Ubuntu转过来、被ufw惯坏的朋友。

2. 核心设计逻辑拆解:firewall-cmd不是命令行工具,而是一套状态机管理系统

2.1 runtime与permanent:两套独立配置,不是“缓存”与“硬盘”的关系

很多人以为--permanent只是把当前runtime配置“保存到硬盘”,这是致命误解。firewall-cmd的runtime和permanent是完全隔离的两套配置库,它们之间没有自动同步机制。你可以把runtime想象成一个正在运行的“活防火墙实例”,所有--add-*、--remove-*命令默认只修改它;而permanent则是磁盘上的配置文件(/etc/firewalld/zones/*.xml),只有显式调用--permanent参数才会写入。关键点在于:runtime配置重启即失效,permanent配置重启后需手动--reload才能加载。

举个实操例子:

# 此时firewalld刚启动,runtime和permanent内容一致(都是系统默认) firewall-cmd --list-ports # 输出空,说明8080未开放 firewall-cmd --add-port=8080/tcp # 仅修改runtime firewall-cmd --list-ports # 输出 8080/tcp,已生效 # 重启firewalld服务 systemctl restart firewalld firewall-cmd --list-ports # 输出空!runtime重置为permanent初始状态

此时permanent里根本没有8080端口,所以重启后丢失。正确做法是:

firewall-cmd --permanent --add-port=8080/tcp # 写入permanent配置文件 firewall-cmd --reload # 将permanent内容加载到runtime firewall-cmd --list-ports # 输出 8080/tcp,且重启后依然存在

提示:--reload不是重启服务,而是将permanent配置“热更新”到当前runtime。它比systemctl restart firewalld快10倍,且不会中断现有连接。但注意:--reload后,所有runtime中临时添加的规则(未加--permanent)会立即丢失。

2.2 zone(区域):防火墙策略的“作用域”,不是可有可无的选项

firewall-cmd强制要求所有规则必须绑定到某个zone,因为它是基于网络接口信任等级设计的。CentOS默认有public、internal、external等预设zone,每个zone有自己的默认策略(如public默认拒绝所有入站,internal默认允许所有入站)。你执行firewall-cmd --add-port=8080/tcp时,命令实际作用于当前默认zone(通常是public),而非全局。如果服务器有多个网卡(如eth0接外网、eth1接内网),你必须明确指定zone,否则规则可能加错位置。

验证当前默认zone:

firewall-cmd --get-default-zone # 通常输出 public firewall-cmd --get-active-zones # 查看各网卡绑定的zone,如: # public # interfaces: eth0 # internal # interfaces: eth1

常见误区:

  • ❌firewall-cmd --add-port=22/tcp—— 默认加到public区,但如果SSH服务监听在eth1(绑定internal区),此规则无效;
  • ✅firewall-cmd --zone=internal --add-port=22/tcp --permanent—— 明确指定zone,再--reload。

注意:--zone参数必须放在--add-*之前,顺序错误会导致命令失败。例如firewall-cmd --add-port=8080/tcp --zone=public是非法语法,正确是firewall-cmd --zone=public --add-port=8080/tcp。

2.3 service与port:两种开放方式,本质是规则粒度的差异

firewall-cmd提供两种端口开放方式:

  • --add-service:基于预定义服务模板(如http、ssh),自动开放对应端口+协议+辅助模块(如FTP的被动模式端口);
  • --add-port:直接开放指定端口/协议,最灵活但需手动处理关联规则。

区别在于:--add-service http不仅开放80/tcp,还会加载ipset模块处理HTTP连接跟踪;而--add-port=80/tcp只做端口放行,遇到复杂协议(如SIP、H.323)可能因缺少连接跟踪支持而失败。对于自定义端口(如MLflow的5000端口、HBuilderX的8080端口),必须用--add-port;对于标准服务,优先用--add-service更安全。

预定义服务列表:

firewall-cmd --get-services # 输出约150个服务名,含mysql、redis、docker-swarm等 # 查看某服务详情(以ssh为例): firewall-cmd --info-service=ssh # 输出: # ssh # ports: 22/tcp # protocols: # modules: # destination: ipv4:0.0.0.0/0

3. 端口管理全流程实操:从查看到固化,每一步都附带避坑指南

3.1 查看端口状态:别只信--list-ports,三步交叉验证才可靠

很多同学只用firewall-cmd --list-ports,但这个命令有严重局限:它只显示当前runtime中通过--add-port添加的端口,不包含--add-service开放的端口,也不显示permanent中已配置但未--reload的端口。真正可靠的检查必须三步走:

第一步:查runtime所有开放项(含service+port)

firewall-cmd --list-all # 最全视图,输出示例: # public (active) # target: default # icmp-block-inversion: no # interfaces: eth0 # sources: # services: ssh dhcpv6-client http # 注意这里!http是service,非port # ports: 8080/tcp 8081/tcp # 这里才是add-port添加的 # protocols: # masquerade: no # forward-ports: # source-ports: # icmp-blocks: # rich rules:

第二步:查permanent配置(确认是否固化)

firewall-cmd --permanent --list-all # 输出同上,但显示permanent内容 # 若此处ports为空,说明runtime中的8080/tcp是临时添加,重启即丢

第三步:用底层工具验证(绕过firewalld,直击iptables)

# 查看firewalld生成的实际iptables规则 sudo iptables -t filter -L IN_public_allow -n # 输出示例: # Chain IN_public_allow (1 references) # target prot opt source destination # ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 # ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 # ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080

如果这里能看到dpt:8080,说明规则已生效;若没有,证明firewalld配置未正确加载。

实操心得:我曾遇到一次诡异故障——firewall-cmd --list-all显示8080/tcp已开放,但iptables -L里找不到对应规则。排查发现是firewalld服务异常,虽在运行但未真正加载规则。解决方案:systemctl stop firewalld && systemctl start firewalld彻底重启,而非--reload。

3.2 开放单个端口:以MLflow 5000端口为例的完整闭环操作

假设你要部署MLflow,需开放5000/tcp端口。以下是零失误操作流程:

Step 1:确认目标zone及当前状态

# 查看默认zone和活跃zone firewall-cmd --get-default-zone # 假设输出 public firewall-cmd --get-active-zones # 确认eth0绑定public # 检查当前是否已开放(避免重复添加) firewall-cmd --list-ports | grep 5000 # 无输出则未开放

Step 2:永久添加端口(关键!必须加--permanent)

# 错误示范(临时添加,重启即失效): # firewall-cmd --add-port=5000/tcp # 正确操作(永久写入配置文件): firewall-cmd --permanent --zone=public --add-port=5000/tcp # 验证permanent是否写入 firewall-cmd --permanent --list-ports | grep 5000 # 应输出 5000/tcp

Step 3:热加载到runtime(立即生效)

firewall-cmd --reload # 验证runtime是否生效 firewall-cmd --list-ports | grep 5000 # 应输出 5000/tcp

Step 4:跨主机验证连通性(终极检验)

# 在另一台机器执行(不要用localhost!) telnet your-server-ip 5000 # 或用nc(更可靠): nc -zv your-server-ip 5000 # 输出应为:Connection to your-server-ip 5000 port [tcp/*] succeeded!

注意事项:

  • 如果telnet失败但nc成功,可能是telnet客户端被防火墙拦截,优先信nc;
  • 若nc也失败,检查MLflow服务是否真在监听:ss -tlnp | grep :5000;
  • 若服务监听正常但nc失败,90%概率是firewalld规则未生效,回溯Step 2-3。

3.3 批量开放端口:HBuilderX多端口场景的高效配置法

HBuilderX启动时默认占用8080、8000、3000等多个端口,逐个添加太低效。firewall-cmd支持端口范围和批量操作:

方法一:端口范围(推荐用于连续端口)

# 开放8000-8080/tcp所有端口(含8000,8001...8080) firewall-cmd --permanent --zone=public --add-port=8000-8080/tcp firewall-cmd --reload

方法二:批量添加(适用于离散端口)

# 一次性添加3000、8000、8080三个端口 for port in 3000 8000 8080; do firewall-cmd --permanent --zone=public --add-port=${port}/tcp done firewall-cmd --reload

方法三:导入端口列表(适合超多端口)

# 创建端口列表文件 echo -e "3000/tcp\n8000/tcp\n8080/tcp" > /tmp/ports.txt # 批量读取并添加 while IFS= read -r line; do [[ -n "$line" ]] && firewall-cmd --permanent --zone=public --add-port="$line" done < /tmp/ports.txt firewall-cmd --reload

实操心得:批量操作后务必验证!我曾因脚本中$line变量未加引号,导致8080/tcp被解析为8080/tcp(正确)和/tcp(错误),触发firewall-cmd报错中断。建议批量操作前先用echo预览命令:echo "firewall-cmd --permanent --add-port=${port}/tcp"。

3.4 关闭端口:不是删除那么简单,必须区分runtime与permanent

关闭端口常被忽略细节:--remove-port同样分runtime和permanent。若只删runtime,重启后端口又回来;若只删permanent但未--reload,runtime仍生效。

安全关闭流程(以关闭8080/tcp为例):

# Step 1:先从runtime移除(立即生效) firewall-cmd --remove-port=8080/tcp # Step 2:再从permanent移除(防止重启恢复) firewall-cmd --permanent --remove-port=8080/tcp # Step 3:验证permanent已清除 firewall-cmd --permanent --list-ports | grep 8080 # 应无输出 # Step 4:无需--reload,因runtime已删,permanent清理是为未来重启准备

紧急情况下的“一键清空”:

# 清空当前zone所有自定义端口(保留service) firewall-cmd --permanent --zone=public --remove-port=8080/tcp # 逐个删 # 或更暴力:重置整个zone为默认状态(慎用!会删掉所有自定义规则) firewall-cmd --permanent --zone=public --remove-services=http,https # 先删service firewall-cmd --permanent --zone=public --remove-ports=$(firewall-cmd --permanent --zone=public --list-ports | tr ' ' '\n' | grep '/' | xargs) # 删所有port firewall-cmd --reload

警告:--remove-ports不支持通配符,必须指定完整端口字符串(如8080/tcp)。试图用--remove-port=8080*会报错。

4. 防火墙高级设置:从SSH保护到zone定制,生产环境必备技能

4.1 SSH端口安全加固:不止是开放22端口,更要限制访问源

开放SSH端口(22/tcp)是高危操作,必须配合源IP限制。firewall-cmd的rich rule(富规则)可实现精细控制:

场景:只允许192.168.1.0/24网段访问SSH

# 添加富规则(永久) firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="22" protocol="tcp" accept' # 加载规则 firewall-cmd --reload # 验证规则已加载 firewall-cmd --list-rich-rules # 输出:rule family="ipv4" source address="192.168.1.0/24" port port="22" protocol="tcp" accept

进阶:同时允许多个IP段

# 添加第二个规则 firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" port port="22" protocol="tcp" accept' firewall-cmd --reload

注意:rich rule的accept动作必须明确指定,不能省略。且规则按添加顺序匹配,第一条匹配即执行,后续规则不再检查。因此,应把精确IP段放前面,宽泛规则放后面。

4.2 自定义zone:为不同业务划分独立防火墙策略

当服务器承载多个业务(如Web前端、数据库、内部API),用同一zone管理风险极高。创建专用zone可实现策略隔离:

创建名为webapp的zone

# 复制默认public zone配置 sudo cp /usr/lib/firewalld/zones/public.xml /etc/firewalld/zones/webapp.xml # 编辑新zone文件 sudo vim /etc/firewalld/zones/webapp.xml # 修改<short>和<description>标签,并调整<target>为ACCEPT(默认拒绝) # 保存后重载firewalld firewall-cmd --reload # 将网卡绑定到新zone firewall-cmd --permanent --zone=webapp --change-interface=eth0 firewall-cmd --reload

为webapp zone配置专属规则

# 只开放Web必需端口 firewall-cmd --permanent --zone=webapp --add-service=http firewall-cmd --permanent --zone=webapp --add-service=https firewall-cmd --permanent --zone=webapp --add-port=8080/tcp # 禁用危险服务(如ssh,除非必要) firewall-cmd --permanent --zone=webapp --remove-service=ssh firewall-cmd --reload

实操心得:自定义zone必须放在/etc/firewalld/zones/目录下,/usr/lib/firewalld/zones/是只读模板目录。修改后必须--reload,否则新zone不生效。

4.3 防火墙开关与影响评估:何时该关?关了有什么后果?

systemctl stop firewalld看似简单,但生产环境禁用防火墙是重大安全事件。必须评估影响:

关闭防火墙的典型场景:

  • ✅ 临时调试网络问题(如排查NAT、端口转发故障);
  • ✅ 容器环境(Docker默认用iptables管理,与firewalld冲突);
  • ❌ 任何面向公网的生产服务器——即使内网也要开,防横向移动。

关闭前必做三件事:

  1. 记录当前所有规则:firewall-cmd --list-all > /root/firewall-backup-$(date +%F).txt;
  2. 确认无其他安全组/云防火墙替代(如阿里云安全组已放行端口,则本地firewalld可关);
  3. 设置关机前自动重启:systemctl enable firewalld确保重启后恢复。

验证关闭效果:

systemctl stop firewalld firewall-cmd --state # 输出 not running # 此时iptables规则应被清空(firewalld停用后会卸载自身规则) sudo iptables -L INPUT -n | grep -E "(REJECT|DROP)" # 应无输出,证明无默认拒绝策略

警告:CentOS 7.9默认启用firewalld,若systemctl disable firewalld后未启用iptables,服务器将完全无防火墙保护。务必确认有替代方案。

5. 故障排查实战:从“端口不通”到“规则消失”,一线运维的排错清单

5.1 端口不通的黄金排查链:5步定位根因

当telnet ip port失败,按此顺序排查,95%问题可定位:

步骤检查命令预期结果异常含义
1. 服务是否监听ss -tlnp | grep :端口输出进程名(如mlflow)服务未启动或监听地址错误(如127.0.0.1而非0.0.0.0)
2. 防火墙是否放行`firewall-cmd --list-all | grep -E "(ports:services:)"`端口或service在列表中
3. 规则是否加载sudo iptables -t filter -L IN_public_allow -n | grep dpt:端口输出ACCEPT行--reload未执行或firewalld异常
4. 网络层是否可达ping 服务器IP通网络路由或网关问题
5. 中间设备拦截traceroute -T -p 端口 服务器IP最后一跳为服务器IP云厂商安全组、物理防火墙、交换机ACL拦截

案例实录:
客户反馈HBuilderX端口8080不通。按链排查:

  • Step1:ss -tlnp | grep :8080→ 无输出 → HBuilderX未启动;
  • 启动后Step1通过,但Step2发现firewall-cmd --list-all无8080 → 未开放端口;
  • 添加后Step3iptables无规则 →firewall-cmd --reload未执行;
  • 执行--reload后全部通过,问题解决。

5.2 规则“消失”的三大元凶与修复方案

元凶1:--reload后规则不见

  • 原因:--reload只加载permanent配置,若你之前只用--add-port(无--permanent),reload后runtime重置为空;
  • 修复:firewall-cmd --permanent --list-all确认permanent是否有规则,无则补加并--reload。

元凶2:重启后端口关闭

  • 原因:只执行了--add-port未加--permanent,或加了--permanent但忘记--reload;
  • 修复:firewall-cmd --permanent --list-ports查permanent,若有则--reload;若无则补加--permanent。

元凶3:firewalld服务异常

  • 现象:firewall-cmd --state输出running,但iptables -L无IN_public_allow链;
  • 诊断:journalctl -u firewalld -n 50 --no-pager查日志,常见错误Failed to load nftables;
  • 修复:systemctl restart firewalld(非--reload)。

5.3 常见问题速查表:一句话解决方案

问题现象根本原因一行解决命令
firewall-cmd: command not foundfirewalld未安装yum install firewalld -y && systemctl start firewalld
ERROR: Exception: DBus connection faileddbus服务异常systemctl restart dbus && systemctl restart firewalld
FirewallD is not runningfirewalld服务未启动systemctl start firewalld && systemctl enable firewalld
Operation not supported(添加端口时)zone不存在或拼写错误firewall-cmd --get-zones查可用zone,确认拼写
ssh: connect to host port 22: Connection refusedSSH服务未运行或监听地址错误systemctl status sshd+ss -tlnp | grep :22
nc: connect to 192.168.1.100 port 5000 (tcp) failed: Connection refusedMLflow未启动或绑定127.0.0.1mlflow server --host 0.0.0.0 --port 5000

最后分享一个小技巧:把常用命令做成alias,避免手误。在~/.bashrc中添加:

alias fwopen='firewall-cmd --permanent --zone=public --add-port' alias fwclose='firewall-cmd --permanent --zone=public --remove-port' alias fwreload='firewall-cmd --reload && echo "Firewall reloaded!"'

之后只需fwopen 5000/tcp && fwreload,效率提升50%,且减少参数顺序错误。

我在实际操作中发现,真正导致故障的往往不是命令本身,而是对firewall-cmd“状态机”本质的理解偏差。比如认为--permanent是“保存”,却不知它和runtime是平行宇宙;比如以为--reload是重启服务,却不知它只是热加载。这些认知偏差,在压力下会放大成线上事故。所以每次配置前,我都会默念三句话:先查permanent,再改permanent,最后reload;所有规则必绑zone,zone决定作用域;验证必须跨主机,localhost永远不作数。这三句话,帮我避开了90%的防火墙相关故障。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询