端口明明开了却连不上:一次 Linux 防火墙排查复盘

新服务监听 8080,本机 curl 通、外网就是连不上,firewall-cmd 敲了一通还是不行。排查梳理:连不上要从里往外分层、服务监听 127.0.0.1 还是 0.0.0.0、firewalld 的 --permanent 与 zone、iptables 链与规则顺序、云服务器主机之外还有一层安全组、tcpdump 抓包区分主机防火墙与安全组,以及防火墙配置的纪律。

2024 年我们上线一个新服务,监听 8080 端口,本机 curl 一切正常,可外网就是连不上。我第一反应是"防火墙没开端口呗",于是上去敲了一通 firewall-cmd,可敲完还是连不上。这件事最后查清,是好几层关卡叠在一起:服务监听地址绑错了、firewalld 端口确实没放行、云服务器的安全组也没开。它们任何一层挡着,结果都一样——"端口不通"。这次排查逼着我把"一个端口到底要过几道关"彻底理清了。本文复盘这次实战,把 Linux 防火墙这套东西讲清楚。

问题背景

环境:阿里云 CentOS 7,新上线一个监听 8080 的服务
事故现象:
- 服务进程起来了,日志正常
- 在服务器本机上 curl http://127.0.0.1:8080 正常
- 但从外网 / 别的机器连 8080 —— 一律连不上,超时

现场排查:
# 1. 本机自己连得上吗
$ curl http://127.0.0.1:8080/health
OK
# —— 本机正常,说明进程本身没问题

# 2. 换内网另一台机器连
$ curl http://10.0.1.20:8080/health
curl: (7) Failed to connect ... Connection refused
# —— Connection refused,和外网超时还不一样

# 3. 看服务到底监听在哪个地址
$ ss -lntp | grep 8080
LISTEN 0 128 127.0.0.1:8080 ...
# ★ 第一个坑:它只绑了 127.0.0.1,不是 0.0.0.0
#   只绑回环地址 -> 只有本机连得上

# 4. 改成绑 0.0.0.0 后,内网通了,外网还是不通
# 5. 查 firewalld -> 8080 没放行
# 6. 放行后内网外网都通了一半 -> 云安全组也得开

根因(这次叠了三层):
1. 服务只监听 127.0.0.1,外部根本到不了它
2. firewalld 没有放行 8080 端口
3. 阿里云安全组没有放行 8080
三层任意一层挡着,现象都是"连不上"。

修复 1:连不上,先分层——别一上来就怪防火墙

# === "端口连不上"是结果,原因可能在好几层 ===
# 一个外部请求要连上你的 8080,要闯过这些关:
#   云安全组 -> 主机防火墙(firewalld) ->
#   -> 服务有没有在监听 -> 监听的地址对不对
# 排查要【从里往外】一层层确认,别一上来就改防火墙。

# === 第一层(最里):服务到底在不在监听 ===
$ ss -lntp | grep 8080
# -l LISTEN  -n 不解析  -t TCP  -p 显示进程
# 看到 LISTEN 这一行 = 进程确实开着这个端口
# 完全没有这一行 = 服务压根没起来 / 没监听这个端口
#   —— 这种情况防火墙开了也没用,先去查服务本身

# === 第二层:监听的【地址】对不对(超常见的坑)===
$ ss -lntp | grep 8080
LISTEN 0 128 127.0.0.1:8080      <- 只有本机能连
# 对比:
LISTEN 0 128 0.0.0.0:8080        <- 所有网卡都能连(对外要这个)
LISTEN 0 128 *:8080              <- 同上
# 127.0.0.1 是回环地址,只对【本机】可见。
# 服务如果绑在 127.0.0.1,你本机 curl 当然通,
# 但任何外部机器都到不了它 —— 和防火墙毫无关系。

# === 怎么改:让服务监听 0.0.0.0 ===
# 这是【应用配置】的事,不同框架不一样,例如:
#   Spring Boot: server.address=0.0.0.0
#   Nginx:       listen 0.0.0.0:8080;
#   很多服务默认就是 0.0.0.0,但有些为安全默认 127.0.0.1

