来源:
intranet-tunnel/docs/AGENTS-ARCHIVE.md→## *.tunnel.sushike.cloud 基础设施(2026-09-12)· 完整版(原文 843 字符)
*.tunnel.sushike.cloud 基础设施(2026-09-12)· 完整版
(2026-09-13 自工作区 AGENTS.md 的「*.tunnel.sushike.cloud 基础设施(2026-09-12 建立)」 一节下的「排障时容易自设陷阱的两个点」逐字移出;判据与指针留在 AGENTS.md。)
⚠️ 排障时容易自设陷阱的两个点
curl --resolve <域名>:443:127.0.0.1会得到误导结果。
本机测隧道域名时要解析到公网 IP(<公网IP>), 指到 127.0.0.1 会绕过公网入口的 SNI 处理,实测返回 404,让人误判配置没生效。
- DNS 缓存会造成「刚加的记录不生效」的假象。
实测 *.tunnel 记录加好后,本机仍解析到旧的阿里云 IP, 于是 curl 拿到了 all.sushike.cloud.a1.initaf.com 的企业站内容(200 + HubSpot CSP), 一度误以为是 nginx 路由错了。等缓存过期或换 --resolve 强制解析再测。
- Portainer CE 的响应头容易认错:它自带
Content-Security-Policy: ... js.hsforms.net ... recaptcha ...、 Permissions-Policy: accelerometer=(), ...、X-Csrf-Token, 看起来像某个企业站。认隧道是否生效要看 X-Proxy-By: intranet-tunnel 头 (内置反代添加的),或看 HTML 里的 ng-app="portainer" / <title>Portainer</title>。
来源:
intranet-tunnel/deploy/docker/client/README.md→## 七、验证与排查(原文 3282 字符)
七、验证与排查
# 看实时日志
docker compose logs -f
# 打印生效配置(Token 已脱敏)——判断"配的到底是什么"最直接的办法
docker compose exec tunnel-client /usr/local/bin/tunnel-client -env /app/.env -print-config
# 版本
docker compose exec tunnel-client /usr/local/bin/tunnel-client -version
# 容器是否健康
docker compose ps
# Web 配置界面与健康检查端点
curl -s http://127.0.0.1:47802/healthz
/healthz 无需鉴权(容器健康检查拿不到口令),返回形如:
{"persistence":true,"running":true,"state":"connecting","status":"ok","tunnels":0,"uptime":"12s","version":"1.2.1"}
persistence:false 意味着配置库不可用(多半就是 data/ 权限问题,见第三节)。
tunnels 是当前生效的隧道总数,等于「本地静态(tunnels.json / 界面上建的) + 服务端下发」之和 —— 所以它可能多于 tunnels.json 里的条数: 服务端会额外下发自己的隧道。实测 NAS 上就是 3 条本地 + 1 条服务端下发 = 4, 看到数字比文件里的多,属正常,不是重复注册。
怎么确认访问真的走了本方案
判据是响应头 X-Proxy-By: intranet-tunnel(内置反代添加的):
curl -sI https://<隧道域名>/ | grep -i '^x-proxy-by'
⚠️ 只看状态码会被应用自带的响应头带偏:被穿透的应用自己也会发一堆头 (Portainer 就有 CSP、X-Csrf-Token 等),看着「像另一个系统」, 但它们只证明「应用收到了请求」,不证明请求是经由本方案进来的。
更硬的判据是与后端直连做 SHA256 逐字节比对:
# ① 后端直连(在客户端所在机器上执行;端口换成被穿透服务的端口)
curl -s http://host.docker.internal:18080/ | sha256sum
# ② 经隧道(域名要解析到服务端公网 IP;不要 --resolve 指到 127.0.0.1,
# 那会绕过公网入口的 SNI 处理,实测会返回 404,容易被误判成配置没生效)
curl -s https://<隧道域名>/ | sha256sum
两条哈希一致,才说明隧道端到端逐字节透传。页面含动态内容(时间戳、CSRF 令牌)时 哈希本来就会不同,这时改用 curl -sI 比对响应头。
常见问题
| 现象 | 原因与处理 | |
|---|---|---|
客户端启动失败: config: TOKEN 不能为空 | client.env 没填或名字不对;compose 读的是 ./client.env,不是 .env | |
Token 校验失败 | Token 失效——服务端面板里重置后更新 client.env,再 docker compose up -d(restart 不重读 env_file) | |
connection refused / i/o timeout | 服务端地址或端口不对,或服务端 47800 未放行;先在宿主机上 nc -vz <SERVER_ADDR> 验证 | |
x509: certificate is valid for ... | 服务端用自签证书,需 TLS_INSECURE=true;若有受信任证书则应为 false | |
| 隧道在面板里显示在线,但访问域名 502 | 大概率是内网地址问题:容器里的 127.0.0.1 指向容器自己,见第六节 | |
| 日志刷屏 | 调 LOG_LEVEL=warn | |
改了 client.env 不生效 | env_file 只在创建容器时读取:docker compose up -d --force-recreate | |
| 界面打不开 | ①WEB_ENABLE=true 了吗;②WEB_LISTEN 必须是 0.0.0.0:47802(写 127.0.0.1 只绑容器自己的回环);③从局域网访问时确认 compose 的 ports 是 "47802:47802",不是只绑回环的 "127.0.0.1:47802:47802";改完必须 up -d 重建(restart 不重建容器,端口映射不会变);④在 NAS 上 `ss -lnt \ | grep 47802` 看实际绑在哪个地址 |
| 界面能开,但保存的配置重启就没了 | data/ 没有授权给 uid 10001,配置库降级为无持久化;日志搜「配置库打开失败」,重跑 sh init.sh 并确认 chown 成功 | |
容器一直 unhealthy | 健康检查优先探 /healthz,失败才退回 pgrep 进程存活;两者都失败说明进程真的有问题,看 docker compose logs | |
| 界面里有字段是灰的、写着「由环境变量接管」 | 该键在 client.env 里设置过,优先级更高;见第五节 | |
up / restart 卡住约 60 秒后报 mkdir /home/<用户名>/.docker: permission denied | 家目录不归你所有(NAS 实测属主是 root:root 755)→ 先 export DOCKER_CONFIG=/tmp/dsh-docker-config 绕过,根治用 root 把家目录 chown 还给自己。⚠️ ps / config / logs 等只读子命令不受影响,容易误判成 compose 没问题;见第九节 | |
Conflict. The container name "/<旧ID>_tunnel-client" is already in use | up 被中断后 dockerd 里残留的名字占用,而 docker inspect / docker rm / docker ps -a 都看不到它:先 kill -9 残留的 compose 进程,再 docker rm -f tunnel-client 后 up -d;见第九节 |
关于 EXPOSE / ports
EXPOSE 47802是 Web 配置界面端口,仅当WEB_ENABLE=true时才真正监听;- compose 里的
ports: ["47802:47802"]绑所有接口(等价于0.0.0.0:47802),
局域网内可直接访问;只绑回环的旧写法 "127.0.0.1:47802:47802" 已不再使用, 详见第四节(含如何限制来源);
- 关闭 Web 界面时客户端不监听任何端口,这个端口映射不会有任何作用——
那是正常形态,不是配置漏写。