站点交接白皮书 · 2026-11-14 PassNAT HTTPS 排障实录
一句话结论:
cag.odn.cc打不开,既不是证书问题,也不是隧道配置写错,而是两层「地址」问题叠加——先是节点114.66.28.184的 443 被平台侧屏蔽,换节点后又被 Windows 的 IPv6 DNSfe80::1拖回旧 IP。两处都清掉后,HTTPS 稳定 200、0.42 秒响应。这份文档写给谁:下一次遇到「隧道显示正常但网页打不开」的自己,以及任何接手这台机器的人。全部结论都有实测数据支撑,命令可直接复制。
零、五分钟速览
| 维度 | 内容 |
|---|---|
| 症状 | https://cag.odn.cc/notes/ 连接超时;http://114.66.28.184:15682/notes/ 却正常 |
| 误判 1 | 以为 localAddr = 127.0.0.1:3000 填错了(站点在 3102)—— 实际门户 3000 反代 /notes,填 3000 才对 |
| 误判 2 | 以为证书有问题 —— 实际 SAN、有效期、配对、链路四项全对 |
| 真因 1 | 节点 114.66.28.184 的 443 端口 TCP 层不可达(同节点 80/8500/15682 全通) |
| 真因 2 | Windows 优先使用 IPv6 DNS fe80::1,该路由器 DNS 仍返回旧 IP 184,改 IPv4 DNS 无效 |
| 处置 | 控制台把 HTTPS 隧道切到 114.66.28.183;本机 IPv6 DNS 换成 2400:3200::1 |
| 结果 | 解析 183,curl 200 / 0.42s,真实浏览器全绿(0 报错、0 混合内容) |
| 耗时 | 从报障到彻底解决约 1 小时,其中 3 次是因为「只验证了一半」而误判 |
一、时间线:这一天到底曲折在哪
| 时间 | 事件 | 当时的判断 | 事后看 |
|---|---|---|---|
| 前一日 16:06 | 申请 Let's Encrypt 证书,落到 C:\certs\ | 以为完事 | 证书确实没问题,但它不是瓶颈 |
| 前一日 16:15–16:53 | 建了多条测试隧道(183 / 96.65 / 14.91 节点,cag1.odn.cc 等) | 试探节点 | 留下 5 份 toml 噪音,后期全靠它们做对照实验 |
| 前一日晚 | 提交工单:HTTPS 隧道不通 | 怀疑平台没开 HTTPS | 平台回复「青春版支持 HTTPS」,方向正确但没指出端口问题 |
| 今日 10:13 | 工单回复:走节点 443、不要带端口、必须 https | 认为配置一切就绪 | 这条信息是重要线索,但漏了「节点 443 可能没开」 |
| 10:15–10:20 | 逐项体检配置、证书、frpc、DNS | 全部正常,陷入困惑 | 缺了「端口矩阵」这一步,看到 443 超时就该立刻做 |
| 10:21 | 用户控制台把节点从 184 切到 183 | 客户端重新下发配置 | 关键一步,但此时 DNS 还没跟 |
| 10:22 | 用 --resolve 强制指向 183 | 200 / 1.37s | 证明应用与证书全对,问题被压缩到「解析」一个变量 |
| 10:23 | 权威 DNS 切到 183,TTL 603 | 以为马上好 | 公共 DNS 陆续生效,114DNS 慢了 8 分钟 |
| 10:26–10:29 | 死等 114DNS | 以为只是缓存 | 114DNS 确实 10:29 才切 |
| 10:40 | 用户改完 IPv4 DNS、等够了,仍不通 | 一度怀疑平台又出问题 | 真凶:IPv6 DNS fe80::1 从未被改过 |
| 10:42 | 换掉 IPv6 DNS,清缓存 | 立即 200 / 0.42s | 收工 |
最贵的一课:10:22 就已经证明「链路 + 应用 + 证书」全对,剩下的只有 DNS。但因为只查了 IPv4 那一半,又白等了 18 分钟。
二、目标与环境速查
需求:让 cag.odn.cc 以 HTTPS 承载笔记站(/notes/),走 PassNAT 青春版隧道;http://114.66.28.184:15682 作为备用入口。
| 项 | 值 |
|---|---|
| 站点入口 | 门户 server.js(计划任务 KaoyanPortal,端口 3000)→ 反代 /notes → Next 笔记站(KaoyanNotes,端口 3102) |
| 隧道类型 | https + https2http 插件(frp 原生能力,HTTPS 在边缘终结,回源走 HTTP) |
| 隧道节点 | 114.66.28.183:8500(原 184,已废弃) |
| 隧道配置文件 | C:\Users\cag\AppData\Roaming\com.passnat.client\frpc\18093f6f7432318b.toml |
| 客户端 | C:\Users\cag\AppData\Local\PassNAT\frpc.exe(v0.71.0)+ passnat-client-v2.exe |
| 证书目录 | C:\certs\cag.odn.cc-chain.pem、C:\certs\cag.odn.cc-key.pem |
| 域名托管 | odn.cc 的 NS 在阿里云(vip1/vip2.alidns.com),由 PassNAT 平台代管记录 |
| 备用 HTTPS | https://cag.quest(Cloudflare 隧道,独立链路) |
三、第一步:先证明「不是我们的错」
排障的纪律是先把不变量钉死,把变量一个个减掉。配置层做了四项体检,全部通过。
3.1 隧道配置(关键字段)
serverAddr = "114.66.28.183"
serverPort = 8500
metadatas.id = "udojmM76fS"
[[proxies]]
name = "91a7bece-0780-4527-aaa4-a08852686f56"
type = "https"
customDomains = ["cag.odn.cc"]
[proxies.plugin]
type = "https2http"
localAddr = "127.0.0.1:3000"
hostHeaderRewrite = "127.0.0.1"
crtPath = "C:\\certs\\cag.odn.cc-chain.pem"
keyPath = "C:\\certs\\cag.odn.cc-key.pem"
三点必须同时满足,缺一不可:
type = "https"+https2http插件(不是裸tcp)。customDomains必须与访问域名完全一致,不能带端口、不能带路径。- Windows 下证书路径的
\必须写成\\,否则 frpc 报error unmarshaling json,隧道静默不起来。
3.2 证书四项核对
| 检查项 | 命令 | 结果 |
|---|---|---|
| SAN 是否含域名 | openssl x509 -in crt.pem -noout -ext subjectAltName | DNS:cag.odn.cc ✅ |
| 有效期 | openssl x509 -in crt.pem -noout -dates | 2026-11-13 → 2027-02-11 ✅ |
| 私钥是否配对 | 比对 x509 -pubkey 与 rsa -pubout 的 sha256 | 一致 ✅ |
| 证书链是否完整 | grep -c "BEGIN CERTIFICATE" chain.pem | 3 张(叶子 + 中间 + 根)✅ |
要点:PassNAT 的证书不上传平台,只存在运行客户端的机器上,控制台里填的是绝对路径。这是它和 CDN 厂商最大的区别,也是最容易误解的地方。
3.3 客户端与长连接
Get-CimInstance Win32_Process | ? { $_.Name -match 'frpc|passnat' } | Select ProcessId,CommandLine
Get-NetTCPConnection | ? OwningProcess -in 36812,37720
两条 frpc 进程分别挂着 4e57717e...(tcp→3000)与 18093f6f...(https→cag.odn.cc),都到 114.66.28.184:8500 处于 Established。配置语法另用 frpc.exe verify -c xxx.toml 验证,返回 syntax is ok。
结论:客户端在线、配置合法、证书齐全。 问题不在本机配置。
四、第一个坑:差点把对的改成错的
隧道里 localAddr 写的是 127.0.0.1:3000,而笔记站明明跑在 3102。第一反应是「填错了,改成 3102」。
动手前先量了一下:
curl -s -o /dev/null -w "%{http_code} %{size_download}\n" http://127.0.0.1:3000/notes/ # 200 66057
curl -s -o /dev/null -w "%{http_code} %{size_download}\n" http://127.0.0.1:3102/notes/ # 200 66057
两边返回的字节数完全相同,说明门户 3000 已经把 /notes 反代到 3102。localAddr = 3000 是正确的,改成 3102 反而会绕过门户的鉴权注入与静态资源处理。
教训:看到「端口不一致」不要先改,先量。一个
curl的字节数对比,省掉一次线上故障。
五、第二个坑(真凶一):443 端口被节点屏蔽
配置全对、客户端在线、页面本地正常,那么问题只能出在「路上」。于是做端口矩阵:
| 目标 | 结果 | 含义 |
|---|---|---|
114.66.28.184:443 | TIMEOUT | ❌ 本节主角 |
114.66.28.184:80 | OPEN | 节点活着 |
114.66.28.184:8500 | OPEN | frps 控制端口正常 |
114.66.28.184:15682 | OPEN | 备用 HTTP 隧道正常 |
114.66.28.183:443 | OPEN | 同机房另一节点正常 |
110.42.96.65:443 | OPEN | 正常 |
103.40.14.91:443 | OPEN | 正常 |
本机访问 baidu.com:443 | 200 / 0.5s | 本机出站 443 正常 |
同节点其他端口全通、唯独 443 超时;同机房其他节点 443 全通。 这不是本机防火墙(否则百度也不通),不是配置(否则 15682 也不通),只能是该节点的 443 未放行 / VIP 未挂载。
再用外部视角复核一次:通过本机代理(另一网络出口)发 CONNECT cag.odn.cc:443,同样返回 502 Bad Gateway——换出口也不通,锁定目标侧。
诊断口诀:一个端口不通时,先问三个问题——同 IP 其他端口通吗?同角色其他 IP 通吗?换个出口还通吗?三问之后,责任方基本就定了。
六、修复一:控制台换节点 184 → 183
操作:PassNAT 控制台 → 隧道管理 → 该 HTTPS 隧道 → 节点/线路改为 114.66.28.183 → 保存 → 重启客户端。
生效标志:本机 toml 被重新下发(serverAddr 从 184 变 183,文件 mtime 更新到 10:21),frpc 自动重连。
此时出现的一个假象:控制台里所有节点的「延迟」永远显示 N/A,让人以为一个都不能用。这是平台测速显示的问题,与实际连通性无关——实测 183 页面 0.42 秒,比 Cloudflare 的 cag.quest(1.67 秒)还快。判断节点可用性,永远以「TCP 端口探活 + 真实页面响应」为准,不要看那个数字。
验证手法(DNS 还没更新时特别有用):
curl -sS --resolve cag.odn.cc:443:114.66.28.183 \
-o /dev/null -w "%{http_code} %{time_total}s\n" https://cag.odn.cc/notes/
# 200 1.37s
--resolve 绕过 DNS,直接把「域名 + SNI + 指定 IP」拼在一起打,能通就证明:链路、证书、https2http、hostHeaderRewrite、后端应用,全部正确。剩下的变量只有解析。
七、第三个坑(真凶二):DNS 的三层缓存
7.1 权威记录与 TTL
curl -s "https://dns.alidns.com/resolve?name=cag.odn.cc&type=A"
# {"Answer":[{"TTL":525,"type":1,"data":"114.66.28.183"}]}
权威已指向 183,TTL 603 秒(10 分钟)。这个数字决定了「最坏情况下要等多久」,也决定了排查时该不该怀疑 DNS。
7.2 公共 DNS 逐个点名
| DNS | 返回 | 备注 |
|---|---|---|
223.5.5.5(阿里) | 183 | 快 |
119.29.29.29(DNSPod) | 183 | 快 |
180.76.76.76(百度) | 183 | 快 |
8.8.8.8(Google) | 183 | 快 |
1.1.1.1(Cloudflare) | 183 | 快 |
114.114.114.114 | 184 | 10:29 才切换,慢了约 8 分钟 |
本机默认(路由器 fe80::1) | 184 | 真正的拦路虎 |
反直觉点:DNS 变更不是「一改全改」。权威改了,各地递归缓存各自倒计时。「我等了十分钟」不等于「缓存都过期了」,必须逐个 DNS 点名验证。
7.3 最阴的一层:IPv6 DNS fe80::1
用户已经把网卡 IPv4 DNS 改成 119.29.29.29 / 223.5.5.5,也等足了时间,依然超时。这时用一条命令看真相:
Get-DnsClientServerAddress | ? { $_.ServerAddresses.Count -gt 0 } |
Select InterfaceAlias, AddressFamily, @{n='DNS';e={$_.ServerAddresses -join ', '}}
输出暴露了问题:
以太网 2 (IPv4) 119.29.29.29, 223.5.5.5 ← 用户改的
以太网 23 (IPv6) fe80::1, fe80::1 ← 从没被改过
Windows 的解析顺序里 IPv6 DNS 优先,而 fe80::1 是路由器通过 RA 下发的地址,它(或其上游)还缓存着旧 IP。验证一下:
nslookup cag.odn.cc fe80::1 # → 114.66.28.184 (旧)
nslookup cag.odn.cc 119.29.29.29 # → 114.66.28.183 (新)
两条查询走的是同一台机器、同一个域名,结果却不同。 这就解释了一切:改 IPv4 DNS 只是改了一半,系统压根没在用那一半。
教训:任何「改 DNS」的操作,必须同时确认 IPv4 与 IPv6 两个 AddressFamily。只改一半,等于没改,而且会让人误以为「DNS 生效了但网站还是坏的」,把排查引向完全错误的方向。
八、修复二:替换 IPv6 DNS
# 备份原值:fe80::1(路由器下发)
netsh interface ipv6 set dnsservers name="以太网" source=static address=2400:3200::1 register=none validate=no
netsh interface ipv6 add dnsservers name="以太网" address=2400:3200:baba::1 index=2 validate=no
Clear-DnsClientCache
还原(如需回到路由器下发):
netsh interface ipv6 set dnsservers name="以太网" source=dhcp
结果:
系统解析:cag.odn.cc → 114.66.28.183
curl :200 0.42s
九、验收:纯净环境,零强制解析
最终验收不使用任何 --resolve、不注入 host 规则,完全依赖系统 DNS,模拟真实用户:
| 项目 | 结果 |
|---|---|
| 系统解析 | cag.odn.cc → 114.66.28.183 ✅ |
首页 /notes/ | 200,首字节 0.42s,浏览器完整加载 1.69s |
| 笔记详情 | 200,渲染 6 个表格,正文 8551 字,0 破图 |
表格库 /notes/sheets/ | 200 |
收藏 /notes/favorites/ | 200 |
| 安全上下文 | window.isSecureContext === true,协议 https: |
| 混合内容 | 0 条(页面内无 http:// 资源引用) |
| 控制台错误 | 0 条 |
| 证书校验 | 浏览器未用 -k 也通过(域名匹配 + 链可信) |
附注:
/notes/login返回 404 属正常——登录页在门户/login(200),笔记站本身不设登录路由。验收时别把它当成故障。
十、可复用的排障手册
10.1 五步定位法(按顺序,不要跳)
- 解析:
nslookup 域名+ 逐个公共 DNS 点名 + IPv4/IPv6 都要看。 - 端口:目标 IP 的 443 是否 TCP 可达;同 IP 其他端口、同角色其他 IP 做对照。
- 隧道配置:
type、customDomains、localAddr、证书路径(\\转义)。 - 证书:SAN、有效期、公私钥配对、链完整性。
- 应用:
--resolve强制打通后看页面,确认反代与静态资源。
10.2 六条命令(照抄即可)
# 1 端口探活(不用装 nc)
timeout 5 bash -c 'cat < /dev/null > /dev/tcp/1.2.3.4/443' && echo OPEN || echo TIMEOUT
# 2 强制域名到指定 IP(绕过 DNS,验证链路与证书)
curl -sS --resolve example.com:443:1.2.3.4 -o /dev/null -w "%{http_code}\n" https://example.com/
# 3 用 DoH 绕过一切本地缓存
curl -sS --doh-url https://dns.alidns.com/dns-query -o /dev/null -w "%{http_code}\n" https://example.com/
# 4 指定 DNS 服务器查询
nslookup example.com 223.5.5.5
# 5 隧道配置语法校验
frpc.exe verify -c C:\path\to\tunnel.toml
# 6 看域名的权威 TTL(决定要等多久)
curl -s "https://dns.alidns.com/resolve?name=example.com&type=A"
# 7 网卡 DNS 全量(务必区分 AddressFamily)
Get-DnsClientServerAddress | ? { $_.ServerAddresses.Count -gt 0 } |
Select InterfaceAlias, AddressFamily, @{n='DNS';e={$_.ServerAddresses -join ', '}}
10.3 四个反直觉结论
- 「节点延迟 N/A」不代表不可用——平台测速显示问题,实测 0.42 秒。判断可用性只看端口探活与页面响应。
- 改了 IPv4 DNS 不等于改了 DNS——IPv6 DNS 优先级更高,且常由路由器 RA 静默下发,极易漏改。
- 443 通不代表站点通——还需
https2http插件、hostHeaderRewrite、证书路径转义、后端端口四件事同时正确。 --resolve能通就是好消息——它一次性排除了链路、证书、反代、应用四层,把问题精确压缩到「解析」。
10.4 三个「不要」
- 不要看到端口不一致就直接改配置(先
curl比字节数,可能上游本来就反代)。 - 不要用「我等了十分钟」当作缓存已过期的证据(逐个 DNS 点名)。
- 不要在控制台测速数字上纠结(用真机探活)。
十一、遗留与维护
| 事项 | 说明 | 建议动作 |
|---|---|---|
| 证书续期 | 当前证书 2027-02-11 到期 | 续期后替换 C:\certs\ 下 chain 与 key 两个文件,重启 PassNAT 客户端即可,无需改控制台 |
| 184 节点 443 | 该节点 443 仍不可达 | 若日后需回迁,先回工单要求开放 443,或直接留在 183 |
| 历史测试隧道 | 目录里有 5 份 toml(183/96.65/14.91,cag1.odn.cc 等) | cag1.odn.cc 已无解析,属废弃实验;可择机在控制台删除,避免日后误判 |
| IPv6 DNS | 已改为阿里公共 DNS | 需要恢复路由器下发:netsh interface ipv6 set dnsservers name="以太网" source=dhcp |
| 双 HTTPS 入口 | cag.quest(CF,1.67s)与 cag.odn.cc(PassNAT,0.42s)并存 | 两者独立链路,可互为备份;cag.odn.cc 目前更快 |
| 备用 HTTP 入口 | http://114.66.28.184:15682 | 仅在 HTTPS 故障时作为临时通道,注意明文传输 |
十二、一页速查卡
症状:隧道客户端在线、配置校验通过、本地页面正常,但公网 https 超时
↓
① nslookup 域名 → 看解析到哪
② 端口矩阵(同 IP 多端口 / 同角色多 IP / 换出口)
↓ 443 不通且仅此端口 → 平台侧,换节点
③ 控制台换节点 → 客户端重新下发 toml → --resolve 强制验证
↓ 强制能通 = 只剩 DNS
④ 逐个公共 DNS 点名 + 查权威 TTL
↓ 公共 DNS 都对了但本机不行
⑤ Get-DnsClientServerAddress → 看 IPv4 与 IPv6 两行
↓ IPv6 还是 fe80::1 → 换成公共 IPv6 DNS
⑥ 清缓存,纯净访问验收
写在最后:这次真正耗时的不是技术,而是「只验证了一半就下结论」。10:22 的
--resolve已经证明其余全对,如果当时多敲一条Get-DnsClientServerAddress,后面 18 分钟的等待根本不会发生。排障的效率,取决于你多快把变量收敛到一个。