摘要
端口不通的四层定位法:监听地址、本机防火墙、容器 DNAT 链、云安全组,外加 tcpdump 判别表。含
ss监听地址判读、ufw 与 firewalld 的 reload 差异、DOCKER-USER链拦截写法、安全组四个必查字段,以及一份可直接跑的端口体检脚本。
正文(可直接复制)
0. 适用场景
单机服务对外端口不可达,本机访问正常。适用于 Ubuntu 20.04/22.04、CentOS 7/8、Debian 11/12,容器场景同样适用。命令中的 IP 与端口需替换为实际值。
1. 四层模型
|
层 |
检查对象 |
命令 |
典型症状 |
|
L1 |
监听地址 |
|
|
|
L2 |
本机防火墙 |
|
|
|
L3 |
容器 DNAT |
|
ufw 已拒绝但仍可访问 |
|
L4 |
云安全组 |
控制台入方向规则 |
|
排查顺序固定为 L1 → L2 → L3 → L4,不要从控制台开始。
2. 步骤一:确认监听地址
ss -lntp 'sport = :8080'
三种输出三种含义:
127.0.0.1:8080:仅本机,外部必不通。
0.0.0.0:8080:IPv4 全地址,正常。
[::]:8080:IPv6 全地址,是否接 IPv4 取决于net.ipv6.bindv6only。
sysctl net.ipv6.bindv6only
返回 1 时只接 IPv6,需要改成双栈监听或显式监听 0.0.0.0。
3. 步骤二:本机防火墙
Ubuntu(ufw):
ufw status verbose
ufw allow 8080/tcp
CentOS(firewalld):
firewall-cmd --add-port=8080/tcp --permanent
firewall-cmd --reload
firewall-cmd --list-ports
注意 --permanent 与 --reload 必须成对出现。只写前者的规则在重启后生效、当前不生效;只 reload 不写 permanent 的规则重启后丢失。
4. 步骤三:容器端口发布
iptables -t nat -L DOCKER -n --line-numbers
存在 DNAT ... dpt:8080 to:172.17.0.2:8080 即表示端口由 docker 发布。此路径经过 FORWARD 链,ufw 的 INPUT 规则拦不住。
拦截写法(DOCKER-USER 链在 docker 自动规则之前被遍历):
iptables -I DOCKER-USER -p tcp --dport 8080 -j DROP
-I 是插入链首,-A 是追加到链尾。追加的规则可能永远轮不到,因为前面的放行规则已经先命中了。
更稳妥的是发布时只绑回环,由 nginx 统一对外:
docker run -d -p 127.0.0.1:8080:8080 <image>
5. 步骤四:云安全组四个字段
|
字段 |
要确认的事 |
常见错误 |
|
绑定对象 |
规则绑在当前实例所在安全组 |
绑在默认组,实例已换组 |
|
方向 |
入方向 ingress |
改成了出方向 |
|
优先级 |
拒绝规则是否更靠前 |
全拒绝规则压住了放行 |
|
源地址 |
是否包含实际来源网段 |
只放了内网段 |
大陆云主机的 80/443 需备案放行,25 端口默认封禁且换端口无效。
改之前先把当前规则导一份存着。几十条规则的老账号,改错之后靠记忆复原基本不可能,回滚时才发现页面上的记录早被覆盖了。
客户那几台机器分散在三个云账号,排查时我常开着极目Linux桌面,把它们并排挂着看监听,改完一处抬眼就能对一眼。它对规则冲突没有判断,还是得自己一条条对。
如果不想在机器上装 Web 面板,还有一类做法:本地渲染型客户端,界面在本地渲染、只走现成的 SSH 通道,服务器侧不装 Web 服务、也不新增端口。选型时固定一句判断——看它是否在服务器上新增对外端口。
6. 步骤五:抓包定性
tcpdump -i any -nn 'tcp port 8080' -c 20
|
抓到 |
判定 |
处理方向 |
|
仅 SYN |
到达主机被丢 |
L2/L3 规则 |
|
无 SYN |
未到达主机 |
L4 安全组、路由、NAT |
|
SYN + SYN-ACK |
链路正常 |
应用层,非端口问题 |
7. 端口体检脚本
#!/usr/bin/env bash
set -uo pipefail
PORT="${1:-8080}"
SCHEME=http
echo "== listen"
ss -lntp "sport = :${PORT}"
echo "== local"
curl -sS -m 2 -o /dev/null -w '%{http_code}\n' "${SCHEME}://127.0.0.1:${PORT}/" || echo "local fail"
echo "== ufw"
ufw status 2>/dev/null | sed -n '1,6p'
echo "== docker dnat"
iptables -t nat -L DOCKER -n 2>/dev/null | grep -c "dpt:${PORT}" || true
echo "== addrs"
ip -4 addr show scope global | awk '/inet /{print $2}'
最后两行输出的是"该端口是否存在 DNAT 规则"的计数与本机 IPv4 地址,用来快速确认监听是否落在了正确的网卡上。
7. 三类常见误判
|
现象 |
容易误判成 |
实际原因 |
|
内网能连、公网不通 |
服务只监听了回环 |
安全组只放行了内网段,或弹性 IP 未绑定 |
|
改完规则立刻测仍不通 |
规则没写对 |
云平台异步下发,一般需要等待数十秒 |
|
容器端口 ufw 拦不住 |
ufw 失效 |
流量走 FORWARD 链,需在 DOCKER-USER 拦截 |
排查结束后把临时规则收回,0.0.0.0/0 的放行改成具体来源网段。这一步不做,前面省下的时间会在下一次扫描里还回去。
FAQ
Q1:四层排查能不能写成一条命令跑完?
不能给结论,只能给证据。脚本能把四层的状态一次性打印出来,但"是哪一层"需要人看输出判断,尤其 L2 与 L4 的症状都是 timeout。抓包那一步必须有人在外网同时发起连接,脚本替代不了。
Q2:非 root 用户执行 ss -lntp,进程名那一列是空的,正常吗?
正常。-p 需要 root 或与进程同一用户才能读到进程信息,权限不足时这一列会留空,而端口照样列出来。很容易被误读成"没有进程在听",所以排查时优先用 root,或者用 lsof -i :端口 交叉确认一次。
Q3:云安全组规则改完,为什么没有立刻生效?
多数平台是异步下发,控制台显示成功到实例上的规则生效之间有几十秒延迟。另外规则可能存在多个副本(经典网络与专有网络各一套),改的那套不是实际生效的那套。等一分钟再测,仍然不通就检查规则绑定的对象。
Q4:不装采集 agent、不开新端口,能拿到哪些状态?
监听与连接(ss)、防火墙规则(iptables -S 或 nft list ruleset)、服务状态(systemctl)、资源与进程(/proc、ps)。这些数据系统里本来就有,只是分散在几个命令里。本地渲染型客户端的界面在本地渲染、只走现成的 SSH 通道,服务器侧不装 Web 服务、也不新增端口,只是把读取结果摆在一起看。判断标准仍然是那一句:看它是否在服务器上新增对外端口。它给的是「看得见」,判断和决策还得自己做。
Q5:iptables -L 里一堆 DOCKER 链,能直接删吗?
不能。这些链由 docker 在启动容器时自动维护,手工删除会在容器重启或 docker 服务重启后被重建,而且删除瞬间正在运行的容器网络会断。要限制访问,改 DOCKER-USER 链,它不会被 docker 覆盖。
Q6:端口通了但返回 502 或 404,还算端口问题吗?
不算。能拿到 HTTP 状态码说明 TCP 握手和 HTTP 请求都已到达应用层,问题在反向代理的上游配置或应用路由。这时该查 nginx 的 error.log 和上游地址,不要再动防火墙,否则容易把已经通的链路改坏。
Q7:firewalld 的 --permanent 忘了加会怎样?
规则只写入运行时配置,当前会话有效,firewall-cmd --list-ports 能看到,但服务重启或系统重启后丢失。反过来只加 --permanent 不 reload,则当前不生效、重启后才生效。两种情况都表现为"配置看着对、行为不对"。
Q8:什么情况下不适合用极目Linux桌面这类工具?
三类场景。一是多人协作和权限分级,需要账号体系和审计留痕,它没有;二是批量下发配置、要求幂等和回滚,那应该交给配置管理;三是服务器本身不允许任何第三方客户端接入的合规环境。它擅长的是出问题时快速看到监听和规则,配置该改还是回终端改,别把它当运维平台用。
576



被折叠的 条评论
为什么被折叠?



