常在Linux服务器上折腾的人,基本都有过这种经历:应用部署得妥妥当当,端口监听也正常,本机访问一切顺利,但换台机器过去就连不上。这时候大多数人第一反应是去查代码、查网络,折腾半天才发现,真正的拦路虎是防火墙。CentOS作为服务器领域最常见的发行版之一,防火墙操作几乎是每个运维、开发乃至自学Linux的新手都绕不开的基础内容。
这篇文章我想把CentOS防火墙这套东西掰开揉碎讲清楚,从最基础的启动、停止,到开放端口、修改配置文件,再到常踩的坑和排查思路,一条龙梳理一遍。内容不玩虚的,全部基于实际操作经验,新手照着抄作业也能搞定,老手也能从一些问题排查章节里查漏补缺。
1. 动手之前先搞清楚:CentOS防火墙到底有几套体系
1.1 firewalld、iptables、nftables到底是什么关系
很多新手在CentOS上查防火墙资料,会越查越乱:一会儿有人说用iptables,一会儿有人说用firewall-cmd,还有人提nftables,到底听谁的?其实这不怪你,是CentOS自己经历了几个时期的变更。
先把时间线理清楚:
| 系统版本 | 默认防火墙管理工具 | 底层实现 | 配置文件 |
|---|---|---|---|
| CentOS 6 | iptables 服务 | netfilter | /etc/sysconfig/iptables |
| CentOS 7 | firewalld | iptables / nftables | /etc/firewalld/ |
| CentOS 8 / 9 | firewalld | nftables | /etc/firewalld/ |
CentOS 6那个年代,系统里跑的是一个叫iptables的服务,规则直接写到/etc/sysconfig/iptables这个文件里,改完要么service iptables restart,要么重新加载,非常传统。从CentOS 7开始,默认换成了firewalld,它本身不是防火墙的实现,而是一个动态管理工具,真正干活的依然是内核里的netfilter模块,只是通过firewall-cmd这样的命令来管理,比直接摆弄iptables规则要友好得多。
到了CentOS 8,底层又换成了nftables,这是新一代的内核包过滤框架,语法更简洁、更灵活。但好消息是,对使用者来说,日常命令几乎没变,还是firewall-cmd那套。
很多人会把firewalld和iptables当成两个完全独立的东西,其实不对。你可以把内核的netfilter框架理解成一块电路板,iptables和nftables是两种不同规格的接线方式,而firewalld是外壳上的控制面板,它内部既可以用旧式接线,也可以切换到新式接线。CentOS 7的firewalld默认走iptables接口,CentOS 8开始默认走nftables接口,但对外暴露的firewall-cmd命令基本保持一致。
实际工作中,你很可能碰上同一台服务器上既有firewalld又有iptables服务的情况,这时候要特别小心,两个服务同时启用会导致规则混乱,后面我会专门讲这个坑。
1.2 区域(zone)是理解firewalld的钥匙
学习firewalld,第一个绕不开的概念就是zone,中文叫“区域”。很多资料把zone说得玄乎,其实我觉得用一个生活场景类比更好懂:想象你家小区有不同的门禁等级,大门口的访客登记最严格,单元门要刷门禁卡,你自己家门口是指纹锁最亲松。firewalld的zone就是提前定义好的几套“门禁策略”,根据网卡的信任程度套用不同的规则。
CentOS默认带了9个zone,分别是:
| 区域名称 | 默认策略 | 典型用途 |
|---|---|---|
| drop | 丢弃所有入站连接,不做任何回复 | 最严格的隔离区 |
| block | 拒绝所有入站连接,回复ICMP拒绝消息 | 严格隔离但可见 |
| public | 不信任该网络,仅放行少数明确允许的端口 | 公网接入场景 |
| external | 不信任网络,做NAT转发 | 路由器/网关外网口 |
| internal | 信任网络,通常用于内网 | 内网服务器 |
| dmz | 允许部分端口对外 | 隔离区服务器 |
| work | 信任网络中的工作机器 | 办公网 |
| home | 信任家庭网络 | 家用环境 |
| trusted | 接受所有入站连接 | 完全信任的内网 |
CentOS默认的zone是public,这也是很多服务器初装好之后的状态。在public区域下,默认只放行了SSH(22端口),其他端口一律拦在门外。这就是为什么你自己跑个Web服务监听8080,从本机curl localhost:8080有反应,但外部IP访问却死活不通。
理解zone的价值在于:你不用把系统想象成“只有开和关两个状态”,而是可以根据网络环境选择不同的策略模板。比如一台内网备份服务器,可以直接切到trusted区域,免去一条条开端口的麻烦;一台暴露在公网的Web服务器,老老实实用public,只放行80、443,顶多加一个改过的SSH端口。
1.3 查看当前防火墙配置的常用命令
在动手操作之前,先学会看现状,这比上来就敲命令重要得多。我自己排查服务器问题,第一件事永远是看三个东西:服务状态、当前区域、已放行的端口。
# 查看firewalld服务是否在运行 systemctl status firewalld # 或者用更简洁的方式 firewall-cmd --state # 查看默认区域 firewall-cmd --get-default-zone # 查看当前所有网卡绑定的区域 firewall-cmd --get-active-zones # 查看某个区域的所有配置 firewall-cmd --zone=public --list-allfirewall-cmd --zone=public --list-all这条命令输出的信息量很大,它会列出public区域下放行了哪些服务(services)、哪些端口(ports)、有没有配置富规则(rich rules)。我习惯把这串输出截图存在服务器维护文档里,改防火墙之前对比一下,改完再对比一次,防止误改。
还有一个容易忽略的操作:如果你有多块网卡,可以给每块网卡指定不同的zone。比如内网网卡绑trusted,外网网卡绑public,这样内网来的请求全放行,外网来的请求走严格策略。命令是:
# 把ens33这块网卡绑定到trusted区域 firewall-cmd --zone=trusted --change-interface=ens33 # 永久生效 firewall-cmd --permanent --zone=trusted --change-interface=ens33不过说实话,云服务器上这种多网卡多zone的场景用得不多,本地机房或者自己搭的物理服务器上更常见一些。
2. CentOS防火墙开启、关闭与开机自启操作指南
2.1 确认防火墙当前状态:三种查看方式
有朋友问我,怎么知道自己服务器防火墙到底是开着的还是关着的?这个问题看着简单,但真有人会在状态判断上栽跟头,因为“防火墙关了”有两种理解:一种是服务本身停了,另一种是服务还开着但里面没有任何拦截规则。
最直接的方式是用systemctl status firewalld,输出里会明确写Active: active (running)或者Active: inactive (dead)。不过有经验的运维更习惯用firewall-cmd --state,它只输出一个简单的running或not running,用在脚本里判断非常方便。
还有一种情况,服务显示在运行,但firewall-cmd --list-all一看ports和services全是空的,这种状态实际上和没开防火墙差不多。所以我的建议是,三个命令配合使用:
# 1. 看服务状态 systemctl is-active firewalld # 2. 看防火墙运行状态 firewall-cmd --state # 3. 看默认区域的实际规则 firewall-cmd --list-all第一条命令判断服务有没有跑,第二条是firewalld自己的状态,第三条看它到底拦了什么。如果第三条里空空如也,那说明当前区域没有放行任何端口,按默认规则,除了部分服务外其他入站流量都会被拒——这时候别被“服务在运行”蒙蔽了,它其实正在很勤快地挡你的包。
2.2 启动、停止、重启防火墙的完整命令
CentOS 7以上用的是systemd,所以管理防火墙服务本质上就是管理一个systemd单元,命令非常规律,记住动词就能举一反三。
# 启动防火墙 systemctl start firewalld # 停止防火墙 systemctl stop firewalld # 重启防火墙(先停后启,会短暂断流) systemctl restart firewalld # 重新加载配置(不中断现有连接,推荐日常使用) systemctl reload firewalld # 设置开机自启动 systemctl enable firewalld # 取消开机自启动 systemctl disable firewalld这里面的细节区别,很多人没留意。restart是彻底停止再重新启动防火墙服务,过程中所有连接都会中断重连,如果你正通过SSH远端操作,理论上会断一下,好在firewalld启动非常快,实际影响可能就是一秒钟的事。而reload只是重新读取配置文件,不会中断现有连接,适合改了端口规则之后热加载。
在防火墙操作里,还有一个和disable长得很像但作用更狠的命令:
# 彻底锁死,防止firewalld被任何方式启动 systemctl mask firewalld # 解除锁死 systemctl unmask firewalldmask会把firewalld的单元文件符号链接到/dev/null,任何试图启动它的操作都会失败。这个用法比较极端,一般只有在确定要用iptables替代firewalld时才用。日常关防火墙,disable就够用了。
2.3 开机自启动与永久关闭的取舍
先说说最基础的现象:很多人用systemctl stop firewalld把防火墙停了,以为一了百了,结果发现重启服务器后防火墙又回来了。原因很简单,stop只管当前运行状态,disable才管开机要不要启动。改完状态一定要记得改自启动,不然下次重启就“打回原形”。
# 当前关闭 + 开机不启动,彻底关掉防火墙 systemctl stop firewalld systemctl disable firewalld反过来的场景也一样,如果你某天需要临时把防火墙开起来,但下次开机不想让它自启,就只执行systemctl start firewalld,别碰enable。
这里必须多说一句,生产环境真的不建议永久关闭防火墙。这不是让你死板,而是防火墙本身是个安全边界,关掉之后服务器等于对全网裸奔。尤其是云服务器,除了系统防火墙,安全组里也应该规则收敛。如果你觉得防火墙一直拦着业务端口很烦,正确思路是把用到的端口和来源IP精确放行,而不是一关了之。真正需要关闭防火墙的场景,一般是内网测试环境、临时跑个Demo、本机做开发调试,这种环境里关了确实省心。
2.4 生产环境到底能不能直接关防火墙
每过一段时间就有同事来问我:“我能不能把防火墙直接关了?”我的回答永远是:能,但你要清楚代价。防火墙不光是挡“坏人”的,它还拦了一堆扫描、探测、异常请求。一旦关闭,服务器就完全暴露在网络里,指望靠应用本身防御,事后再排查异常流量,成本远比提前配置几条规则高得多。
有一个折中方案值得推荐:把默认区域的策略改得更严格,然后只放行真正的业务端口。比如一台只跑Nginx的服务器,最终public区域里只留SSH和80/443。多出来的端口一个都不放,效果其实跟“关防火墙”差别没那么大,但安全性完全不是一个量级。
如果你实在拿不准这台服务器该开哪些端口,可以先用ss -lntp看看当前监听了哪些端口:
ss -lntp这条命令会列出所有TCP监听端口和对应的进程,是梳理服务器端口的利器。把这份清单和业务负责人核对一下,就知道哪些端口是真正需要对外放行的,哪些只在本地回环地址上监听,根本不需要开给外面。
3. 开放端口的四种实操方法(新手直接抄作业)
3.1 firewall-cmd一行命令开放指定端口
这是最常用、也最推荐的方式。开放一个TCP端口的标准命令如下:
# 放行8080端口的TCP流量,并写入永久配置 firewall-cmd --zone=public --add-port=8080/tcp --permanent # 重新加载,让配置生效 firewall-cmd --reload我第一次用这个命令的时候也困惑过:为什么加了--permanent还要再--reload一次?这里要解释一下firewalld的设计逻辑。它把规则分成运行时(runtime)和永久(permanent)两套。不加--permanent的修改,立刻生效,但重启防火墙或重启服务器以后就消失了;加了--permanent,只写入配置文件,当前运行状态没变,必须--reload或者重启服务才会把配置文件加载进来。
所以最稳妥的习惯就是:要么全加--permanent再reload,要么临时只做runtime但心里清楚它重启就没了。千万不要一条加--permanent一条不加,混着用之后,你都分不清当前生效的规则和配置文件里存的规则是不是一致的。
放行UDP端口同理,把协议改成udp就行:
firewall-cmd --zone=public --add-port=53/udp --permanent firewall-cmd --reload查看已经放行的端口:
firewall-cmd --zone=public --list-ports如果要撤掉某个端口:
firewall-cmd --zone=public --remove-port=8080/tcp --permanent firewall-cmd --reload3.2 端口范围与来源IP限制的进阶配置
除了单个端口,firewalld还支持一次性放行一段连续端口,语法几乎一样:
# 放行10000到20000之间的所有TCP端口 firewall-cmd --zone=public --add-port=10000-20000/tcp --permanent firewall-cmd --reload这个需求最常见于FTP被动模式。FTP客户端连接服务器时,除了21端口,被动模式下还会随机使用一段高位端口。很多人在云主机上搭FTP,只放行了21,结果客户端登录成功,一列目录就卡死,或者传大文件中途断开,十有八九就是被动端口段没放行。直接用--add-port=40000-50000/tcp这种方式把高位数口段整段放开,问题立刻解决。
再进阶一点,限制来源IP会让规则更安全。比如数据库端口3306,理论上只有运维跳板机和业务服务器才应该访问,如果直接对所有来源放行,风险很大。这时可以用富规则(rich rule):
# 只允许192.168.1.100这个IP访问本机3306端口 firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.100" port protocol="tcp" port="3306" accept' --permanent # 只允许192.168.1.0/24这个网段访问 firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="3306" accept' --permanent firewall-cmd --reload富规则相当于自定义一条零散策略,比单纯的开放端口更精细。如果你要管理大量这样的规则,建议用--remove-rich-rule把规则原样去掉,或者直接用--reload配合配置文件统一维护。
3.3 直接修改public.xml配置文件的场景
除了命令行,直接改配置文件也是一个正经路子。firewalld的永久配置存放在/etc/firewalld/zones/目录下,默认区域的配置就是public.xml。
一个典型的public.xml长得这样:
<?xml version="1.0" encoding="utf-8"?> <zone> <short>Public</short> <description>For use in public areas.</description> <service name="ssh"/> <service name="dhcpv6-client"/> <port port="8080" protocol="tcp"/> <port port="10000-20000" protocol="tcp"/> </zone>你完全可以手动往这个文件里加<port>标签,然后执行firewall-cmd --reload。但我的建议是:能命令搞定的不要手改文件。原因很简单,命令有语法校验,写错了会当场报错提示;直接改XML,如果标签格式不对,firewall-cmd --reload可能直接失败,而且错误信息经常是“parse error”,排查起来烦得很。
不过有一种场景下,改配置文件反而更高效:服务器需要统一批量下发端口。你可以在一台基准机器上配置好所有端口,把public.xml拷贝到其他服务器的相同目录,然后逐台firewall-cmd --reload。几百台服务器批量操作,命令行一条条敲显然不现实的,这时候配置文件模板就派上大用场了。
3.4 用iptables命令临时放行端口的备用方案
有时候firewalld服务挂了、起不来,或者就是不想引入firewalld这套体系,还可以直接使用iptables命令来快速放行端口。前面说过,从CentOS 7起默认管理工具是firewalld,但系统里通常还保留了iptables命令,而且firewalld本身也兼容看懂iptables规则——底层都是netfilter。
直接开放一个端口的临时规则:
# 在INPUT链首部插入一条放行规则 iptables -I INPUT -p tcp --dport 8080 -j ACCEPT这条命令立即生效,但重启后失效。如果用的是CentOS 7系统,而且系统里安装并启用了iptables-services服务,可以把规则保存下来:
service iptables save它会将当前规则写入/etc/sysconfig/iptables,下次开机由iptables服务自动加载。这里有个前提,系统里必须有iptables-services这个包,CentOS 7默认没装,要自己安装:
yum install -y iptables-services systemctl enable iptables systemctl start iptables说到底,iptables方案更像是备用手段。日常管理还是推荐用firewalld,毕竟命令统一、配置文件清晰,还支持zone和富规则。iptables的历史包袱重,规则链又复杂,没必要在常规CentOS环境里给自己添乱。
4. 配置持久化与常见场景组合
4.1 运行时规则与永久规则的同步机制
firewalld这套“运行时+永久”双轨机制,是新手最容犯错的地方。刚才已经说过,不加--permanent的改动只对当前进程有效,加了--permanent必须reload才生效。但还有一种情况很多人不知道:如果你只做了runtime修改,想保留当前运行态的所有规则,可以直接执行:
firewall-cmd --runtime-to-permanent这命令的意思就是,当前正在生效的所有运行时规则,全部转换成永久配置。我有时候帮客户临时排查问题,先放行了一堆端口,问题解决了,再去决定哪些该留、哪些该删。确定要保留的,直接--runtime-to-permanent批量转永久,比我一个端口一个端口地补--permanent高效得多。
要特别提醒的是,执行--runtime-to-permanent之前,先花几秒钟看看当前规则,别把临时调试用的端口也一起固化进去了。我曾经亲眼见过有同事临时放行了PostgreSQL的5432端口,后面忘了清理,结果这个端口在服务器上敞开了半年才被发现,好在那是内网环境,否则又是一场安全事故。
4.2 常见服务端口放行组合
结合我维护服务器的经验,放行端口很少是单打独斗的场景,通常都是每个业务带一串端口。这里整理一份常见组合清单:
| 场景 | 端口与协议 | 建议放行范围 |
|---|---|---|
| Web服务 | 80/tcp、443/tcp | 建议对全放行 |
| SSH远程 | 22/tcp(若修改则用新端口) | 建议指定来源IP |
| DNS服务 | 53/udp、53/tcp | 对客户端网段放行 |
| 邮件服务 | 25/tcp、110/tcp、143/tcp、465/tcp、993/tcp、995/tcp | 按业务需求收敛 |
| FTP服务 | 21/tcp、20/tcp、被动端口段 | 被动端口段按客户端数量配置 |
| MySQL/PG数据库 | 3306/tcp、5432/tcp | 仅业务服务器或跳板机放行 |
| Redis缓存 | 6379/tcp | 仅内网放行,禁止公网暴露 |
| NFS共享 | 111/tcp、2049/tcp、40000-50000/tcp | 仅内网客户端网段 |
特别想强调数据库和三方中间件的端口,这是安全事故高发区。Redis未授权访问被挖矿的案例,我身边就发生过。配置防火墙时记住一个原则:端口号越小越常见,越常见越要收敛来源IP。Web服务的80/443全放行没问题,但数据库、Redis、MQ这些中间件端口,能指定IP就指定IP,实在做不到,至少要把来源网段限制在业务所在的内网段。
FTP端口组合再展开一句:如果用了pasv模式,被动端口范围需要在/etc/vsftpd/vsftpd.conf里配好,比如pasv_min_port=40000和pasv_max_port=40100,然后防火墙里放行这一段。只放21端口不配被动端口的,客户端能连上但列不出目录,一个经典到不能再经典的坑。
4.3 Docker容器端口与防火墙之间的坑
现在的服务器上跑Docker太常见了。Docker容器用-p 8080:80把容器端口映射到宿主机后,很多人发现外面的机器还是访问不了,这个问题一半出在防火墙,一半出在理解上。
先说场景:容器用桥接网络,-p 8080:80会把宿主机的8080端口映射到容器的80端口,数据包到达宿主机网卡后由Docker的iptables规则做DNAT转发。如果宿主机的firewalld没有放行8080端口,外部流量在到达宿主机的INPUT链时就被拦截了,根本轮不到Docker转发。
解决办法和普通端口一样,在宿主机防火墙里放行映射出来的端口:
firewall-cmd --zone=public --add-port=8080/tcp --permanent firewall-cmd --reload还有一类更隐蔽的情况:Docker会直接操作iptables的FORWARD链和DOCKER链,而firewalld管理的那套规则有时会互相干扰。比较常见的是,CentOS 7上firewalld和Docker同时存在,重启firewalld之后Docker的masquerade规则被重置,容器访问外网失败,但容器之间的通信正常。遇到这种情况,最省事的办法是重启Docker服务:
systemctl restart dockerDocker重启后会重建自己的iptables规则,问题通常就消失了。如果你反复遇到firewalld和Docker冲突,可以考虑把firewalld的端口规则好好梳理一遍,或者干脆把firewalld在Docker主机上停掉,只依赖安全组或硬件防火墙做边界防护——但这属于取舍问题,别轻易学,得看你们的安全规范允不允许。
5. 端口不通问题排查与常见故障实录
5.1 端口明明开了却连不上的排查思路
“端口在防火墙放行了,外部还是访问不了”是运维群和论坛里日经话题。我踩了无数坑之后,总结了一条固定的排查链路,从本机往外一层层测,很快能定位问题。
第一步,先确认端口确实在监听。用ss -lntp看端口和进程,重点看地址列是0.0.0.0还是127.0.0.1。如果只监听在127.0.0.1,那防火墙放不放行都没用,服务根本没对网络开放,得改应用配置让它监听在所有网卡上。比如Nginx的listen指令,如果你写的是listen 127.0.0.1:8080,外部永远访问不了。
第二步,本机自测。curl http://127.0.0.1:8080能通,说明服务正常;再curl http://服务器内网IP:8080,如果通,说明本机网络栈和防火墙对本地流量是放行的(firewalld默认对本地回环和本机访问不做拦截)。
第三步,用firewall-cmd --list-all确认端口在列表里。这里要顺带确认当前默认区域是不是public,如果你的网卡实际绑在internal或某个自定义zone上,你在public里开的端口根本不起作用。
第四步,外部机器测试。用telnet IP 8080或者nc -vz IP 8080,通了就万事大吉;不通继续往下查。
第五步,检查云安全组。现在大部分服务器跑在云平台上,除系统防火墙以外,云控制台的安全组也要放行端口。这个最容易被忽略,因为你明明在服务器上开了端口,实际上流量可能在云安全组那一层就被丢了。
第六步,检查SELinux。CentOS默认SELinux是enforcing状态,它管的不只是文件安全上下文,还会限制网络服务绑定端口。新版SELinux默认只对少数端口开放绑定权限,你如果用了一个非标准端口,而对应的布尔值没打开,服务会报“Permission denied”或者“Cannot assign requested address”。用audit2why看原因最直接。
这套链路走完,基本能把90%的“端口不通”问题揪出来。很多新人一卡就卡在“防火墙开了但访问不了”,其实防火墙只是排查链路里的一环,而且往往不是最终原因。
5.2 重启后规则丢失的常见原因
有一种熟悉的状态:当天辛苦配置了一堆端口,现场测试全通,过了一晚或者服务器一重启,端口又访问不了了,一看firewall-cmd --list-ports,空空的。
这个问题的根因,归根结底就一句话:规则没有写入永久配置。最容易犯的错误场景有两种。第一种是--add-port后面漏了--permanent,当时管用,但规则在防火墙服务重启后就被清掉了。第二种是加了--permanent但忘了--reload,配置文件里确实写了,但运行态没生效。从使用者的视角看,两种现象完全不同:第一种是现在能看到端口,重启后没了;第二种是端口立刻访问不通,reload之后才通。
排查方法很直接:
# 对比运行态和永久配置 firewall-cmd --list-ports firewall-cmd --permanent --list-ports两条命令输出一致,说明同步没问题;不一致,看是哪边缺了。按我自己的习惯,每次配置完之后,都会把上面两条命令都跑一遍,这样能立刻发现遗漏,不用等重启后再抓瞎。
如果你用的不是CentOS 7以上的版本,而是老掉牙的CentOS 6,情况会有些差异。CentOS 6的iptables服务,规则保存在/etc/sysconfig/iptables,保存规则的方式是service iptables save,如果你只用了iptables -I而没保存,重启后同样会丢。中心思想一样:永久和临时是两回事,别混为一谈。
5.3 SELinux拦截端口导致的连接失败
同样是端口放行了、服务在跑了,但外部连接被拒或者服务根本起不来的场景,很多老手也会被SELinux搞蒙。SELinux是CentOS的一个安全模块,它和防火墙是两套独立的机制,防火墙管网络层的流量进出,SELinux管进程资源的访问控制。
检查SELinux是否在拦截:
# 查看当前SELinux状态 getenforce # 查看近期的AVC拒绝日志 ausearch -m avc -ts recent输出里有denied字样,再用audit2why翻译一下拒绝原因:
ausearch -m avc -ts recent | audit2why常见的场景是给Nginx或Apache配置了一个非标准端口,比如8080,SELinux的安全策略里只允许httpd进程绑定80、81、443、488、8008这几个默认端口,绑8080会被拒绝。解决办法有两种:要么把端口加进SELinux的策略里,要么开启对应的布尔值。
# 把8080端口加入httpd允许绑定的端口列表 semanage port -a -t http_port_t -p tcp 8080 # 如果不想折腾SELinux,可以考虑临时允许某类服务的网络访问 setsebool -P httpd_can_network_connect 1这里需要提醒一下,-P参数表示持久化保存,不加的话重启后又恢复。SELinux的调试相对冷门,但它确实是CentOS端口访问故障里的“第三只拦路虎”,防火墙、安全组、SELinux三个层面都排掉,才敢说端口问题真的解决了。
5.4 firewalld与iptables服务冲突处理
前面提到过firewalld和iptables服务同时启用会出问题,这里展开说一下。CentOS 7上有两个服务:firewalld.service和iptables.service。它们都试图管理内核的netfilter规则,同时启用会产生两种结果:要么规则互相覆盖,要么服务启动时报错。
我曾经在一台测试机上既启用了firewalld又启用了iptables,结果firewalld正常,iptables规则却经常丢。排查了半天才发现,两个服务在启动顺序上互相较劲,firewalld启动时会清空或者重置一部分iptables规则,导致手工加的规则莫名其妙消失。
正确做法是二选一。你想用firewalld,就把iptables服务停掉并禁用:
systemctl stop iptables systemctl disable iptables反过来如果想用传统的iptables:
systemctl stop firewalld systemctl disable firewalld systemctl mask firewalld yum install -y iptables-services systemctl enable iptables systemctl start iptablesmask那一步是狠招,彻底防止firewalld被任何东西拉起来。正常场景下,disable就够了,但如果你所在的团队可能有其他人会顺手systemctl start firewalld,那就上mask,干净利落。
5.5 常见问题速查表
把自己在群里看到的高频问题和排查结论整理成一张表,方便直接对着查:
| 问题现象 | 最可能原因 | 排查命令 | 解决操作 |
|---|---|---|---|
| 服务已启动,外部无法访问 | 防火墙未放行端口 | firewall-cmd --list-ports | --add-port+--reload |
| 防火墙重启后规则丢失 | 未加--permanent | 对比两条list-ports命令 | --runtime-to-permanent |
| 端口已放行但外部不通 | 云安全组未放行 | 登录云控制台检查 | 在安全组增加入方向规则 |
| 本机通、外部不通 | 云安全组或upstream问题 | 检查安全组和NAT规则 | 调整安全组 |
| 服务监听在127.0.0.1 | 应用配置只绑定回环地址 | ss -lntp看监听地址 | 修改应用监听为0.0.0.0 |
| 非标准端口服务启动失败 | SELinux限制端口绑定 | ausearch -m avc -ts recent | semanage port -a |
| 容器端口映射后外部不通 | 宿主机防火墙未放行 | firewall-cmd --list-ports | 放行宿主机映射端口 |
| Docker容器无法访问外网 | firewalld重置了Docker规则 | systemctl status docker | systemctl restart docker |
| 防火墙开着看着像没开 | 当前zone不是默认zone | firewall-cmd --get-active-zones | 修改网卡绑定的zone |
这张表不敢说覆盖全部情况,但80%以上我实际遇到过的端口类问题都能在上面找到对应项。剩下的情况,基本要靠tcpdump抓包分析才能定位了。
再分享一个小技巧:配置完防火墙规则后,其实可以连续测试几次再收工,不要凭感觉。比如放行端口之后,从外部nc -vz IP 端口测两次,第二次专门测试规则持久化,确认重启防火墙服务之后依然通畅,才叫真正配置完成。这套“配置—验证—重启再验证”的流程,花不了几分钟,但能省下后面一大笔排查时间。
实际操作中,我的习惯是把每次防火墙变更都记录在案,哪一天为哪个业务放行了哪个端口,通过哪条命令或哪个配置文件实现的。日后业务下线或者迁移,照着清单一条条回收,把规则收敛回安全基线,反而成了最轻松的事。服务器维护这东西,做得越规范,后续越不操心。