# === 区分两种报错,它能告诉你卡在哪一层 ===
# "Connection refused" —— 包到达了主机,但没人接。
#   通常是:服务没监听、或监听地址不对。
#   (防火墙如果是 reject 也可能 refused,但少见)
# "Connection timed out" —— 包发出去石沉大海,没有回应。
#   通常是:防火墙 / 安全组把包【默默丢弃】了。
# 这两种报错,是判断"卡在哪一层"的重要线索。

修复 2:firewalld——CentOS 7 的防火墙

# === CentOS 7/8 默认的防火墙管理工具是 firewalld ===

# === 先看 firewalld 是不是开着的 ===
$ systemctl status firewalld
$ firewall-cmd --state
running
# 如果是 running,它就在拦你;not running 则它没参与

# === 看当前放行了什么 ===
$ firewall-cmd --list-all
public (active)
  services: ssh dhcpv6-client
  ports:
# ★ ports 是空的 —— 8080 根本没被放行

# === 放行 8080 端口 ===
$ firewall-cmd --add-port=8080/tcp --permanent
# --permanent ★ 关键:让规则【永久】保存,
#   不加它,规则只在内存里,防火墙一重启就没了。
$ firewall-cmd --reload
# --reload:让 permanent 的规则真正生效
# 记住套路:--permanent 写规则 + --reload 生效

# === 验证放行成功 ===
$ firewall-cmd --list-ports
8080/tcp

# === 也可以按"服务名"放行(firewalld 预定义了常见服务)===
$ firewall-cmd --add-service=http --permanent    # 等于放行 80
$ firewall-cmd --add-service=https --permanent
$ firewall-cmd --get-services      # 看所有预定义服务

# === 删除规则 ===
$ firewall-cmd --remove-port=8080/tcp --permanent
$ firewall-cmd --reload

# === zone(区域)的概念 ===
# firewalld 把网卡划进不同的 zone,每个 zone 一套规则。
# 常见:public(默认,较严)、trusted(全放行)、内部等。
$ firewall-cmd --get-active-zones    # 看网卡分别在哪个 zone
# 排查时注意:你放行的规则,要加在网卡【实际所在】的
# 那个 zone 上,加错 zone 等于没加。
$ firewall-cmd --zone=public --add-port=8080/tcp --permanent

修复 3:更底层的 iptables

# === firewalld 只是个"前台",它底层操作的是 iptables ===
# 老系统(CentOS 6)、或直接用 iptables 的环境,要懂它。

# === 看当前所有规则 ===
$ iptables -L -n -v --line-numbers
# -L 列出  -n 不解析(快)  -v 详细  --line-numbers 带行号
# 重点看 INPUT 链 —— 进入本机的包都过它

# === iptables 的核心模型 ===
# 包经过一条条"链"(chain),链里是一条条"规则"。
# 进本机的流量走 INPUT 链,从上到下逐条匹配,
# 匹配上就执行动作:ACCEPT(放行)/ DROP(丢弃)/
# REJECT(拒绝并回个错)。
# ★ 关键:规则【从上往下】匹配,先匹配上的先生效。
#   所以新规则加在哪个位置很重要。

# === 放行 8080(加在 INPUT 链)===
$ iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
# -I INPUT:【插入】到 INPUT 链最前面(-A 是追加到最后)
# -p tcp:协议  --dport 8080:目标端口  -j ACCEPT:动作放行
# 为什么常用 -I 而不是 -A:如果链末尾有一条
# "DROP 全部",你用 -A 追加的放行规则排在 DROP 后面,
# 永远轮不到,等于没加。

# === 一个排错重点:看规则顺序 ===
$ iptables -L INPUT -n --line-numbers
# 如果你的 ACCEPT 规则排在某条 DROP/REJECT 之后,
# 它就被前面那条拦截了。这是 iptables 最常见的坑。

# === iptables 规则默认不持久,要保存 ===
$ service iptables save              # CentOS 6
$ iptables-save > /etc/sysconfig/iptables   # 通用
# 不保存的话,重启就全没了 —— 和 firewalld 不加
# --permanent 是一个道理。

