做运维这些年,凡是跟Linux服务器打过交道的人,没有不被防火墙折腾过几回的。最典型的场景就是:Nginx配置好了,进程也在跑,从浏览器一访问就是“连接超时”,跑到服务器上curl 127.0.0.1却一切正常。问题十有八九出在Firewalld的防火墙规则上——很多Linux发行版的默认防火墙只放行了SSH,80、443这些端口没有显式允许之前,外网根本进不来。这篇文章我就把Firewalld规则系统从头理一遍,包括它的Zone(区域)模型、图形化配置工具、以及日常最常用的firewall-cmd命令。整个过程会贴出具体命令和实际场景,适合刚接触Linux的读者,也适合被线上防火墙规则坑过几次的运维老哥。
1. Firewalld的核心思路:Zone(区域)模型
1.1 为什么搞出Zone这套东西
在Firewalld之前,不少人习惯直接操作iptables。iptables的链和表管理功能强大,但对人的心智要求极高:一条规则写错,轻则服务访问异常,重则远程连接直接断开。我见过太多人写iptables规则写着写着就放弃了,最后一招就是清空所有规则,瞬间“一切都通了”。清空即安全其实是个错觉,生产环境这么干,等于把门锁拆了。
Firewalld最大的改变是把规则按信任级别划分成了几个zone。你不必再关心规则链的跳转顺序,只需要明确一件事:“哪个网段是可信的、哪个网段是半可信的、哪个网段完全不可信”。每个zone里面预定义了一组允许放行的服务、端口、协议,规则以语义化的方式组织在一起。往深了说,Firewalld的底层最终还是会落到iptables或nftables上,但它提供了一层动态管理机制,支持热加载,不再要求运维直接面对一堆带数字和状态的处理链。
打个比方,这就和物理世界的门禁系统一样。办公楼里不同区域有不同的门禁等级,访客从哪个门进来,能走到哪一层,门禁已经替你安排好了。你不需要知道每个保安具体站在哪,只需要设置好“访客能不能进入会议室所在的楼层”。Zone正是承担了这样的角色,把网络接口按照信任程度分配到不同策略组里。
1.2 九个默认Zone分别是什么
Firewalld安装后自带九个zone,每种zone的信任程度从严格到宽松排列。我整理成了一张表,方便你对照使用:
| Zone区域 | 默认行为 | 适用场景 |
|---|---|---|
| drop | 丢弃所有入站包且不回应,对方看到的主机如同不存在 | 最严格,防扫描、防探测 |
| block | 拒绝所有连接,但会回应icmp-host-prohibited或icmp6-adm-prohibited | 明确告诉对方“我不接受” |
| public | 不信任该网络,仅放行少量服务(如ssh、dhcpv6-client) | 默认zone,普通服务器对外网开放时 |
| external | 启用IPv4 NAT伪装,仅放行ssh | 路由器出口、边界网关 |
| dmz | 隔离区,仅放行少量允许进入的隔离服务 | 对外开放的缓冲主机 |
| work | 相对可信的工作网络,放行ssh、ipp-client、dhcpv6-client | 公司办公环境 |
| home | 更信任的家庭网络,放行ssh、mdns、ipp-client、samba-client、dhcpv6-client | 家庭局域网 |
| internal | 内部网络,信任程度与home类似 | 公司内网机架服务器 |
| trusted | 接受所有流量,不做任何拦截 | 完全可信的管理网段 |
注意,不同发行版里预置的服务名会有细微差别,比如有的版本在internal里多放行了cockpit。所以不要死记硬背,用之前最好先跑一下firewall-cmd --list-all看看真实配置。
1.3 一台双网卡服务器如何划分Zone
清楚了zone的含义,Zone的分配就顺理成章了。假设部署了一台双网卡服务器,一个网卡接入外部服务网关,另一个网卡接办公室内网网段。我的划分方式是:外部网卡放进public区域,只允许80、443、ssh;内部网卡放进internal区域,办公室同事可以直接连开发调试接口。这样对外和对内的策略天然分离,不再出现“一套规则管所有网”的尴尬局面。
绑定网卡到zone的命令很简单:
firewall-cmd --permanent --zone=internal --add-interface=eth1 firewall-cmd --reload需要说明的是,这里用--add-interface会把这个接口永久加入internal区域。如果你只是临时测试,就不加--permanent,reload之后会自动恢复原状。不少老哥容易在这里搞混,结果以为自己配好了,重启后网卡又回到默认zone,排查了半天。
2. 图形化配置工具:firewall-config怎么用
2.1 图形化工具的定位
很多运维老哥对图形化工具有种抗拒心理,觉得命令行才是专业。其实firewall-config在查看配置和单条规则调试时,效率往往比命令行高。它的定位不是“主力配置工具”,而是“可视化巡检工具”。生产环境大批量规则变更,我依然会写脚本走命令行;但有时候临时想看某台机器放行了哪些端口,打开面板一眼扫过去,比敲一串命令快得多。
另外,有些刚接触Linux的读者对命令行天然恐惧,图形化工具可以帮他们建立对防火墙规则的直观认识。使用它不需要先背命令,只要理解zone、服务、端口这几个概念就能上手。
2.2 安装与打开方式
RHEL系发行版安装很简单:
yum install firewalld firewall-config systemctl enable --now firewalld如果你是Debian系,包名可能略有区别,但大体逻辑差不多。打开方式有两种:一是在桌面环境的应用程序菜单里直接点“防火墙”;二是在终端直接输入firewall-config。
如果是通过SSH远程管理服务器,没有本地桌面也没有VNC,那图形界面基本用不了。虽然可以用X11转发凑合着看,但延迟会让人崩溃,而且需要服务器提前装了X组件。所以我一般建议:远程场景老老实实用命令行,图形化工具留给本地桌面的管理机用。
2.3 快速放行一个服务或端口
打开面板后,左边的区域列表显示的是所有zone,右边是当前zone的详细信息。操作顺序大概是:
第一步,顶部下拉菜单“Configuration”里切换成Permanent或Runtime,关系我在后面命令行部分会详细解释,这里先记住:Permanent是永久生效,Runtime是临时生效。第二步,左侧选择一个zone,比如public。第三步,右侧下方有“Services”“Ports”“Protocols”等选项卡,在Services里勾选http、https,或者在Ports里手动添加8080/tcp。第四步,点菜单栏“Options”里的Reload Firewalld,让永久配置加载到内存。
这套操作比命令行直观太多了,基本不需要记命令。但有一个细节必须注意:如果你选的是Runtime,所有改动在防火墙重启后都会丢失。你以为配好了,第二天一来访问又超时,这种情况我遇到不止一次。所以用图形工具配好后,一定记得在“Options”里执行Runtime to Permanent,把当前运行规则转成永久配置。
2.4 图形化工具的边界
图形化工具最大的硬伤,是复杂规则无法精细表达。比如:“仅允许172.16.0.0/16访问3306端口,并且记录日志”,这种带来源限制的富规则在面板上找半天都找不到入口。批量添加上百个端口时,图形化工具的操作成本也远高于命令行。
所以我的习惯是:图形工具用来查看、快速单点调试,命令行负责正式变更。两者互相配合,而不是非此即彼。你如果正在学Firewalld,我倒是建议先从图形化入手,把zone和服务的关系看明白了,再回头玩命令行,理解会深很多。
3. 主力工具:firewall-cmd命令实战
3.1 先搞懂runtime和permanent
firewall-cmd命令大部分看起来很简单,但如果没弄明白runtime和permanent的区别,迟早会踩坑。Firewalld的规则存在两个层面:运行时(runtime)规则,是当前内存里生效的规则,你敲一条firewall-cmd --add-port=8080/tcp,端口立即放通,但只要执行reload或者重启服务,这条规则就消失了。永久(permanent)规则,是写入配置文件的规则,重启后依然存在,但不会自动加载进内存,需要执行firewall-cmd --reload才会生效。
举个例子,你执行了:
firewall-cmd --permanent --zone=public --add-port=8080/tcp这条命令已经把8080写进了永久配置,但当前内存中仍然没有放行。接下来必须敲firewall-cmd --reload,让永久配置重新入内存。很多人刚接触时,喜欢直接加--permanent却忘记reload,然后发现端口还是不通,就开始怀疑命令写错了。这其实是绝大多数“规则加了没反应”问题的根源。
另外一个很有用的命令是firewall-cmd --runtime-to-permanent,把当前内存中的所有运行时规则保存为永久配置。调试完防火墙,确定某个端口可以正常访问了,再执行这条命令,比你一条一条重敲要省事得多。
3.2 查看规则的高频命令
动手修改之前,先掌握查询命令,这样整个配置过程会非常清晰:
firewall-cmd --state firewall-cmd --get-default-zone firewall-cmd --get-active-zones firewall-cmd --zone=public --list-all--state查看防火墙运行状态;--get-default-zone查看默认zone;--get-active-zones查看当前所有网卡和源地址绑定到了哪些zone,这是排查问题时用得最多的命令;--list-all则是把一个zone里的完整细节列出来,包括放行了哪些服务、端口、是否有伪装、是否有端口转发。如果想看永久配置,则在命令后加--permanent。
这里有个细节:--get-active-zones为什么重要?因为端口不通时,你首先要确认网卡到底在哪个zone里。很多人以为自己在public区域配置了规则,实际网卡却因为某种原因被绑定到了internal,那自然不生效。先查active zones,等于先确认自己操作的位置对不对。
3.3 添加、移除、重载规则的标准姿势
放行一个服务:
firewall-cmd --permanent --zone=public --add-service=http firewall-cmd --reload放行一个端口:
firewall-cmd --permanent --zone=public --add-port=8080/tcp firewall-cmd --reload移除端口:
firewall-cmd --permanent --zone=public --remove-port=8080/tcp firewall-cmd --reload为什么每次都要reload?因为--permanent只写配置,不更新运行规则,reload的作用是让配置文件和运行时保持一致。而reload和complete-reload有区别,我已经在后面的常见问题里细说,这里先记住:生产环境尽量用--reload,不要用--complete-reload。
如果你需要同时放行多个端口,可以写一行:firewall-cmd --permanent --zone=public --add-port=8080-8090/tcp。这种范围写法很方便,但要注意别把范围写得过大,否则跟直接信任一个网段没区别了。
3.4 富规则:指定源地址、端口和日志
富规则是firewall-cmd里最强大的功能,它可以做精细的源地址限制。举一个常见需求:公司的MySQL只允许内网172.16.0.0/16访问3306,望文生义地写--add-port=3306/tcp就又变成全网可访问了。
正确做法是使用富规则:
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="172.16.0.0/16" port protocol="tcp" port="3306" accept' firewall-cmd --reload带上日志功能时,加上log prefix="MYSQL"就行了。日志项会在调试和追溯攻击时很有用。还有一个容易被忽略的点:富规则里的source address只对来自该来源的流量生效,其他来源默认还是拒绝。这意味着你可以把数据库、后台管理页面这类敏感服务,只暴露给固定网段,而不是对所有人开放。
3.5 端口转发与NAT伪装
端口转发的场景也很常见,比如把内网某台机器的8080服务映射到公网443。配置分为两步:先开启masquerade,也就是NAT伪装:
firewall-cmd --permanent --zone=external --add-masquerade再添加转发规则,把本机443端口转发到192.168.1.10的8080:
firewall-cmd --permanent --zone=external --add-forward-port=port=443:proto=tcp:toport=8080:toaddr=192.168.1.10 firewall-cmd --reload这里有一个前提:masquerade必须开启,否则跨机转发不生效。如果只是本机端口转发,比如把80转到8080,可以不指定toaddr,但开启masquerade仍然更稳妥。需要说明的是,使用外部zone时,默认只信任ssh,如果你需要其他入站服务,也要一并加到external zone里。很多人telnet转发端口不通,并不是转发配置错了,而是external zone本来就拒绝入站,被防火墙拦在了最前面。
4. 三个真实场景的配置全过程
4.1 场景A:新服务器上线,开放Web服务
假设刚领到一台全新的CentOS服务器,系统默认防火墙是开启的,默认zone是public。你要在上面部署Nginx,让外网用户通过80和443访问。这时候执行的配置是:
firewall-cmd --permanent --zone=public --add-service=http firewall-cmd --permanent --zone=public --add-service=https firewall-cmd --reload如果是非标准端口,比如应用跑在8443上,就直接写端口:
firewall-cmd --permanent --zone=public --add-port=8443/tcp firewall-cmd --reload配置完成后验证方案:先在服务器上执行curl -I http://127.0.0.1确认服务本身正常,再从外部telnet或curl访问。如果服务正常但外部不通,第一件事执行firewall-cmd --get-active-zones和firewall-cmd --zone=public --list-all,看端口是否真的出现在public区域里。这一步可以过滤掉绝大多数配置问题。
另外,我建议顺手把https也放掉,即使现在还没配证书。很多团队先只放80,后面上SSL时又忘了放443,结果又是一轮“怎么访问不了”的排查。规则一次加全,比频繁变更要省心。
4.2 场景B:数据库只允许内网网段访问
这是另一类常见需求。某后台系统有MySQL,跑在172.16.10.5上,公司内网网段是172.16.0.0/16,其他来源一律不许连3306。如果用--add-port,那3306就对全网开放了,这不是我们想要的。
用富规则限定的方案:
firewall-cmd --permanent --zone=internal --add-source=172.16.0.0/16 firewall-cmd --permanent --zone=internal --add-service=mysql firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="172.16.0.0/16" port protocol="tcp" port="3306" accept' firewall-cmd --reload把内网网段加入internal区域,表示这个来源的流量走internal策略;再显式允许3306。同时,即使默认public区域没有放行3306,内网来源也可以正常访问。这种组合写法的好处是,内网用户能连数据库,而外网用户无论怎样都连不上。
如果是我,会更倾向于只用富规则那一行,限制得最精确。把--add-source和--add-rich-rule配合使用适合那种“内网还有多个vlan、不同vlan信任程度不同”的复杂网络。方案没有绝对对错,能说清原理就行。
4.3 场景C:端口转发,把外网请求转给内网服务
假设有一台网关服务器,公网IP绑在eth0上,内网里有台192.168.1.10的开发机,跑了一个应用开在8080。现在要让外部用户通过网关的443端口访问到这个内网应用。配置如下:
firewall-cmd --permanent --zone=external --change-interface=eth0 firewall-cmd --permanent --zone=external --add-masquerade firewall-cmd --permanent --zone=external --add-forward-port=port=443:proto=tcp:toport=8080:toaddr=192.168.1.10 firewall-cmd --reload这几条命令的顺序有讲究:先把网卡绑到external zone,再开masquerade,最后才是forward-port。masquerade是NAT伪装的总开关,没有它,后面的转发规则形同虚设。另外,目标地址必须是内网机器能直接到达的地址,不能是公网地址,否则数据包会绕路,多半转不过去。
这类转发配置排错时,可以先在网关上tcpdump -nni eth0 port 443看包是否到达,再到内网机器上tcpdump -nni eth0 port 8080看包是否成功转发。两边的包都对得上,说明防火墙规则没问题,剩下的就是应用层的问题了。
5. 常见问题与故障排查实录
5.1 规则明明加了,为什么端口还是不通
这个问题在我处理过的咨询里出现频率极高。排查思路要清晰,按顺序来。第一步,看端口有没有配到正确的zone里,执行firewall-cmd --get-active-zones确认网卡在哪个zone,再到对应zone执行firewall-cmd --zone=xxx --list-all。第二步,确认你是否执行了--reload,如果只是--permanent而没有reload,内存里根本没有新规则。第三步,检查是否有来源限制,很多富规则带了source address,来源不在范围内自然不通。第四步,如果还是不通,可能就不是防火墙问题,要查SELinux是否拦截,或者服务本身没有监听在0.0.0.0上,只是监听在127.0.0.1。
其中SELinux是个隐藏杀手。防火墙放行了,访问还是被拒,dmesg | grep avc能看到一大堆拒绝日志。这种情况下不是防火墙的锅,你需要按SELinux的报错去调整策略。
5.2 远程改规则,把自己锁在门外了
这是每一个运维都会经历的至暗时刻。最常见的操作失误是把默认zone改成drop或者block,然后执行reload,SSH连接瞬间断开,再也没能连上。
我现在的习惯是:改任何可能影响SSH的规则前,先开一个新的TTY窗口保持一个已登录的SSH会话;或者用at命令设定一分钟后自动恢复防火墙配置,给自己留条后路。例如:
echo "firewall-cmd --permanent --zone=public --add-service=ssh; firewall-cmd --reload" | at now + 2 minutes这样即使自己把规则改坏了,两分钟后防火墙也会自动把SSH放回来。真正锁死之后也别慌,优先走带外管理,比如服务器管理口、虚拟化平台的控制台。经历过一次就会明白:防火墙规则操作之前备份有多重要。
说到备份,我强烈建议把/etc/firewalld/整个目录定期备份。规则变更前用cp -a一份,或者纳入版本管理。这样出了问题可以快速回滚,不用回忆自己到底敲过什么。
5.3 Firewalld和Docker的恩恩怨怨
这是个老话题了。某些旧发行版(比如经典的CentOS 7)上,Firewalld和Docker之间经常互相踩脚。表现为:Docker容器发布端口后,只要执行一次firewall-cmd --reload,容器端口突然就不能访问了。
原因简单说,Docker通过iptables维护自己的NAT规则,Firewalld在做reload时,会在自己的规则链上做一轮重组,把Docker写入的规则给冲掉。新版本Firewalld配合nftables后端之后,这个问题大大缓解,但旧版本环境里依旧存在。
临时规避办法有几个:一是把Docker网桥接口加入trusted区域,让Firewalld不再干预容器网络;二是在Firewalld规则变更后,重启Docker服务让Docker重新写入规则。这两个办法都是“打补丁”,能解决一时问题,但不是根本解法。如果是生产环境,还是建议升级操作系统和Docker版本,彻底告别这种互相打架的年代。
5.4--reload和--complete-reload到底差在哪
这两条命令都能让永久配置重新加载,但影响范围完全不同。--reload是平滑重载,它会保留连接跟踪表里已有的established、related连接,正在通信的会话基本不受影响,所以SSH连接大概率不会断。--complete-reload则是彻底重置整个防火墙状态,所有基于防火墙建立的连接全部中断,包括你在用的SSH。
生产环境中,我几乎只用--reload。只有某条规则改完后发现系统状态异常,我才会考虑--complete-reload,而且前提是必须能通过控制台操作。这里顺便提一下panic mode,firewall-cmd --panic-on会立刻切断所有网络流量,连SSH也不放过,相当于把网线直接拔了。这种功能在应急时很好用,但没事千万别乱开,恢复时必须到物理控制台操作。
6. 我的规则管理习惯:把防火墙当代码看
工作几年下来,我最大的体会是:防火墙规则不是配置完成就结束的一次性操作,它应该像代码一样,有计划、有记录、有备份、有回滚方案。我现在管理生产环境的规则,大致流程是这样:先在测试环境复现一遍需求,确认规则条目和reload行为;然后在变更窗口内备份/etc/firewalld目录;接着执行变更命令;最后把firewall-cmd --list-all的输出保存一份快照,和变更前做对比。
做对比这个步骤特别有用。很多问题其实是“我以为自己改了两个端口,结果把之前放行的服务也带丢了”。有快照,一眼就能发现差异。如果你有版本管理习惯,把/etc/firewalld/zones/下的配置文件纳入管理,每次变更提交一次记录,那回滚基本上就是一瞬间的事。
最后再分享一个小技巧:写脚本批量添加规则时,每条命令都带上--permanent,最后统一执行一次firewall-cmd --reload。这样脚本即使中途报错,也不会留下一半运行时规则影响排查。批量变更最忌讳的就是边加边生效,出问题时你根本搞不清哪一步把配置弄脏了。Firewalld本身不复杂,大多数“疑难杂症”都出在操作习惯上,养成留后路、留快照、先测试的习惯,防火墙基本就不会再给你添堵了。