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/03. 端口管理全流程实操:从查看到固化,每一步都附带避坑指南
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/tcpStep 3:热加载到runtime(立即生效)
firewall-cmd --reload # 验证runtime是否生效 firewall-cmd --list-ports | grep 5000 # 应输出 5000/tcpStep 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冲突);
- ❌ 任何面向公网的生产服务器——即使内网也要开,防横向移动。
关闭前必做三件事:
- 记录当前所有规则:
firewall-cmd --list-all > /root/firewall-backup-$(date +%F).txt; - 确认无其他安全组/云防火墙替代(如阿里云安全组已放行端口,则本地firewalld可关);
- 设置关机前自动重启:
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 → 未开放端口; - 添加后Step3
iptables无规则 →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 found | firewalld未安装 | yum install firewalld -y && systemctl start firewalld |
ERROR: Exception: DBus connection failed | dbus服务异常 | systemctl restart dbus && systemctl restart firewalld |
FirewallD is not running | firewalld服务未启动 | systemctl start firewalld && systemctl enable firewalld |
Operation not supported(添加端口时) | zone不存在或拼写错误 | firewall-cmd --get-zones查可用zone,确认拼写 |
ssh: connect to host port 22: Connection refused | SSH服务未运行或监听地址错误 | systemctl status sshd+ss -tlnp | grep :22 |
nc: connect to 192.168.1.100 port 5000 (tcp) failed: Connection refused | MLflow未启动或绑定127.0.0.1 | mlflow 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%的防火墙相关故障。