# === 删除规则 ===
$ iptables -D INPUT 3        # 按行号删第 3 条
# 先 --line-numbers 看清行号再删

修复 4:云服务器还有一层——安全组

# === 这次最隐蔽的一层:云平台的安全组 ===
# 阿里云、腾讯云、AWS... 在你的主机之外,
# 还有一层【安全组】(AWS 叫 Security Group)。
# 它是云平台层面的防火墙,在【网络入口】就拦包。

# === 为什么它最容易被忽略 ===
# 安全组不在服务器内部,你在服务器上敲任何命令
# (firewall-cmd、iptables)都【看不到它、也改不了它】。
# 它只能在【云控制台的网页】上配置。
# 很多人 firewalld 查了、iptables 查了,全放行了,
# 还是不通,就是漏了这一层。

# === 怎么判断是不是卡在安全组 ===
# 现象:主机防火墙确认放行了,本机/同安全组内的机器
#       能连,但从安全组外 / 公网连就超时(timeout)。
# 这种"内部通、外部不通"的组合,高度怀疑安全组。

# === 怎么处理 ===
# 登录云控制台 -> 找到这台 ECS/云主机 ->
# 安全组 -> 入方向规则 -> 添加一条:
#   协议 TCP,端口 8080,来源 0.0.0.0/0(或指定网段)
# 加完【立即生效】,不用重启。

# === 排查云上端口不通的完整顺序 ===
# 1. ss -lntp        服务在监听吗?监听 0.0.0.0 吗?
# 2. firewall-cmd    主机防火墙放行了吗?
# 3. 云安全组         控制台里放行了吗?
# 4. (如有)子网 ACL / 网络 ACL 这类更外层的策略
# 一层层确认,别在某一层钻牛角尖。

# === 一个实用的对照表 ===
# 本机 curl 不通      -> 服务本身的问题(没起/端口错)
# 本机通、内网不通    -> 监听地址(127.0.0.1?)或主机防火墙
# 内网通、公网不通    -> 云安全组(十有八九)

修复 5:端口连通性测试的几把工具

# === 排查时,要会"从不同位置、用不同工具"测端口 ===

# === 在服务器本机测(确认服务和监听)===
$ ss -lntp | grep 8080         # 看在不在监听、绑哪个地址
$ curl -v http://127.0.0.1:8080/   # 本机回环测
$ curl -v http://10.0.1.20:8080/   # 用本机内网 IP 测
# 后者能测出"是不是只绑了 127.0.0.1"

# === 从另一台机器测(确认网络可达)===
$ nc -zv 10.0.1.20 8080
# -z 只探测  -v 显示结果
# succeeded = 通  failed = 不通
$ telnet 10.0.1.20 8080
# 能进入 telnet 界面 = 端口通;立刻报错 = 不通
$ curl -v --connect-timeout 5 http://目标:8080/

# === 看本机所有监听端口的全景 ===
$ ss -lntu
# -u 把 UDP 也带上,-l 只看监听
# 排查"这个端口被谁占了 / 到底开了哪些端口"

# === 抓包确认"包到底有没有到达本机" ===
# 这是判断防火墙层的杀手锏:在服务器上抓包,
$ tcpdump -i any port 8080 -nn
# 然后从外部发起连接:
#  - 抓到了 SYN 包 -> 包到达了主机,
#    那问题在主机内(防火墙 DROP / 服务没监听)
#  - 一个包都没抓到 -> 包在到达主机【之前】就被拦了,
#    问题在更外层(云安全组 / 网络 ACL)
# 这一招能精准区分"主机防火墙"还是"云安全组"。

# === 临时验证:把防火墙停掉试试(仅用于诊断!)===
$ systemctl stop firewalld
# 停掉后如果就通了 -> 确认问题在 firewalld 规则
# ★ 测完【立刻开回来】,绝不能让生产服务器裸奔。
$ systemctl start firewalld

修复 6:防火墙配置的纪律与最佳实践

# === 这次事故也暴露了我们防火墙管理上的随意 ===

