1. 批量放行背后的真实场景
说实话,大部分人对防火墙的认知停留在"装个图形界面、点几下按钮"的阶段,一旦遇到线上服务器就不敢动了。我最早接手某公司的测试环境时,接到一个需求:把内外网交互需要的端口一次性放行,涉及节点十几个,端口少说三四十个。刚开始我也是一台一台敲命令,后来发现纯粹是给自己挖坑——敲错一个端口、记错一个区间,排查起来比写规则本身更痛苦。
CentOS7 默认的防火墙是 Firewalld,它的规则配置方式比传统的 iptables 要"动态"不少,配置文件也更有规律。但规律不等于简单,尤其是当你需要批量操作的时候,文件内容的结构、加载顺序、优先级这些细节全都会冒出来。这篇博文我会从配置文件入手,讲清楚批量放行端口的几种实操方法,也会把踩过的坑一并交代。
这篇文章适合谁看?需要运维服务器的人、刚接触 CentOS7 防火墙的开发者、还有那些已经用了 Firewalld 但一直靠敲命令续命、从来没认真读过配置文件的人。读完你至少能回答这几个问题:Firewalld 配置文件到底怎么组织?批量放行有哪些靠谱手段?为什么有时候规则写了却不生效?
2. Firewalld 的配置文件,到底长什么样
2.1 核心配置目录和文件定位
Firewalld 的配置分两种:一种是运行时配置,修改后立即生效,但机器重启就丢;另一种是持久化配置,写入到磁盘文件里,重启后依然保留。持久化配置默认放在/etc/firewalld/目录下。
这个目录下面常见的子目录有:
zones:存放区域配置文件,每个文件对应一个 zone,默认的 public.xml 就在这里面。services:存放服务定义文件,比如 http.xml、https.xml。icmptypes:ICMP 类型定义,一般不需要动。rules:这是新版 Firewalld 提供的富规则目录,标准命名是zone-<区域>.xml。
如果你机器上没找到/etc/firewalld目录,先确认一下 Firewalld 是否安装并启动,很多时候是服务没装,自然没有配置目录。
注意:修改持久化配置文件之前,建议先执行
firewall-cmd --state确认当前防火墙状态,再操作。不要在各个配置文件之间来回跳着改,改完不知道哪份是准的,很容易把线上环境搞乱。
2.2 public.xml 的典型结构
public.xml 是 public 区域的配置文件,默认情况下,这是大多数人接入外网的网卡所在区域。看一个典型示例:
<?xml version="1.0" encoding="utf-8"?> <zone target="default"> <short>Public</short> <description>Unsolicited inbound traffic is not allowed.</description> <service name="ssh"/> <port port="80" protocol="tcp"/> <port port="443" protocol="tcp"/> </zone>这个文件结构不复杂,<service>代表放行某个预定义服务,<port>代表放行某个端口和协议。Firewalld 在启动或者 reload 的时候会读取这些文件,把规则加载进内核。
你要是把配置写错了,比如 protocol 写成了tcp/udp,实际加载就会出问题。协议字段只接受单个协议名称,正确的做法是用两个<port>分别写。
2.3 运行时配置和持久化配置的区别
这个必须说清楚,因为很多人会在这里栽跟头。
运行时配置保存在内存里,firewall-cmd --add-port=8080/tcp这种命令修改的就是运行时配置,立即生效,但不会写进磁盘文件。firewall-cmd --permanent --add-port=8080/tcp才是写入持久化配置的写法。
如果你只用了--permanent而没有 reload,那么内存里仍然没有新规则,看起来就是"规则写了但不生效"。反过来,如果你只用了不带--permanent的命令,规则倒是立即生效了,机器一重启,规则就灰飞烟灭。
批量操作场景下,推荐的做法是全部带上--permanent,最后执行一次重载或者运行时重新加载,让所有规则一次性落地。
3. 批量放行端口的核心思路
3.1 两种批量配置思路的对比
批量放行端口,本质上就是两条路:一是通过命令行循环执行;二是直接改配置文件。
命令行循环适合临时快速放行,配置文件适合需要长期保留、批量部署的场景。二者没有绝对的优劣,关键是看你的使用场景。
如果只是五六台机器、二三十个端口,用命令行循环完全够用,而且执行完了可以用firewall-cmd --list-all验证。如果是几十台机器、上百个端口,逐个敲命令很容易出错,而且每台机器执行一遍,时间成本也不低。这时候直接操作配置文件的优势就出来了:把一份写好的 public.xml 分发到各台机器,reload 一次全部生效。
3.2 为什么推荐用配置文件方式做批量
我的经验是,端口数量越多、机器数量越多,配置文件方式越靠谱。原因很简单:
- 配置文件可以被版本管理工具追踪,改了什么一目了然。
- 分发和备份都很方便,复制一份文件就能复现规则。
- 出问题的时候,打开文件就能看到规则的全貌,不用执行一堆命令去猜测当前状态。
- 命令行方式在循环里写错一个变量或者跑偏一个区间,排查起来比较痛苦。
当然,配置文件方式也有学习成本,至少得知道 XML 的基本结构,但这也是熟能生巧的事。
4. 批量放行端口的四种实操方法
4.1 方法一:命令行循环批量放行
最直接的做法是写一个简单的 Shell 循环。假设我们要放行 8000 到 8020 的 TCP 端口:
for port in $(seq 8000 8020); do firewall-cmd --permanent --add-port=${port}/tcp done firewall-cmd --reload这个命令的意思是用seq生成 8000 到 8020 的数字序列,逐个执行--add-port,最后统一重载。执行完可以用firewall-cmd --list-ports查看是否全部生效。
如果你要放的端口不是连续区间,而是不规则的一组,可以把端口写进一个文件,然后按行读取:
cat /root/open_ports.txt # 8080/tcp # 9090/udp # 3306/tcp while read port; do firewall-cmd --permanent --add-port=${port} done < /root/open_ports.txt firewall-cmd --reload这里有一个小细节要注意:--add-port后面必须带协议,8080/tcp和8080/udp是两个不同的条目,不要以为只写端口号就够了。
实操心得:如果你的端口清单里有重复项,循环执行并不会报错,但会给后续排查带来干扰,建议先用
sort -u去重一下。
4.2 方法二:直接编辑 public.xml 文件
如果喜欢"所见即所得"的配置方式,可以手动编辑/etc/firewalld/zones/public.xml。比如要放行多个端口,直接在<zone>节点里加入<port>子节点:
<zone target="default"> <short>Public</short> <description>Unsolicited inbound traffic is not allowed.</description> <service name="ssh"/> <port port="8080" protocol="tcp"/> <port port="8081" protocol="tcp"/> <port port="9090" protocol="udp"/> </zone>改完之后执行:
firewall-cmd --reload这里有一个关键点:/etc/firewalld/zones/目录下的文件如果不存在,你可以从默认配置复制一份过来,或者直接新建一个同名文件。默认区域文件的模板在/usr/lib/firewalld/zones/下面。
我个人的习惯是先复制默认文件过来再编辑,这样不会因为漏写基础属性导致加载异常。
4.3 方法三:使用富规则进行批量放行
富规则(Rich Rule)是 Firewalld 提供的一种更精细的规则表达方式,支持来源地址、端口范围、协议组合等。举一个例子,放行来自内网网段访问 TCP 1000-2000 端口的流量:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="1000-2000" protocol="tcp" accept' firewall-cmd --reload富规则还可以写进配置文件:
<zone target="default"> <short>Public</short> <description>Unsolicited inbound traffic is not allowed.</description> <service name="ssh"/> <rule family="ipv4"> <source address="192.168.1.0/24"/> <port port="1000-2000" protocol="tcp"/> <accept/> </rule> </zone>批量场景下,富规则主要有两个优势:一是可以减少规则条数,比如一个端口范围覆盖几十个端口;二是可以加入来源地址限制,让规则更安全,而不是对所有来源完全开放。
4.4 方法四:通过端口范围快速放行
Firewalld 的<port>节点其实支持写成端口范围,<port port="1000-2000" protocol="tcp"/>代表放行从 1000 到 2000 的所有 TCP 端口。
命令行也有对应的写法:
firewall-cmd --permanent --add-port=1000-2000/tcp firewall-cmd --reload需要留心的是,端口范围在--list-ports的输出里会以1000-2000/tcp的形式显示,不要把它当成单个端口。
批量放行连续端口区间时,范围写法能极大减少规则条数。但如果端口范围较宽,建议先想清楚是否真的有这个需求,因为端口放得越开,被扫描和利用的风险越大。
4. 完整实操示例:从规划到落地
4.1 需求规划
以我实际处理过的场景为例:某测试环境需要对外开放一组服务端口,包括 Web 服务 8080-8085、调试端口 9090(UDP)、数据库端口 3306,同时只允许来自内部管理网段的 SSH 访问,其余来源一律拒绝。机器上已经装好了 CentOS7,Firewalld 默认开启。
4.2 操作步骤
第一步,确认当前状态:
systemctl status firewalld firewall-cmd --state第二步,规划配置内容,先备份现有配置:
cp -r /etc/firewalld /etc/firewalld.bak.$(date +%Y%m%d)第三步,编写新的 public.xml。我推荐用富规则实现来源 IP 的限制,普通端口用<port>节点实现:
<zone target="default"> <short>Public</short> <description>Unsolicited inbound traffic is not allowed.</description> <service name="ssh"/> <port port="8080-8085" protocol="tcp"/> <port port="3306" protocol="tcp"/> <port port="9090" protocol="udp"/> <rule family="ipv4"> <source address="10.20.30.0/24"/> <service name="ssh"/> <accept/> </rule> <rule family="ipv4"> <source address="0.0.0.0/0"/> <service name="ssh"/> <drop/> </rule> </zone>这里要注意两个富规则的顺序:先放行内网网段 SSH,再丢弃其他来源的 SSH,规则的匹配顺序是先到先得,上面的规则优先命中。
第四步,加载配置:
firewall-cmd --reload firewall-cmd --list-all4.3 验证放行是否生效
验证端口放行是否成功,不能只看配置,还应该从外部发起真实连接。比如用另一台机器执行:
nc -zv <服务器IP> 8080如果通了,返回 Connected;如果不通,检查中间是否有其他防火墙拦截,以及目标机器上的规则是否真的写入。还有一个常见问题是云厂商的安全组,即使本机放行了,安全组没放行,端口依然不通。这个不在 Firewalld 的管辖范围内,但排查的时候一定要想到。
5. 常见问题与排查技巧
5.1 规则已写入但端口不通
这种情况大概率出在三个方面:
- 规则写进了配置文件,但没有执行 reload,当前运行时配置里根本没有新规则。解决办法是执行
firewall-cmd --reload。 - 端口放行的协议不对。比如服务跑的是 TCP,你放行的却是 UDP。
- 服务绑定地址不对。服务只监听了 127.0.0.1,外部网络自然连不上,这不是防火墙的问题,但排查顺序里一定要考虑。
5.2 reload 和完全重启的差异
firewall-cmd --reload会保留当前运行时规则,只是重新加载持久化配置。systemctl restart firewalld则是完全重启服务,所有运行时规则都会丢失,重新从配置文件加载。
批量修改配置后的常规操作是firewall-cmd --reload,不建议直接 restart,因为 restart 会瞬间断开所有已建立的连接,对线上影响比较大。
5.3 端口范围写法的坑
<port port="8080-8085" protocol="tcp"/>这种写法在配置文件里是合法的,但如果你使用firewall-cmd --add-port=8080-8085/tcp命令,得到的效果和配置文件写法相同。二者不要混用时产生误解。
还有一个容易踩的坑:命令行工具的补全或者引号问题。在 Shell 里如果端口区间写成8080-8085/tcp,没问题;但如果你漏了协议后缀,命令会直接报错:
Error: INVALID_PORT: Bad port (and/or protocol)5.4 配置文件权限错误导致加载失败
如果你是从其他地方复制来的 public.xml,复制后文件属主和权限可能不对。Firewalld 正常要求配置文件属主是 root,权限至少是 644。如果权限过严,服务可能读不了;如果权限过宽,会有安全风险。检查方法:
ls -l /etc/firewalld/zones/public.xml如果不是 root 所有,执行修正:
chown root:root /etc/firewalld/zones/public.xml chmod 644 /etc/firewalld/zones/public.xml5.5 常见问题速查表
| 问题现象 | 可能原因 | 检查/解决方式 |
|---|---|---|
| 规则写了但端口不通 | 未 reload | 执行firewall-cmd --reload |
| 端口通但来源不受限制 | 规则里没有来源限制 | 改用富规则并指定 source address |
| 重启后规则丢失 | 用了运行时配置没加 --permanent | 改用--permanent写入持久化配置 |
| reload 后服务连接受影响 | 不该用 restart 却用了 restart | 尽量用 reload,必要时分批次操作 |
| 配置文件报错 | 权限、属主错误或 XML 结构错误 | 修正权限、检查 XML 标签闭合 |
6. 批量操作中的避坑经验
批量操作最怕的不是规则复杂,而是"改完不知道改了什么"。我见过有人一次性批量放行了 20 个端口,然后其中一个写错协议,排查了几个小时才定位到问题。
建议在批量操作前,先把端口清单整理成表格,包含端口号、协议、用途、目标来源,再决定用哪种方式落地。如果端口是连续区间,直接用范围写法;如果端口是零散的一组,建议写进文件循环执行;如果想做来源限制,优先考虑富规则。
这里也有一个经验:规则不要追求"一次全写完",而是分模块写。先把基础放行端口写好,再写来源限制,最后再加拒绝规则。每写一个模块,做一次验证,这样即使出问题,也能快速定位到是哪一类规则引起的。
另一个容易忽略的点是:/etc/firewalld/zones/下可能有多个 zone 配置文件,比如public.xml、internal.xml。很多服务器有多个网卡,每个网卡可以绑定到不同 zone。如果你改了 public.xml,但是实际流量从 internal 区域进入,那么你的规则根本没有作用到对应的网卡流量上。检查网卡和区域的绑定关系:
firewall-cmd --get-active-zones这个命令会显示每个活跃 zone 绑定了哪些网卡或来源。确认当前服务所在的网卡绑定的区域,再决定改哪个配置文件。
7. 把批量操作变得更可靠的一些小技巧
如果你需要频繁批量调整端口,比如每周都在测试环境上加临时端口,重复写 XML 或者循环命令确实有一点枯燥。我后来做了个小优化:把端口清单写在文本文件里,用一段简单的脚本批量生成 XML 片段,再手动粘贴到 public.xml 中。
虽然手动拼 XML 也不是完全自动化,但至少避免了命令循环里逐条执行带来的耗时。你也可以用脚本把新规则追加到 XML 中,但要注意 XML 标签闭合的问题,所以更稳妥的做法是生成片段后人工确认再粘贴。
还有一个建议:每次改动 Firewalld 配置之前,至少做好两件事——备份配置文件、记录当前端口放行清单。当前清单可以通过一条命令导出:
firewall-cmd --list-all > /tmp/firewalld_list_$(date +%Y%m%d).txt这样一旦回滚,就知道最初的规则是什么状态。
8. 最后想说的话
Firewalld 的配置文件批量放行端口,本质上不难,难的是在操作之前把思路理清楚。你是想临时开几个端口,还是一套长期规则要部署到多台机器?想清楚这个问题,方法自然就出来了。
我个人在实际操作中的体会是,不要惧怕命令行循环,也不要一味依赖图形工具。命令行方式看似原始,但在批量场景下反而是最灵活、错误率最低的路径。配置文件方式则需要你对 XML 结构有一定掌控力,而这份掌控力其实来自多次踩坑之后的积累。
最后再分享一个小技巧:如果你经常在 CentOS7 上做防火墙调整,建议把常用的端口清单、网段信息整理成一份文档,放在服务器上或者内部笔记里。下次需要放行端口的时候,直接对照文档操作,比自己临时回忆要快得多,也不容易遗漏。希望这篇内容对你有帮助。