排查 Linux 上“服务明明起来了、端口就是连不上”的问题,十次里有八次最后都落在防火墙这一层。firewalld、iptables、ufw、nftables 这几套东西在不同发行版上各不相同,命令长得像、语义差得远,很多人背了几条命令,换个系统就抓瞎,重启完机器规则又没了,甚至手一抖把自己 SSH 关在门外。这篇就把 Linux 下查看防火墙状态、临时与永久关闭防火墙、开放和关闭端口这几件事一次讲透,顺带把“放行了还是不通”的完整排查链路走一遍。内容偏实战,偏命令细节,也包含我这些年踩过的坑,适合刚上手运维的朋友,也适合被端口问题折磨过的开发者回头对一遍自己的操作习惯。
1. 先分清你面对的是哪套防火墙,不然命令全是白敲
1.1 四套工具其实只有两个真正的执行者
这一点必须先说清楚,因为它决定了你后面所有命令的意义。Linux 内核里真正干活的模块叫 netfilter,它在协议栈里挂了几个钩子,数据包进出的时候被这些钩子拦下来做判断。用户态能操作它的工具,历史上是iptables,新一代是nftables。这两个是“直接操作内核规则表”的工具。
而firewalld和ufw是管理层,它们自己不拦包,最终还是要翻译成 iptables 或者 nftables 规则。firewalld是 RHEL 7 之后、CentOS 7/8、Rocky、AlmaLinux、Fedora 这一系默认带的东西,RHEL 8 之后它底层已经换成了 nftables 作为后端。ufw则是 Debian、Ubuntu 这一系的默认封装,语法极简,ufw allow 22/tcp一行搞定。
所以你执行systemctl stop firewalld,和在网上抄来的iptables -F,这两件事不是一回事。前者是把管理层停掉,firewalld 停的时候会把自己写进去的规则链清走;后者是直接清空 iptables 的规则表。如果你机器上根本没有 firewalld 只有裸 iptables,那 stop firewalld 只会告诉你“找不到这个服务”,而你的端口依然是拦着的。
注意:CentOS 7 上
firewalld和iptables-services是互斥的,同时装容易互相打架,规则会被对方覆盖。看到一台机器两个都装了,先确认到底哪个在跑,别两边都改。
1.2 用三条命令判断当前活跃的是谁
别靠猜,靠查。我一般是这么走一遍的:
# 看发行版,先大体判断方向 cat /etc/os-release # 看几个服务谁是 active systemctl is-active firewalld ufw nftables iptables # 看工具装没装 which firewall-cmd ufw nft iptablessystemctl is-active返回的是active、inactive或者unknown。unknown就说明这个单元根本不存在,不用往下想了。如果firewalld是 active,那你就用firewall-cmd系列;如果ufw是 active,那就用 ufw;如果两个都不在,而iptables -L -n出来一堆规则,那就是裸 iptables 在管,通常配合/etc/sysconfig/iptables或者iptables-persistent做持久化。
还有一种情况很迷惑人:firewalld是 active,但你iptables -L -n之后看到DOCKER、DOCKER-USER、KUBE-SERVICES这些链,会以为防火墙规则很乱。其实这些是 Docker 和 Kubernetes 自己插进去的链,跟 firewalld 的IN_public_allow不是一批东西。看规则的时候心里要能把它们分开,不然你会觉得自己从没配过的东西怎么全冒出来了。
1.3 为什么会出现“防火墙关了端口还是不通”
这是新手最容易懵的场景,也是我见过最多的误判。防火墙只是数据包要过的其中一道门,它前面后面还有好几道:
- 服务本身没监听,或者只监听了
127.0.0.1而不是0.0.0.0。这种情况你把防火墙全删了也没用,因为包到了本机,内核发现没人接,直接回 RST。 - 云主机的安全组或网络 ACL 没放行。安全组是虚拟网络层的过滤器,跟你机器里的防火墙完全是两套东西,你在系统里怎么折腾都影响不到它。
- SELinux 拦了。SELinux 对端口有标签限制,非标准端口跑 httpd、nginx 的时候特别容易中招。
- 中间还有一层反向代理或者负载均衡。
- Docker 起了容器但端口映射写错了,或者映射到宿主机的端口被别的进程占了。
所以后面我会把“查看状态—关闭防火墙—放行端口—验证连通”串成一条链路来讲,单独记命令很容易,串起来才是真本事。
2. 查看防火墙状态:光会 systemctl status 远远不够
2.1 firewalld 的状态其实有三层
很多人查 firewalld 就一句systemctl status firewalld,看到绿色的 active 就以为“防火墙开着”。这只回答了第一层问题。我更关心的是另外两层:防火墙整体是不是在过滤,以及当前规则长什么样。
# 第一层:服务在不在跑 systemctl status firewalld # 第二层:防火墙功能是否处于运行状态,返回 running 或 not running firewall-cmd --state # 第三层:看具体规则 firewall-cmd --get-default-zone # 默认区域是哪个 firewall-cmd --get-active-zones # 当前哪些区域是激活的 firewall-cmd --list-all # 默认区域的完整配置(最常用) firewall-cmd --list-all-zones # 所有区域的配置,输出很长 firewall-cmd --list-ports # 只看放行的端口 firewall-cmd --list-services # 只看放行的服务firewall-cmd --list-all是我按得最多的一条。它会告诉你当前默认 zone 的名字、绑定的网卡、放行的 services、ports、rich rules,以及 target 是 default 还是 ACCEPT 还是 REJECT。如果看到target: ACCEPT,说明这个区域实际上是不拦的,哪怕服务跑着,也只是“开着但不管事”。
--list-ports出来的格式是8080/tcp 5000-5100/tcp这种,一眼能看出端口的协议和范围。这个很有用,因为--list-services里只有服务名,比如http、ssh、dhcpv6-client,你得知道http对应 80/tcp、https对应 443/tcp,不然容易漏判。
2.2 iptables 和 nftables 看规则的方式
裸 iptables 的环境,命令是:
iptables -L -n --line-numbers # 带行号,方便后面按行号删 iptables -L -n -v # 带包计数和字节计数,能看出规则有没有命中 iptables -t nat -L -n # 看 NAT 表,排查端口映射、转发必看 iptables -S # 以可复制的命令形式输出规则,调试最方便-n的作用是不做反向域名解析,不加这个参数,机器会去查 DNS,输出卡半天还容易卡死。--line-numbers是给删除用的,iptables -D INPUT 3就是删掉 INPUT 链第 3 条。-v的计数我特别推荐,配完规则后从外面访问一次,再回来看计数有没有涨,涨了就说明包确实匹配到这条规则了,这个判断比什么都直接。
nftables 环境则是:
nft list ruleset # 全部规则 nft list table inet filter # 只看 filter 表 nft list chain inet filter inputnft list ruleset的输出比 iptables 干净很多,有结构化缩进,链、规则、计数器一目了然。RHEL 8/9 上如果你既装了 nftables 又装了 firewalld,记住 firewalld 是往 nftables 里写规则的,直接改 nft 规则会被 firewalld 的重载覆盖掉。
2.3 ufw 的状态与规则清单
Ubuntu 这一系简单得多:
ufw status # 简洁模式 ufw status verbose # 带默认策略、日志级别 ufw status numbered # 带编号,用来删规则这里有个大坑:ufw status如果显示Status: inactive,那说明 ufw 本身没启用,但不代表没有防火墙在拦包。可能是因为机器上跑着裸 iptables 规则,也可能云厂商在外部做了限制。反过来,Status: active时它会列出规则和默认策略,Default: deny (incoming), allow (outgoing)这行尤其要看,它决定了没显式放行的一切入站流量都被拒。
2.4 一条组合拳把状态查全
我习惯把下面这套按顺序敲一遍,基本不会漏:
| 检查项 | 命令 | 看什么 |
|---|---|---|
| 服务是否运行 | systemctl is-active firewalld ufw | active / inactive / unknown |
| 过滤功能状态 | firewall-cmd --state或ufw status | running / not running |
| 具体规则 | firewall-cmd --list-all | zone、ports、services、rich rules |
| 内核规则 | iptables -S或nft list ruleset | 真正生效的规则 |
| 监听端口 | ss -lntp | 服务到底在不在听、听在哪个地址 |
| 计数器 | iptables -L -n -v | 规则是否被命中 |
最后两行特别关键。防火墙和监听端口这两件事要对着看:防火墙放行了 8080,但ss -lntp里根本没有 8080,那问题不在防火墙;反过来ss里有 8080 而外面连不上,再去查防火墙。这个对照关系建立起来之后,排查速度会快一个量级。
3. 关闭防火墙:停服务和禁自启是两码事
3.1 stop、disable、mask 的语义差别
这是命令语义层面最常见的误解,必须掰开说。
systemctl stop firewalld:现在就停,重启机器之后会自动起来。这是临时操作。systemctl disable firewalld:取消开机自启,当前进程还在跑,规则还在生效。你要是只敲了这一条,然后去测端口,会发现还是不通。systemctl mask firewalld:把它软链到/dev/null,任何服务想通过依赖把它拉起来都会失败。用于彻底防止被别的组件唤醒。
所以真正“永久关掉”的正确组合是两条:
systemctl stop firewalld systemctl disable firewalld # 确认 systemctl is-active firewalld # 应该返回 inactive systemctl is-enabled firewalld # 应该返回 disabled只敲 stop 不敲 disable,重启之后规则又回来了,然后你会怀疑自己是不是记错了。这种“重启就失效”的问题,我在别人机器上遇到过好几次,多半就是漏了 disable。反过来只敲 disable 不敲 stop,机器不重启就一直拦着,也会让人以为 disable 没生效。
Ubuntu 上更简单:
ufw disable ufw status # 确认变成 inactiveufw disable同时做到了不启用和开机不起。它不会把已有规则删掉,只是不加载,哪天ufw enable又都回来了,这点比 firewalld 友好一些。
3.2 关掉之后先确认规则真的清干净了
停掉 firewalld 之后,我建议再跑一次:
iptables -S iptables -t nat -S你会看到 firewalld 自己的链(IN_public、IN_public_allow、FORWARD_IN_ZONES这些)已经不见了,但DOCKER、KUBE-*这类链大概率还在。这才是真实状态:firewalld 关了,但 Docker 插进去的规则依然生效。这也是“我关了防火墙容器端口还是不通/或者居然能通”的原因之一。
如果你的目标是彻底裸奔,那就得把这些链也处理掉:
iptables -F # 清空所有链的规则 iptables -X # 删除自定义链 iptables -Z # 计数器归零 iptables -P INPUT ACCEPT iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT注意:这几条命令在远程连接上是危险操作。
-P INPUT DROP这一类改默认策略的动作,如果没提前放行 22 端口,执行下去 SSH 立刻断,而且规则不持久化的话你重启也救不回来,只能进带外控制台。我个人的规矩是,改默认策略之前,先单独敲一条iptables -I INPUT -p tcp --dport 22 -j ACCEPT放在链首,保命。
3.3 关闭防火墙的真实代价,不只是“不安全”
“防火墙关闭有影响吗”这个问题我被人问过很多次。多数人想象的答案是“不安全”,但实际上短期内更常见的后果是功能层面的:
- Kubernetes 集群节点上关掉 iptables 相关机制,kube-proxy 的服务转发会失效,Service 直接不通。
- 一些中间件安装脚本依赖 iptables 规则做端口转发,关掉之后转发链路断了。
- Docker 重启时如果 iptables 规则被清空过,容器的端口映射可能失效,需要重启 Docker 服务重建规则。
- firewalld 停掉后再启动,之前用
--add-port但不带--permanent配的运行时规则会全部丢失,因为它本来就是临时的。
嵌入式开发工具、仿真软件、板卡调试工具安装时经常要求“关闭防火墙”,本质原因是它们需要在局域网内做设备发现或者开一堆随机端口做通信,防火墙会拦掉广播包或者非预期端口。这种场景下,我的建议是别整个关掉,而是把工具要用的端口范围或者局域网网段单独放行,安全性差得不是一星半点。具体做法在第 4 节的富规则里讲。
3.4 我更推荐的中间路线
如果你的问题只是“某个端口不通”,那么关防火墙属于用大炮打蚊子。真正的解法是精确放行。理由有两个:一是关掉之后你就失去了一个排查工具,以后再出问题更难定位;二是很多生产环境的安全基线检查会直接判不合规。
我自己的处理顺序一直是:先看服务监听,再精确放行端口,放行之后还不通再去考虑关闭或者排查别的层。只有在这几种情况下我才会真的关掉防火墙:纯内网测试机、明确知道自己要断开所有过滤能力的调试场景、以及排查过程中临时关掉用来“证伪”——确认问题确实出在防火墙这一层,然后立刻打开并按需放行。
4. firewalld 下开放和关闭端口,细节全在 zone 和 permanent 上
4.1 --permanent 和 --reload 的配合关系
firewalld 有两套配置:runtime 和 permanent。运行时配置立即生效,重启服务或者 reload 后丢失;永久配置写到/etc/firewalld/下的 XML 文件里,需要 reload 才生效。
# 只改运行时,立即生效,reload 后失效 firewall-cmd --add-port=8080/tcp # 只改永久配置,当前不生效 firewall-cmd --add-port=8080/tcp --permanent # 正确姿势:两条一起用 firewall-cmd --add-port=8080/tcp --permanent firewall-cmd --reload我个人的习惯是永远都带上--permanent,然后统一 reload。这样不会出现“当时能用,重启就没了”的诡异情况。reload 的代价是它会重建整个规则集,正在建立的连接可能被短暂中断。生产环境上,尽量避免在业务高峰期 reload,尤其是那种长连接的服务。
有人怕 reload 断连接,改用firewall-cmd --runtime-to-permanent,把当前运行时配置整体刷成永久。这条命令在“我已经手工放行了一堆端口,现在想一次性固化”的场景下很好用,但注意它是全量覆盖,会把之前永久配置里但当前运行时没有的东西也一并覆盖掉。
4.2 zone 决定规则落到哪张链上
firewalld 的 zone 概念,说白了就是“一套按来源和网卡分组的规则集合”。默认 zone 通常是public,规则加在不指定--zone的时候就落到默认 zone。看当前哪些 zone 激活、绑了哪张网卡:
firewall-cmd --get-active-zones假设输出是:
public interfaces: eth0那说明 eth0 上的流量走 public 区域。如果你往--zone=work里加规则,但没有任何网卡或来源网段绑在 work 上,那条规则永远不会被匹配到。这就是“我明明放行了端口却不通”的经典原因之一。
踩过几次之后我的做法是:先--list-all看清默认 zone 和绑定关系,加规则时不写--zone,让它落到默认 zone,省去对应关系上的心智负担。只有需要按来源网段分流的时候(比如只让办公网访问数据库),才显式指定 zone。
4.3 端口范围、协议和富规则
基础写法:
# 单个 TCP 端口 firewall-cmd --add-port=8080/tcp --permanent # 一个范围,比如 MLflow 这类平台常用的 5000 起始的一串端口 firewall-cmd --add-port=5000-5010/tcp --permanent # UDP 端口,注意协议要写对,很多服务是 UDP 的 firewall-cmd --add-port=51820/udp --permanent # 放行已有服务名,等价于放行它预设的一组端口 firewall-cmd --add-service=http --permanent firewall-cmd --reload/tcp这个后缀千万别漏,漏了会报错,因为 firewalld 必须知道协议。UDP 也要写/udp,写成 tcp 就白干了。这一点在做端口转发、DNS、各类发现协议的时候特别容易错。
如果要做来源限制,就得用富规则(rich rule):
# 只允许 192.168.1.0/24 网段访问 3306 firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port port="3306" protocol="tcp" accept' # 拒绝某个 IP 访问 22 firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.99" port port="22" protocol="tcp" reject' firewall-cmd --reload富规则的价值在于“精确到来源”。像数据库这类服务,全放开和只放开办公网,安全等级完全不同。前面说的嵌入式工具、仿真软件要在局域网做设备发现,用富规则把整个 192.168.x.0/24 段放行,比关掉防火墙要靠谱得多,而且重启之后依然有效。
4.4 关闭端口和清理残留规则
关闭端口就是把--add-port换成--remove-port:
firewall-cmd --remove-port=8080/tcp --permanent firewall-cmd --reload firewall-cmd --list-ports # 确认已经没了有一个隐蔽问题:如果你之前既加了运行时又加了永久配置,--remove-port不带--permanent的时候只删运行时,永久配置里的那条还在,下次 reload 它又回来了。判断依据很简单:firewall-cmd --list-ports和firewall-cmd --permanent --list-ports出来的结果对比着看,差异就是这两层配置不一致的地方。我遇到过很多“删了端口过两天又开了”的情况,全是因为这个。
还有服务名放行的规则也要一起清:
firewall-cmd --remove-service=http --permanent firewall-cmd --reload--list-services里看不到的端口,很可能是被某个 service 定义带进来的。firewalld 的服务定义文件在/usr/lib/firewalld/services/下,想看某个服务到底开了哪些端口,直接cat那个 XML 就行。这个技巧很少人用,但排查“这个端口谁给我开的”特别有效。
5. iptables 和 ufw 下的端口放行写法对照
5.1 iptables 里插入位置比规则本身更重要
iptables 是按链的顺序逐条匹配,匹配到就执行动作,后面的规则不再看。所以-A(追加到末尾)和-I(插入到链首)的区别是决定性的:
# 追加,如果前面已经有 REJECT/DROP 命中了,这条永远轮不到 iptables -A INPUT -p tcp --dport 8080 -j ACCEPT # 插入到第 1 位,最优先匹配 iptables -I INPUT 1 -p tcp --dport 8080 -j ACCEPT # 也可以指定插到第几行 iptables -I INPUT 3 -p tcp --dport 8080 -j ACCEPT我基本只用-I,偶尔需要精确控制顺序时用带行号的-I INPUT 5。判断顺序问题最直接的办法是iptables -L INPUT -n -v --line-numbers,看你的 ACCEPT 规则排在 REJECT 前面还是后面。排后面就是无效的,这一条能解决相当一部分“放行了还是不通”。
删除规则有两种方式:
# 按规则内容删,必须和添加时写得完全一致 iptables -D INPUT -p tcp --dport 8080 -j ACCEPT # 按行号删,先把行号看清楚 iptables -L INPUT -n --line-numbers iptables -D INPUT 3按内容删的时候,如果你添加时还带了-s、-i这些参数,删除时也要一字不差地带上,不然会提示找不到规则。这点挺折磨人的,所以我更推荐直接改配置文件或者用行号删。
5.2 规则持久化:重启不丢的做法
裸 iptables 默认是不持久化的,iptables命令敲完的规则重启全没。不同发行版的持久化方式不一样:
| 发行版 | 持久化方式 |
|---|---|
| RHEL/CentOS 7 系 | 装iptables-services,service iptables save,规则存到/etc/sysconfig/iptables |
| Debian/Ubuntu | 装iptables-persistent,netfilter-persistent save |
| RHEL 8/9 系 | 官方推荐用 nftables 或 firewalld,不鼓励裸 iptables |
# CentOS 7 yum install -y iptables-services systemctl enable iptables service iptables save # Debian/Ubuntu apt install -y iptables-persistent netfilter-persistent saveservice iptables save会把当前内存里的规则整体写到配置文件。这个动作要放在你验证过规则确实生效之后再做,否则就是把一个错误状态固化下来。更早些年我还习惯手工iptables-save > /etc/sysconfig/iptables,效果一样。
5.3 ufw 的语法和两个常见盲区
ufw 是真的简单:
ufw allow 8080/tcp ufw allow 8080 # 不写协议等于同时放行 tcp 和 udp ufw allow 5000:5010/tcp # 范围用冒号,不是横线 ufw allow from 192.168.1.0/24 to any port 3306 ufw deny 23 ufw delete allow 8080 ufw status numbered ufw delete 4 # 按编号删盲区一:ufw allow 8080不带协议,会同时开 TCP 和 UDP,多开了一个面。要精确的就写/tcp。
盲区二:在远程 SSH 上加规则的时候,如果这条规则触发了 ufw 重新加载默认策略,有可能影响当前连接。相对安全的做法是先把 22 明确放行再折腾别的:
ufw allow 22/tcp ufw limit 22/tcp # 更好的选择,超频次连接自动限速,抗暴力破解ufw limit这个用法知道的人不多,它在 30 秒内同一 IP 超过 6 次连接就拒绝,对付扫描和爆破挺有用,而且写起来就一行。
6. 放行之后还是不通,走一遍完整排查链路
6.1 第一步永远是看服务有没有在监听
我见过太多人一上来就查防火墙,查半小时,最后发现服务根本没起来。所以顺序要反过来:
ss -lntp # 或者 ss -lntup # 带上 UDP netstat -lntp # 老系统,需要 net-tools lsof -i:8080 # 精确查某个端口被谁占了 fuser -n tcp 8080重点看两列:本地地址和进程名。如果显示的是127.0.0.1:8080,说明服务只绑了回环地址,外部永远连不上,这跟防火墙一点关系没有,得去改服务配置里的监听地址。如果是0.0.0.0:8080或者*:8080,才是真的对外监听。另外看进程名对不对,确认没有启动失败被另一个进程占了端口。端口被占的场景其实很常见,尤其是常用端口比如 5000、8000、8080,先占后用的时候启动会直接失败。
6.2 分三层验证连通性
定位问题要一层层来,别跳:
# 第一层:本机自己连自己,验证服务本身 curl -v http://127.0.0.1:8080 # 第二层:用本机 IP 连,验证监听地址 curl -v http://192.168.1.10:8080 # 第三层:从另一台机器连,验证网络路径和防火墙 telnet 192.168.1.10 8080 nc -zv 192.168.1.10 8080 nmap -p 8080 192.168.1.10第一层通、第二层不通,基本上就是监听地址问题。前两层都通、第三层不通,才轮到防火墙或者中间网络设备。nc -zv比 telnet 好用,因为它会明确告诉你succeeded还是refused。注意refused(连接被拒)和timeout(超时无响应)是两个不同的信号:refused 说明包到了但端口没人接或者被明确拒绝,timeout 通常是包被静默丢弃了,多半是防火墙 DROP 或者中间设备拦了。这两个词能帮你缩小一半范围,很多教程不提这一点,但实际上特别有用。
6.3 云主机安全组和 SELinux 这两层
如果前两层通、外部不通,而防火墙你确认已经放行了,那就查这两个:
# 云主机:安全组是控制台里配的,系统内看不到 # 去云控制台确认入方向规则里有对应端口,注意协议和来源 IP 段 # SELinux 状态 getenforce # Enforcing / Permissive / Disabled sestatus # 临时切宽松,用来验证是不是 SELinux 在拦 setenforce 0 # 看 SELinux 允许 http 用哪些端口 semanage port -l | grep http_port_t # 允许非标准端口跑 web 服务 semanage port -a -t http_port_t -p tcp 8080SELinux 这个坑特别隐蔽。现象是防火墙明明放行了,ss里服务也在监听,本机 curl 也通,就是外部不通。原因是 SELinux 的策略只允许 httpd 绑定特定标签的端口,你让它跑在 8080 上,虽然进程起来了,但被策略挡住了。setenforce 0之后立刻通,就能确认是它。如果是这个原因,正确做法是用semanage port -a给端口打标签,而不是长期关 SELinux。
改 SSH 端口也是类似逻辑。你想把 22 改成 2222,正确顺序是:先在防火墙放行 2222,再改 sshd 配置,然后重启 sshd,验证新端口能连上,最后再删掉 22 的放行。顺序错了,就是把自己关在门外然后去找带外控制台。我第一次干这事的时候就是先改了配置没放行,好在没关当前会话,用还在线的连接救回来了。
6.4 Docker 容器端口和 firewalld 的冲突
这个场景在装了 Docker 的服务器上非常典型。Docker 启动时会自己往 iptables 里插DOCKER和DOCKER-USER链,实现端口映射。而 firewalld 的 reload 会重建规则集,有可能把 Docker 的链冲掉,表现就是容器还在跑,但端口映射失效,或者反之,你没在 firewalld 里开 8080,容器映射了 8080,结果居然能访问。
# 看 Docker 的链 iptables -t nat -L DOCKER -n iptables -L DOCKER-USER -n # 看容器的端口映射 docker ps docker port <容器名> # 规则被冲掉之后,重启 Docker 服务让它重建 systemctl restart docker我比较推荐的处理方式,是把 Docker 用的网桥加入 firewalld 的 trusted 区域,而不是关掉防火墙:
firewall-cmd --permanent --zone=trusted --add-interface=docker0 firewall-cmd --reload这样 Docker 网桥上的流量不受 zone 过滤影响,同时宿主机的入站规则依然生效。比关防火墙要干净得多,也不会因为 reload 就乱套。
7. 几个高频场景的完整操作串和面试常问点
7.1 新装系统部署一个 Web 服务
假设一台全新 CentOS,要跑一个监听 8080 的服务,我的完整动作是这样:
# 1. 确认防火墙是哪套 systemctl is-active firewalld firewall-cmd --list-all # 2. 确认服务在监听 ss -lntp | grep 8080 # 3. 精确放行 firewall-cmd --add-port=8080/tcp --permanent firewall-cmd --reload # 4. 验证 firewall-cmd --list-ports # 从外部机器 nc -zv <服务器IP> 8080四步走完,如果还不通,再按第 6 节的分层排查往下走。这四步我整理成一个固定流程之后,处理这类问题的平均时间从半小时降到了几分钟。
7.2 临时开端口调试,用完即收
调试期间经常要临时开端口,这里有个省事又安全的做法:只加运行时规则,不加 permanent。
# 临时放行,不用 reload,立即生效 firewall-cmd --add-port=9999/tcp # 调完删掉 firewall-cmd --remove-port=9999/tcp # 实在忘了删,reload 一下自动清干净 firewall-cmd --reload这个用法正好利用了运行时配置“不持久化”的特性。调试完忘了删也没关系,一个 reload 或者重启就恢复干净了。这是我在正式环境上最常用的临时开端口方式,比每次改永久配置稳妥。
7.3 面试里经常被追问的几个点
这几个问题问到的频率很高,答得清楚能看出底子:
- firewalld 和 iptables 是什么关系?答:firewalld 是管理前端,底层还是 netfilter,RHEL 8 之后后端是 nftables。它不是替代 iptables,而是封装。
--permanent不加会怎样?答:只改运行时,reload 或重启后丢失。- 为什么 reload 会影响正在建立的连接?答:它重建整套规则集,过程中规则短暂缺失或重新加载,长连接可能被打断。
- 默认 zone 怎么影响规则生效?答:没有网卡或来源绑定的 zone 里的规则不会被匹配。
- 放行了端口还是不通,你的排查顺序?就按第 6 节:监听地址 → 本机连通 → 外部连通 → 防火墙 → 安全组 → SELinux。
我个人在实际操作中的体会是,防火墙相关的问题,八成不是命令记错,而是没有建立“分层”的思维。命令本身半天就能背下来,真正值钱的是知道包里到哪一层、该在哪一层找原因。前面那套“先看监听,再看本机连通,最后看防火墙”的顺序,比任何一条具体的 firewall-cmd 都更值得记住。另外再分享一个小技巧:iptables -L -n -v里的包计数,是我排查时最依赖的一件工具——配完规则从外面访问一下,回来看计数涨没涨,涨了就说明链路是通的,问题在别处;没涨就说明包根本没走到这条规则,方向立刻明确。