# === 1. 规则要持久化 ===
# firewalld 必须加 --permanent,iptables 必须 save。
# 否则一次重启,所有规则蒸发,故障在某个深夜重现。

# === 2. 最小放行原则 ===
# 只放行真正需要对外的端口,其余一律关着。
# 反例:嫌麻烦直接 systemctl disable firewalld 把防火墙
# 关了 —— 等于服务器所有端口对全世界敞开,极危险。
# 数据库 3306、Redis 6379 这类绝不该对公网开放的端口,
# 一旦防火墙全关,就是重大安全事故。

# === 3. 来源要尽量收窄 ===
# 能限定来源 IP / 网段的,就别用 0.0.0.0/0。
# firewalld 用 rich rule 限定来源:
$ firewall-cmd --permanent --add-rich-rule='
  rule family="ipv4" source address="10.0.0.0/8"
  port port="3306" protocol="tcp" accept'
# 意思:只允许 10.0.0.0/8 这个内网段访问 3306。
$ firewall-cmd --reload

# === 4. 改规则前想好"会不会把自己关在外面" ===
# 尤其别误删 SSH(22)的放行规则,删了你就连不上了。
# 高危操作前,可以挂个"定时恢复"兜底:
$ echo 'systemctl stop firewalld' | at now + 5 minutes
# 5 分钟后自动停防火墙,万一规则改错锁了自己,
# 等 5 分钟就能重新进来。确认没问题后把这个 at 任务删掉。

# === 5. 放行后一定要从外部【实测】 ===
# 别改完 firewall-cmd 就以为完事 ——
# 一定从真正的外部机器 nc/curl 测一次,确认端口真的通。

# === 6. 把端口规则文档化 ===
# 哪个端口为什么开、给谁用,记下来。
# 否则一年后没人敢动那条规则,也没人知道它还需不需要。

命令速查

需求                        命令
=============================================================
看端口监听与绑定地址        ss -lntp | grep 端口
firewalld 状态              firewall-cmd --state
看放行了什么                firewall-cmd --list-all
放行端口(永久)            firewall-cmd --add-port=8080/tcp --permanent
使规则生效                  firewall-cmd --reload
按服务名放行                firewall-cmd --add-service=http --permanent
看网卡所在 zone             firewall-cmd --get-active-zones
看 iptables 规则            iptables -L -n -v --line-numbers
iptables 放行端口           iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
iptables 保存               iptables-save > /etc/sysconfig/iptables
从外部测端口                nc -zv 目标IP 端口
抓包看包到没到              tcpdump -i any port 8080 -nn

口诀:连不上从里往外查 -> 监听地址 -> 主机防火墙
      -> 云安全组 -> refused 是没人接 timeout 是被丢包

避坑清单

  1. 端口连不上别一上来就怪防火墙,要从里往外分层:服务监听→主机防火墙→云安全组
  2. 服务只绑 127.0.0.1 时本机能连、外部都连不上,对外要监听 0.0.0.0
  3. Connection refused 是包到了没人接,timed out 是包被默默丢弃,据此判断卡在哪层
  4. firewall-cmd 改规则必须加 --permanent 再 --reload,否则重启就丢
  5. firewalld 规则要加在网卡实际所在的 zone 上,加错 zone 等于没加
  6. iptables 规则从上往下匹配,放行规则若排在 DROP 之后就永远不生效
  7. iptables 规则默认不持久,要 iptables-save 保存,否则重启全没
  8. 云服务器在主机之外还有一层安全组,只能在云控制台配,服务器内看不到
  9. 内网通、公网不通的组合,十有八九是云安全组没放行
  10. 别图省事直接关掉防火墙,那等于所有端口对全世界敞开,是重大安全事故

总结

