资讯动态

firewalld 防火墙从原理到实战:区域模型、端口放行与排障指南

发布时间:2026/10/21 21:58:50 来源:尧图企业网站定制
在 Linux 服务器上折腾过 Web 服务的同学多半都有过这种经历Nginx 装好了进程也起来了curl localhost 一切正常但一用浏览器从外部访问就是超时。排查到最终大概率会发现是 firewalld 的默认 public 区域把端口拦了。这个坑我踩过不止一次所以今天干脆把 firewalld 从原理到配置再到真实场景里的用法一次性讲透。这篇文章适合三类人刚接触 Linux 维护的新手想系统梳理 firewalld 模型的老手以及需要在生产环境做防火墙策略调整的运维。文章不会只给你几条命令而是把这套动态防火墙的设计逻辑、区域模型、配置层次和排障思路都串起来让你以后遇到问题时能举一反三。1. 为什么是 firewalld从 iptables 的维护痛点到区域模型1.1 iptables 的老问题早年间 Linux 上最主流的防火墙方案是 iptables它的能力本身没问题但维护体验确实差。iptables 有五个链PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING和四张表raw、mangle、nat、filter规则严格按照从上到下的顺序匹配。听起来强大实际生产环境里维护起来相当痛苦。你添一条规则要考虑插在哪一行删一条规则要看会不会影响后续匹配改一次规则往往要把整条链 flush 掉再重新加载。更麻烦的是iptables 规则默认是瞬时的服务重启或者网卡重启之后就会丢想持久化得自己写脚本保存和恢复。多台服务器如果规则不一样脚本维护成本会直接失控。我在早期的项目里吃过这个亏某台机器重启后规则全丢业务端口裸奔了一晚上第二天看监控才发现。1.2 firewalld 的两个核心设计firewalld 解决这些问题的思路很清晰主要体现在两点。第一把规则从扁平的列表变成区域模型。你不需要再关心规则在链里的顺序只需要把网卡或流量来源划分进合适的 zone每个 zone 有自己的默认放行策略新增服务时往 zone 里加 service 或 port 就行。规则之间的优先级冲突由 firewalld 自己处理你不用再去数这条规则排在第几条。第二引入运行时配置和永久配置的分离。运行时修改立即生效但不会污染配置文件永久配置写进 /etc/firewalld/ 下的 XML 文件下次启动自动加载。你完全可以先用运行时配置试效果确认没问题之后再固化。这里可以打个比方iptables 像在一张白纸上手写规则规矩严但改起来费劲firewalld 像给办公室划分功能区每个区域门口挂一块牌子写明能进什么、不能进什么要调整就改牌子不用重画整张纸。1.3 底层实现的变化值得提一下的是firewalld 在较新版本里已经默认跑在 nftables 后端上比如 RHEL 9 系列就是如此但给用户的命令接口还是 firewall-cmd所以文档和操作习惯都是延续的不用重新学。2. zone 与 service 两个核心概念先建立正确的心智模型2.1 九大预置区域分别适合什么场景firewalld 安装后自带九个区域按信任程度从低到高排列。理解这九个区域基本就理解了 firewalld 一半。区域信任程度默认策略典型场景drop最低直接丢弃不回应面向公网且完全不需要暴露的入口block很低拒绝并回 ICMP外网入口但想让对方知道被拒public低仅放行 ssh、dhcpv6-client默认区域适合不信任的外部网络external低放行转发并开启 NAT充当网关或路由器dmz中放行部分服务隔离区服务器work中放行常见办公服务办公网络home中放行常见家用服务家庭网络internal较高放行较多内部服务可信内网trusted最高放行所有流量完全可信的网段实际使用中大部分服务器只会用到 public 和 internal少数带网关性质的机器会用到 external。我建议新手先把这三个搞清楚其他区域了解即可。2.2 service 和 port 到底差在哪很多新手会问放行端口直接 --add-port80/tcp 不就行了为什么要用 --add-servicehttpservice 本质上是端口 协议 名字的封装。比如 http 服务对应 80/tcphttps 对应 443/tcp。定义文件在 /usr/lib/firewalld/services/ 目录下你也可以在 /etc/firewalld/services/ 下写自己的 XML 文件来封装一组端口。我推荐优先用 service 而不是直接加端口理由有三个。第一可读性好团队协作时看到 http 比看到 80/tcp 直观得多代码审计和交接也省事。第二某些服务同时用到 TCP 和 UDP 多个端口封装成服务后一条命令搞定不会漏。第三自定义服务放进 /etc/firewalld/services/ 后可以随系统备份换机器直接拷过去一致性有保障。2.3 runtime 与 permanent 的配合套路firewalld 的配置生效机制是它最好用也最容易踩坑的地方。runtime 配置firewall-cmd --zonepublic --add-port8080/tcp立即生效但机器重启后丢失。 permanent 配置加--permanent参数写入配置文件需要--reload或重启服务才生效。最佳实践只有一种调试阶段用 runtime 试验证 OK 之后执行firewall-cmd --runtime-to-permanent一次性固化或者所有变更都直接带--permanent改完统一 reload。注意--reload会丢弃所有 runtime 配置重新加载配置文件。如果你正在调试一批规则还没来得及固化直接 reload 等于白干。3. 基础操作四件套安装、查状态、加规则、固化配置3.1 安装与开机自启CentOS、RHEL、Rocky、AlmaLinux 系列默认都带 firewalld如果机器上没有装一下也很简单yum install -y firewalld systemctl enable --now firewalld如果你之前用的是 iptables services强烈建议先停用再切换避免两套规则互相干扰systemctl stop iptables systemctl disable iptables3.2 查状态四连运维排障也好日常巡检也好下面四条命令我几乎每天都要敲firewall-cmd --state # 看防火墙是否运行 firewall-cmd --get-default-zone # 看默认区域 firewall-cmd --get-active-zones # 看哪些区域绑定了网卡 firewall-cmd --list-all # 看当前区域的完整规则其中--list-all信息量最大会一次性列出 services、ports、masquerade、forward-ports、rich rules。遇到端口明明放了为什么不通的疑问先跑这条命令确认规则真的在再往网络层排查。3.3 放行服务与端口的标准操作放行服务# 临时放行立即生效 firewall-cmd --zonepublic --add-servicehttp # 永久放行并重载 firewall-cmd --zonepublic --add-servicehttp --permanent firewall-cmd --reload放行端口段firewall-cmd --permanent --zonepublic --add-port8080-8090/tcp firewall-cmd --reload移除规则就是把 add 换成 remove。日常操作记住这个套路就够了真正复杂的是后面的场景规划。4. 实战案例一Web 服务器的端口开放与 SSH 来源限制4.1 场景描述一台云主机运行 Nginx对外提供 HTTP/HTTPS 服务。SSH 管理端口只允许公司办公网段访问其余来源一律拒绝。这是最常见的生产需求难点不在于放行 Web 端口而在于只允许某网段访问 22 端口这件事怎么写才安全。4.2 配置步骤# 1. 放行 HTTP/HTTPS firewall-cmd --permanent --zonepublic --add-servicehttp firewall-cmd --permanent --zonepublic --add-servicehttps # 2. 添加富规则只允许办公网段访问 22 端口 firewall-cmd --permanent --zonepublic \ --add-rich-rulerule familyipv4 source address203.0.113.0/24 port port22 protocoltcp accept # 3. 删除 public 区域默认放行的 ssh 服务 firewall-cmd --permanent --zonepublic --remove-servicessh # 4. 重载生效 firewall-cmd --reload4.3 为什么顺序是先加富规则再删 ssh 服务这个顺序是我特意安排的。如果先删 ssh 服务再加富规则中间会有一个窗口期SSH 既没有默认规则放行也没有富规则兜底这时候远程连接会被全部拒绝。虽然窗口期只有几秒但在生产环境里完全可能打断正在进行的操作。先加后删保证任何时刻都有一条有效规则在放行办公网段这叫先开后关是运维操作的基本原则。4.4 验证方式在办公网段的一台机器上尝试 SSH应该正常连上。用手机流量或者其他网络环境试连接会超时或者被拒绝。Web 端口则不受影响任何人都能访问。我习惯在改完防火墙之后做两次验证一次验证该通的能通一次验证不该通的不通两者都通过才认为变更完成。5. 实战案例二双网卡服务器的多区域隔离与端口转发5.1 场景描述一台服务器有两张网卡一张接公网一张接内网。公网侧只暴露 80/443内网侧要开放 MySQL3306和 Redis6379给局域网内其他机器使用。如果用单区域方案就得在同一个 zone 里同时放行公网和内网的服务很容易出现公网也能访问 3306的隐患。5.2 用区域隔离解决信任差异正确做法是把两张网卡分到不同区域让策略天然隔离。# 先确认网卡名称 nmcli device status # 把公网网卡划到 public内网网卡划到 internal firewall-cmd --permanent --zonepublic --add-interfaceens192 firewall-cmd --permanent --zoneinternal --add-interfaceens224 # 在内网区域放行内部服务 firewall-cmd --permanent --zoneinternal --add-servicemysql firewall-cmd --permanent --zoneinternal --add-serviceredis # 重载生效 firewall-cmd --reload配置完之后来自公网的流量只会匹配 public 区域的规则来自内网的流量只会匹配 internal 区域的规则。MySQL 和 Redis 的端口根本不会暴露到公网侧连忘记拒绝的风险都没有。这就是区域模型比扁平规则列表强的地方。5.3 端口转发把公网请求转给内网应用再延伸一个常见需求内网有一台应用服务器 10.0.0.5:80希望公网用户通过网关机的 8080 端口访问它。# 开启伪装MASQUERADE让转发回来的包能找到路由 firewall-cmd --permanent --zonepublic --add-masquerade # 添加转发规则 firewall-cmd --permanent --zonepublic \ --add-forward-portport8080:prototcp:toport80:toaddr10.0.0.5 firewall-cmd --reload这里有个容易忽略的前提内核必须开启 IP 转发否则规则配了也不生效。sysctl -w net.ipv4.ip_forward1 echo net.ipv4.ip_forward 1 /etc/sysctl.conf另外提醒一点如果内网应用服务器和网关机不在同一网段或者内网还有其他路由策略转发能不能通还要看内网路由。防火墙只负责把包送进去不负责内网路由表。6. rich rule 进阶限速、日志与复杂策略落地6.1 rich rule 的语法结构基础命令能覆盖百分之七八十的场景剩下的复杂策略基本都要靠 rich rule 实现。它的语法支持 source、destination、service、port、protocol、icmp-block、forward-port、log 以及 accept、reject、drop 动作。完整结构大致是这样rule [familyipv4|ipv6] [source address...] [destination address...] [service name...] | [port port... protocol...] | [protocol value...] [log [prefix...] [level...]] [accept|reject|drop]新手看着长其实拆开就是一个一个组合件谁、从哪来、想去哪、用什么协议、干什么动作。6.2 三个高频用法限制某个来源 IP 访问内网数据库firewall-cmd --permanent --zoneinternal \ --add-rich-rulerule familyipv4 source address192.168.10.200 port port3306 protocoltcp reject对某个网段做连接数限速防止爬虫刷爆带宽firewall-cmd --permanent --zonepublic \ --add-rich-rulerule familyipv4 source address203.0.113.0/24 port port80 protocoltcp accept limit value30/m记录被拒绝的 SSH 尝试方便事后审计firewall-cmd --permanent --zonepublic \ --add-rich-rulerule familyipv4 source address0.0.0.0/0 port port22 protocoltcp reject log prefixSSH_ATTACK levelwarning6.3 参数含义与日志位置第二个例子里的limit value30/m表示每分钟最多接受 30 个新连接超过的直接丢弃。这个参数对防暴力破解和防 CC 有一定帮助但别当成万能的真要抗高并发攻击还是得上专门的防护手段。第三个例子的日志要留意一下rich rule 里 log 输出的内容默认进/var/log/messages用journalctl -u firewalld是看不到的。排查时如果发现日志没写先去 messages 里找这个坑我踩过。7. 生产环境避坑指南我踩过的坑和排查链路7.1 坑一runtime 配置重启后消失这是 firewalld 新手最容易踩的坑。改规则时忘了加--permanent顺手 reload 一下当时看着没问题等机器重启之后规则全部回到原始状态业务端口直接暴露。我的习惯是每次改动后立刻执行firewall-cmd --list-all确认当前生效的规则然后检查配置目录ls /etc/firewalld/zones/看 permanent 层面是否同步。两条命令一对比runtime 和 permanent 的差异一目了然。7.2 坑二改默认区域导致 SSH 失联如果你把默认区域从 public 改成 drop而 SSH 连接所在的网卡正好属于默认区域reload 之后你就会被自己锁在门外。这种事故在远程操作时是灾难性的。正确做法是三步走先把 SSH 所在网卡显式绑定到一个放行了 ssh 服务的区域再改默认区域最后 reload。如果人不在机房强烈建议加一个应急机制比如让计划任务每五分钟执行一次恢复防火墙默认配置的命令给自己留退路。7.3 坑三Docker 与 firewalld 的规则冲突Docker 默认直接操作 iptables/nftables 来发布容器端口。如果你在宿主机上开了 firewalld 又用了 Docker 的-p端口映射常见现象是firewalld 里明明放行了端口外部还是访问不通或者反过来-p把容器端口暴露到了公网firewalld 没拦住。排查思路是先看规则顺序iptables -L -n | grep -E DOCKER|firewalld生产环境里我通常的做法是宿主机防火墙只管外部到宿主机的流量容器端口尽量用-p 127.0.0.1:port:containerport只绑定本机回环地址再由宿主机上的 Nginx 反代到容器端口。这样规则链路清晰出问题时在哪一层都好定位不用猜。7.4 坑四误删默认服务public 区域默认带 ssh 和 dhcpv6-client。如果手滑删了 ssh 又没加替代规则SSH 会被当场断掉。恢复办法是通过云厂商控制台或带外管理进入机器把 ssh 服务加回去。这个坑的教训是做删除类操作之前先想清楚删掉之后有没有兜底规则。7.5 我的最后一个建议把防火墙配置纳入版本管理firewalld 的所有永久配置都落在/etc/firewalld/zones/和/etc/firewalld/services/下的 XML 文件里。我每调整完一次生产防火墙都会把这几个目录的变更记录到配置管理里注释写清楚改了什么、为什么改、谁批准的。这比靠记忆维护规则靠谱得多换人交接时也少很多沟通成本。从最初被 firewalld 挡在门外到后来熟练地用 zone、service、rich rule 拆解各种网络需求我最大的感受是这套工具的难点从来不在命令本身而在于有没有建立起区域化的思维模型。希望这篇文章能帮你跳过那些我走过的弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