
一台新服务器部署好 Nginx改完 SSH 端口装完数据库满心欢喜准备验证服务结果浏览器转圈、curl 超时、telnet 不通。排查一圈十有八九就是防火墙把你刚放行的端口挡在了外面。CentOS 系列的防火墙相关指令是每个做运维、自己折腾服务器、在虚拟机里做实验的人都绕不开的门槛。CentOS 6 时代大家用的还是 iptablesCentOS 7 之后换成了 firewalld命令体系直接变了一套好多旧笔记里的操作在新系统上敲了就是 command not found。这篇文章不打算只丢给你一张命令速查表。我会把 CentOS 家族里防火墙的来龙去脉、核心概念、高频操作和实际运维中踩过的坑一次讲透重点讲清楚每条命令背后的逻辑。适合刚接触 Linux 服务器的新手也适合那些一直在“关防火墙保平安”但心里没底的同学。1. 先弄清楚 CentOS 防火墙的“几副面孔”1.1 CentOS 6 时代的 iptables规则直接改简单但粗暴CentOS 6 以及更早的版本默认的防火墙服务就是 iptables。它的管理方式非常直白直接操作一条条规则链增删查改全靠命令。那时候最常用的操作是查看规则iptables -L -n -v放行端口iptables -A INPUT -p tcp --dport 22 -j ACCEPT拒绝来源 IPiptables -I INPUT -s 1.2.3.4 -j DROP保存规则service iptables save规则保存后写在/etc/sysconfig/iptables文件里服务重启时从文件加载。这套东西的优点是逻辑简单缺点也很明显规则一多维护起来非常容易乱。你永远分不清前三天加的规则和今天新加的规则之间是什么关系。CentOS 7 开始系统默认不再启用 iptables-services直接敲iptables命令大概率是 command not found。就算你手动装上了 iptables-services 并启动它也会遇到一个麻烦它的规则和 firewalld 的规则是两套独立体系互相不感知经常出现“firewalld 里放行了但 iptables -L 里看不到对应规则”的诡异现象。所以在 CentOS 7 以上的机器上除非你明确知道自己要回到 iptables否则第一反应不应该是去敲 iptables 命令。1.2 CentOS 7 之后的 firewalld带着“区域”概念的管家人firewalld 是 CentOS 7 开始默认带的管理 netfilter 规则的服务。它没有抛弃底层 iptables而是用一套更友好的接口把它包装了起来。两个核心变化非常关键第一个变化是 zone区域概念。你不再是面对一张扁平的规则列表而是可以把不同信任级别的网络流量划分到不同的策略集合里。比如外网网卡放在 public 区域只放行 SSH内网网卡放在 trusted 区域所有内网流量直接通行。这个设计解决了一个真实痛点一台服务器同时有外网 IP 和内网 IP它们对安全策略的要求完全不同。第二个变化是运行时配置和永久配置分离。firewalld 允许你直接修改当前运行状态而不写入配置文件也可以先修改配置文件再统一重载。这套机制的好处是临时调试策略不会污染正式的持久配置批量修改时可以一次 reload 生效。坏处就是很多人没搞懂这个双轨机制改完规则不带--permanent重启后一切归零用户就以为防火墙有毛病。firewalld 的默认区域配置文件在/usr/lib/firewalld/zones/这是系统自带的模板你最好不要去改它。自定义配置放在/etc/firewalld/zones/你的配置会覆盖系统默认模板。这个“自定义覆盖系统”的设计思路和很多 Linux 服务配置目录的做法是一致的。1.3 为什么说别动不动就关防火墙搜索热词里有一大堆“关闭防火墙命令”“防火墙关闭有影响吗”这类问题说明很多人的第一反应是把防火墙停掉。我理解这种冲动项目明天上线端口访问不通先关了排障最快。但如果你是在公网服务器上这么干等于是把家门敞开了。真实生产环境里服务器只要暴露在公网每时每刻都有人用扫描工具探测端口。你关了防火墙SSH、数据库、Redis、各种 Web 服务的管理接口全部裸奔中招只是时间问题。正确做法永远是只放行必要的端口保留防火墙的保护能力。关闭防火墙只能在本地虚拟机里做快速实验或者临时排查问题时用用完立刻恢复开启。你真正需要掌握的是“按需放行”这一套操作。这比关防火墙多不了几分钟时间但安全等级完全不一样。2. 必须吃透的三个核心概念2.1 zone 区域到底在分什么firewalld 里的 zone就是一组预设的信任级别。你打开/usr/lib/firewalld/zones/目录会发现这些区域定义drop、block、public、external、dmz、work、home、internal、trusted。我拿生活场景打个比方。一个小区有多个入口访客通道public只允许访客登记进入业主刷卡通道trusted可以全通。每张网卡、甚至每个来源 IP 都可以指定走哪条通道。默认情况下网卡都被放到 public 区域这个区域的规则是“只放行明确允许的流量其他一概拒绝”。internal 区域呢默认放行多一点点服务适合公司内部网络。实际操作中你要关心的其实就几个firewall-cmd --get-default-zone查看默认区域。刚装完系统基本是 public。firewall-cmd --get-active-zones查看当前哪些网卡被分配到了哪个区域。firewall-cmd --set-default-zoneinternal可以修改默认区域。把网卡分配到自己想要的区域命令是firewall-cmd --change-interfaceeth1 --zoneinternal。这条命令很重要因为在公司内网环境里你完全可以给内网网卡一个更宽松的区域外网网卡继续保持严格限制。很多资深的运维会把管理网段单独划到 internal 或者 trusted这样内网访问服务顺畅外网照样被防火墙挡着。按来源 IP 分配区域也很有用firewall-cmd --permanent --zonetrusted --add-source192.168.10.0/24这条命令的意思是来源 IP 是 192.168.10.0/24 的流量全部走 trusted 区域规则。trusted 默认全部放行相当于给公司内部网段开了白名单。这是我做内网服务时最常用的一个技巧。2.2 runtime 和 permanent为什么你改了规则却不生效这是新人踩得最多的坑。firewalld 有两套配置运行时配置和永久配置。你执行firewall-cmd --add-port8080/tcp不加任何其他参数它改的是运行时配置马上生效但重启 firewalld 服务或者重启系统这条规则就没了。如果你执行firewall-cmd --permanent --add-port8080/tcp它只是把规则写进配置文件当前运行环境其实还没变。想让永久配置真正生效要再执行firewall-cmd --reload让系统重新加载配置。我见过太多人只执行了第一条当天看着端口通了第二天一早检查发现服务又访问不了排查半天一头雾水。原因就是没有加--permanent或没有执行 reload。理解这两个配置之后你还会遇到第三种情况临时做了一堆测试规则发现正是你需要的想让它永久保存。不一定要把每条命令重新敲一遍加--permanent直接执行firewall-cmd --runtime-to-permanent这个命令能把当前所有运行时配置固化到永久配置里非常实用。2.3 service、port、rich-rule三种规则粒度怎么选firewalld 的规则有三种粒度很多人不知道什么时候该用哪一种。service 是预定义好的常见端口组合。比如 http 对应 80/tcphttps 对应 443/tcpmysql 对应 3306/tcp。你可以用firewall-cmd --get-services查看系统支持哪些服务。它的好处是不用记端口号而且语义清晰。默认 public 区域自带的 ssh 服务就是这种形式。能用 service 解决的问题优先用 service。port 是直接指定协议和端口。比如你跑了一个自定义应用在 18080 端口系统预定义的 service 里没有它那就用firewall-cmd --add-port18080/tcp。端口可以写范围比如1000-2000/tcp一次放行一个段。rich-rule 是最灵活的规则语法也更复杂。它支持来源 IP 限制、限速、日志记录、组合条件判断。比如“只允许 1.2.3.4 访问我的 8080 端口”这个必须用 rich-rule 或者结合 zone 来做service 和 port 都表达不了来源限制。我自己的选型经验是规则越简单越优先用高级抽象需要表达复杂条件时再下沉到 rich-rule。这样配置最清晰可维护性也最好。3. 高频操作指令实战从查到改再到删3.1 先确认防火墙状态和当前配置动手改规则之前第一件事永远是查状态。这一步不是走形式而是避免后面白忙活。最简单的查状态命令是systemctl status firewalld看服务整体状态是 running 还是 inactive。firewall-cmd --state快速输出 running 或 not running。然后看当前配置firewall-cmd --get-default-zone确认默认区域是什么。firewall-cmd --get-active-zones看网卡和区域的对应关系。firewall-cmd --list-all把默认区域当前的规则全部列出来包括放行的服务和端口这是排查时最常用的一步。firewall-cmd --list-all-zones把所有区域的规则都列一遍配置复杂的时候用。firewall-cmd --list-all输出的信息很完整包含目标default target、网卡接口、来源 IP 列表、放行的服务列表和端口列表。拿到这份输出你基本就能判断问题大概出在哪。如果 firewalld 服务都没装输出会是 command not found。CentOS 7 最小化安装时firewalld 是默认装好的但有些精简镜像或者自定义模板里可能没带。没装的话直接用包管理器安装yum install firewalldCentOS 8 以后是dnf install firewalld。3.2 添加和移除规则的完整命令表下面这张表可以当速查手册使用。所有命令执行之后如果输出 ok表示规则接受成功。操作临时生效重启失效永久生效需 reload放行端口firewall-cmd --add-port8080/tcpfirewall-cmd --permanent --add-port8080/tcp移除端口firewall-cmd --remove-port8080/tcpfirewall-cmd --permanent --remove-port8080/tcp放行服务firewall-cmd --add-servicehttpfirewall-cmd --permanent --add-servicehttp移除服务firewall-cmd --remove-servicehttpfirewall-cmd --permanent --remove-servicehttp重载配置不适用firewall-cmd --reload这里的--add-port和--add-service默认作用在--get-default-zone返回的默认区域上。如果你的网卡不在默认区域最好显式指定--zonepublic防止规则写错地方这是我踩过的坑。规则加完之后验证一下是必须的firewall-cmd --list-ports只列端口。firewall-cmd --list-services只列服务。外部再执行一次telnet IP 端口或curl -v http://IP确认实际访问效果。3.3 黑白名单只允许或拒绝特定来源 IP防火墙黑白名单是常被搜的热点很多场景其实可以不用写规则就实现一半但真要做严格的来源控制你得会用 rich-rule。先看黑名单也就是封掉某个来源 IPfirewall-cmd --permanent --add-rich-rulerule familyipv4 source address1.2.3.4 dropfirewall-cmd --reload这条规则把来自 1.2.3.4 的 IPv4 流量全部静默丢弃。客户端表现是超时无响应。如果你想让对方立刻被拒绝而不是干等把drop换成reject就行。做防扫描通常更喜欢 drop因为扫描方不知道端口到底存在还是不存在排查问题时则建议用 reject反馈更明确。再看白名单只允许特定来源访问某个端口。这里要注意一个概念默认 public 区域对未明确放行的流量是拒绝的。所以你要做的其实是两件事先确认该端口没有在 public 区域完全放行再添加一条针对来源 IP 的允许规则firewall-cmd --permanent --add-rich-rulerule familyipv4 source address1.2.3.4 port protocoltcp port22 acceptfirewall-cmd --reload这样只有 1.2.3.4 能访问 22 端口其他来源一律被默认策略拒绝。反过来如果当前区域的默认策略是放行比如某些自定义 zone 的 target 设置为 ACCEPT那么你需要写成“非指定 IP 全部拒绝”firewall-cmd --permanent --add-rich-rulerule familyipv4 source NOT address1.2.3.4 port protocoltcp port22 reject我建议你在生产环境配白名单时先把复杂规则写清楚再 reload然后立刻用一个外部 IP 验证。不要一边改一边等业务方报障那样很容易手忙脚乱。3.4 重载与持久化的几种方式修改永久配置后必须 reload 才能生效。但 reload 也有讲究。firewall-cmd --reload最常用加载永久配置到运行时不会断开已建立的连接。日常改规则推荐用它。firewall-cmd --complete-reload完整重载会断开所有已建立连接相当于把防火墙规则全部推倒重建。只有当系统提示规则异常或者你怀疑内存中的规则和配置文件不同步时再用。firewall-cmd --runtime-to-permanent把当前运行时配置写入永久配置不改动运行状态。还有一个容易混淆的点如果你手动改了/etc/firewalld/zones/public.xml文件用--reload有时不生效因为服务可能缓存了配置。这时候systemctl restart firewalld更直接。我的习惯是能命令行操作就命令行操作只有修改 zone 文件时才考虑重启服务。4. 典型业务场景配置像在实际项目中那样操作4.1 场景一Web 服务上线放行 80/443假设 Nginx 已经装好本机curl http://127.0.0.1正常返回页面但外网死活访问不了。第一反应就是排查防火墙。按照排查顺序来systemctl status firewalld确认防火墙确实开着。firewall-cmd --list-all发现服务列表里没有 http 和 https。然后放行firewall-cmd --permanent --add-servicehttpfirewall-cmd --permanent --add-servicehttpsfirewall-cmd --reload这里我推荐用--add-service而不是--add-port一个是语义清楚一个是它包含了端口对应的协议定义别人看你的配置能直接明白你想干什么。如果用的是非标准端口比如 8080那就用端口方式firewall-cmd --permanent --add-port8080/tcp如果需要放行一个区间端口比如测试环境统一用 8000-9000firewall-cmd --permanent --add-port8000-9000/tcp最后验证firewall-cmd --list-services从另外一台机器执行curl -I http://服务器IP看到 HTTP 状态码就说明通了。4.2 场景二SSH 端口修改与来源限制生产服务器把 SSH 默认的 22 端口改掉是常规操作但改端口前一定要先把新端口在防火墙里放行不然执行完systemctl restart sshd后你当前连接可能直接断掉新端口又被防火墙挡着那就只能去控制台紧急修复了。稳妥的顺序是这样的编辑/etc/ssh/sshd_config找到 Port 字段改成比如 22022。放行新端口firewall-cmd --permanent --add-port22022/tcp重载防火墙firewall-cmd --reload重启 SSH 服务systemctl restart sshd从新端口测试登录确认能连上后再考虑移除旧的 22 端口规则。只改 SSH 端口还是不够安全因为暴力破解工具会扫描所有端口。更有效的做法是限制 SSH 来源 IP只让公司出口 IP 访问firewall-cmd --permanent --add-rich-rulerule familyipv4 source addressx.x.x.x port protocoltcp port22022 acceptfirewall-cmd --reload这样配置之后22022 端口在防火墙层面就只对你指定的 IP 开放其他来源连接直接被拒。服务器自身的 SSH 也建议开启MaxAuthTries限制和密钥登录但那是另一个话题至少防火墙这层你已经做到了。4.3 场景三数据库端口只允许内网网段访问MySQL 的 3306 端口、Redis 的 6379 端口绝不能直接对公网开放。这类端口被扫描到的后果非常严重轻则数据泄露重则直接被加密勒索。规范的做法是利用 zone 来隔离。推荐两步走第一步把内网网段分配到 internal 或 trusted 区域。比如内网是 192.168.10.0/24firewall-cmd --permanent --zoneinternal --add-source192.168.10.0/24第二步在 internal 区域开放 MySQL 服务firewall-cmd --permanent --zoneinternal --add-servicemysql最后重载firewall-cmd --reload这样外网流量走 public 区域没有 3306 的放行规则访问被拒内网流量因为来源 IP 命中了 internal 区域可以正常访问数据库。我用实际经验告诉你这个方法比单纯加一个--add-rich-rulesource address192.168.10.0/24 port protocoltcp port3306 accept要清爽得多因为它把“哪些网络可信”和“可信网络能访问哪些服务”两个问题拆开了后续维护很直观。如果内网网段特别多或者管理层希望用一条规则表达那么 rich-rule 也完全可以胜任firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.10.0/24 port protocoltcp port3306 accept4.4 场景四NAT 伪装与端口转发有些场景需要服务器做端口转发比如内网有一台机器只有内网 IP但你想通过公网服务器的某个端口访问它部署的服务。firewalld 也能做。先开启 NAT 伪装firewall-cmd --permanent --add-masquerade再添加端口转发规则把公网服务器的 8080 转发到内网机器 192.168.1.100 的 80firewall-cmd --permanent --add-forward-portport8080:prototcp:toport80:toaddr192.168.1.100最后重载生效。这里有几个容易踩的点被转发的目标机器也要允许对应端口的访问如果本机防火墙对 8080 没有放行但开启了转发部分版本会在一开始阻止入站连接所以最好同时放行 8080 端口。这个配置在云环境里要特别注意云平台自身的安全组规则和实例内防火墙是两层体系都要检查。5. 常见问题与排查技巧实录5.1 为什么我明明放行了端口还是访问不通这个问题几乎是所有新人必踩的坑而且原因往往不止一种。我整理一个排查顺序按这个顺序查可以少走弯路第一确认 firewalld 服务真的在跑。很多瘦身版系统或自定义镜像里firewalld 根本没启用。执行systemctl status firewalld如果显示 inactive把服务启动并设为开机自启systemctl start firewalld systemctl enable firewalld。第二确认规则加对了区域。你执行firewall-cmd --add-port8080/tcp的时候规则加到了默认区域。但如果你网卡实际不在默认区域等于白加。用firewall-cmd --get-active-zones看网卡在哪个 zone再针对性确认。第三确认服务本身在监听。如果程序没启动或者监听地址是 127.0.0.1外部当然连不上。用ss -lntup | grep 8080看监听状态。监听在 127.0.0.1 意味着只有本机能访问要修改程序配置把监听地址改成 0.0.0.0。第四确认云平台安全组。这是最容易被忽视的一层。阿里云、腾讯云这些环境里安全组是底层拦截实例内防火墙是第二层拦截。安全组不放行你在系统里怎么折腾防火墙都没用。反过来也一样。排查云主机端口不通时两层都要看。5.2 同时装了 firewalld 和 iptables-services规则打架我见过不少服务器是两个服务同时存在且都启用了。症状是firewalld 里明明放行了端口iptables -L -n里却看不到对应规则或者 iptables 的规则把 firewalld 的放行给挡了。原因是两个服务各自管理自己的规则链互相不感知。firewalld 规则保存在 zone 的 XML 文件里iptables-services 规则保存在/etc/sysconfig/iptables里。两个服务同时 enabled 时开机都会把各自的规则加到内核顺序乱套。处理方式很明确只保留一个。现在新装的 CentOS 7 以上系统建议用 firewalld。如果你公司内部的历史标准是 iptables那就要先停掉 firewalld 并取消开机自启systemctl stop firewalldsystemctl disable firewalldyum install iptables-servicessystemctl enable iptables systemctl start iptables启用 iptables 之后规则通过service iptables save保存别再把两套命令混在一起用了。5.3 Docker 容器端口映射和 firewalld 冲突Docker 是运维日常里绕不开的坎。现象很典型容器启动时做了-p 8080:80映射本机访问 8080 正常外网访问就是不通。在 firewalld 里--list-all也看不到 8080 的影子。底层原因是 Docker 启动时直接往 iptables 的 DOCKER 链里写规则firewalld 并不知道这些规则的存在。firewalld 重载或者重启后有时会把 Docker 创建的规则清掉导致容器网络异常这在某些版本里特别明显。我总结的处理经验是两条路简单方案把容器映射的端口也显式在 firewalld 里放行。比如firewall-cmd --permanent --add-port8080/tcp。这样即使 firewalld 重新加载端口规则依然在。更彻底方案把 Docker 的内网网段加入 trusted 区域比如 Docker 默认的 172.17.0.0/16firewall-cmd --permanent --zonetrusted --add-source172.17.0.0/16实际网段以ip addr show docker0输出为准。如果已经发生 firewalld reload 后 Docker 容器无法访问的情况重启一下 Docker 服务通常能恢复systemctl restart docker。生产环境里我会尽量避免频繁 reload 防火墙确需操作前先评估 Docker 服务的影响。5.4 systemctl stop、disable、mask 到底有什么区别关闭防火墙这个话题下面很多人分不清这三个操作。systemctl stop firewalld立即停止服务但不影响开机自启设置。你如果之前 enable 过重启后 firewalld 还会再起来。systemctl disable firewalld取消开机自启但当前如果服务还在运行并不会马上停。所以最完整的“关闭”操作是两个一起systemctl stop firewalld systemctl disable firewalld。systemctl mask firewalld更彻底把服务符号链接指向 /dev/null任何其他服务想依赖它启动都会失败。这是为了防止其他组件又把防火墙拉起来但用这个操作之前你要非常确定自己不需要防火墙。我的建议是日常临时实验可以用 stop disable但生产环境不要 mask。你真正确认需要彻底移除防火墙影响时可以 mask。否则后续想恢复服务时又是满地找原因。5.5 紧急开关和日志排查有时候规则改乱了服务没反应或者你想快速验证“是不是防火墙导致的问题”可以临时把防火墙的流量过滤全部打开或关闭firewall-cmd --panic-on紧急模式拒绝所有流量相当于把服务器从网络上“隐身”。firewall-cmd --panic-off恢复正常。firewall-cmd --query-panic查看当前是否处于紧急模式。这个机制适合救急千万别当成日常开关来用。我曾经测试一台重要机器时误开了 panic-on结果所有人包括自己都连不上最后只能去机房控制台物理操作场面一度很难看。日常排查时日志也很重要。查看防火墙自己的日志journalctl -u firewalld -n 100 --no-pager想更精确地确认端口被防火墙丢弃还是服务本身没响应可以在服务器上执行tcpdump -i eth0 port 8080 -nn然后从外部发起一次访问。看到 SYN 包进来但没有 SYN-ACK 响应大概率是防火墙挡着有来有回说明服务层面有问题。tcpdump 是排查网络问题的王牌工具建议每个运维都熟练掌握。6. 最后分享几个我自己的实操习惯文章写到这里主要内容已经全部分享完了。最后再聊几句我在实际操作中形成的几个习惯希望对你有参考价值。第一个习惯初始化一台全新服务器时我会把防火墙要做的事情一次性规划好。SSH 端口改多少、哪些来源 IP 能连 SSH、Web 服务端口、数据库端口对内网哪个网段开放全部列清楚然后一次性用--permanent加完规则最后 reload。不要想到一个端口加一个端口规则零零散散最后自己都看不懂。第二个习惯每次做防火墙变更都记录一下变更原因和时间。不要小看这个步骤生产环境半年后回来看当时的规则会非常有用。我甚至会顺手把规则导出来存在项目文档里firewall-cmd --list-all-zones firewall_backup_$(date %F).txt下次配置新服务器时直接照着这份备份恢复效率能高很多。第三个习惯所有可能影响业务的防火墙操作都选在业务低峰期做。尤其是不熟悉的 rich-rule语法打错一个引号、地址写错一位效果可能天差地远。改之前先--list-all备份现状改完立刻用外部验证工具确认这两步做到位你就能把绝大部分防火墙问题挡在门外。