这次"端口连不上"的排查,纠正了我一个特别典型、也特别普遍的思维惯性:一看到端口连不上,膝跳反应似的脱口而出"防火墙没开端口呗",然后就一头扎进 firewall-cmd 里一通敲。这次的教训是,这个反应错就错在它太急了——它跳过了"分层确认"这个最该先做的动作,直接认定了原因。事实上,一个外部请求要成功连上你服务器的某个端口,它需要闯过的关卡远不止防火墙这一道:它先要穿过云平台那一层在网络入口就设卡的安全组,再穿过你主机上的 firewalld 或 iptables,然后还得确认你的服务进程真的在监听这个端口,而且——这一点最容易被忽略——服务监听的那个地址还得是对的。这四道关里,任何一道挡着,你在客户端看到的现象都是一模一样的"连不上"。正因为现象相同而原因可能在任何一层,排查就绝不能靠猜,而必须老老实实地从最里面一层往最外面一层,一层一层地确认过去。这次我踩的第一个坑,就藏在最里面那一层,而且和防火墙毫无关系:我们的服务只把端口绑在了 127.0.0.1 这个回环地址上。回环地址的含义,是"只有这台机器自己看得见",所以我在服务器本机上 curl 当然畅通无阻,可任何一台外部的机器,根本就到不了这个只对本机可见的地址——这种时候你就算把防火墙的端口放行得再彻底、安全组开得再大方,也全是白费功夫,因为问题压根不在"门开没开",而在"服务根本没把自己摆在门后"。要让一个服务能被外部访问,它必须监听 0.0.0.0,也就是这台机器所有网卡的所有地址。这个坑教会我,排查的第一步永远是 ss -lntp,先看清楚服务到底在不在监听、以及监听的是哪个地址,而不是急着去碰防火墙。第二个坑在中间那层,也就是 firewalld 本身:8080 端口确实没有被放行。这里我想记住的是 firewalld 操作的一个铁律——改规则必须加 --permanent 再 --reload。--permanent 让规则被持久地写下来,--reload 让它真正生效;少了 --permanent,你的规则就只活在内存里,防火墙服务一重启就人间蒸发,故障会在某个你毫无防备的深夜悄悄重现。第三个坑,也是这次最隐蔽、最让我恍然大悟的一层,是云服务器的安全组。安全组是云平台在你的主机之外、在网络入口处设的一道防火墙,它的隐蔽之处在于:你在服务器内部敲任何命令都看不见它、也改不动它,它只能在云控制台的网页上配置。这就是为什么我把主机上的 firewalld 查了又查、放行了又放行,却依然连不上——因为挡着我的那道关,根本不在我敲命令的这台机器里。这次也让我总结出一组特别实用的对照经验:如果连本机 curl 都不通,那是服务自身的问题;如果本机通、内网其它机器不通,矛头就指向监听地址或者主机防火墙;而如果内网都通了、唯独公网连不上,那几乎可以一口咬定是云安全组没放行。除了分层,这次排查还教给我两个能精准定位的小技巧。一个是看报错的措辞:Connection refused 意味着你的包其实已经到达了主机,只是没有进程接它,这通常指向服务没监听或监听地址不对;而 Connection timed out 意味着包发出去之后石沉大海、连一个回应都没有,这通常意味着它在某一层防火墙那里被悄无声息地丢弃了。另一个更硬核的技巧是抓包:在服务器上 tcpdump 盯着那个端口,然后从外部发起连接,如果你在服务器上抓到了对方的 SYN 包,就说明包成功到达了主机,问题必定在主机内部;如果一个包都没抓到,那就说明包在到达主机之前就被拦下了,问题必定在更外层的云安全组——这一招能像手术刀一样,把"主机防火墙"和"云安全组"这两层干净利落地切开。这次排查从最初那个想当然的"防火墙没开端口",到最后理清"服务监听、主机防火墙、云安全组"这三层环环相扣的关卡,我最大的收获是明白了:面对"连不上"这种结果唯一、原因却可能藏在任意一层的问题,真正的排查能力,不是你记得多少条 firewall-cmd 命令,而是你心里有没有那张"从里到外、一层一层"的关卡地图,以及你愿不愿意压住那个想直接动手的冲动,先把每一层都安安静静地确认一遍。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
Linux教程

SSH 免密配了却还要密码:一次 Linux SSH 排查与加固复盘

2026-5-20 17:34:19

Linux教程

磁盘没满 df 却报 100%:一次 Linux 磁盘空间排查复盘

2026-5-20 17:41:33

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索