部署日期:2026-09-30 | 执行:DSH 会话(自动配置 + 三步实测验证) 目标:在家庭局域网之外访问 DSH 机的下载中心(8899) 结论:已打通并验证。手机/任何浏览器打开
https://111.231.0.226:8899/即可(自签证书,首次点一次「继续访问」)。
手机 / 任意浏览器
│ HTTPS(自签证书,TLS 加密)
▼
腾讯云 CVM 111.231.0.226(ap-shanghai)
│ dl-tls-proxy.service:Node 零依赖 TLS 终止,监听 0.0.0.0:8899
│ ↓ 明文仅存在于云端本机回环
│ http://127.0.0.1:8898
▼
SSH 反向隧道(DSH 机 dl-tunnel.service 主动外连)
│ 云端只监听回环 127.0.0.1:8898 —— 公网摸不到
▼
DSH 机 127.0.0.1:8899(下载中心 + HTTP Basic 认证)
设计要点:
GatewayPorts no,远端转发自动绑到 127.0.0.1:8898;公网只有 TLS 代理一个入口。这比"改成 GatewayPorts clientspecified 让隧道直接对外"更安全,也少动一台生产机。authorized_keys 里的条目带 restrict,port-forwarding,permitlisten="127.0.0.1:8898" —— 这把密钥不能开 shell、不能转发别的端口、只能监听那一个回环口。Restart=always,隧道断线自动重连(ServerAliveInterval=20、ExitOnForwardFailure=yes)。| 检查项 | 结果 |
|---|---|
公网无凭据 GET / | 401 |
公网有凭据 GET / | 200,页面标题「文件下载」 |
| 公网旧密码 | 401(改密后旧密码立即失效) |
公网 GET /healthz | 200(故意免认证,见踩坑 §6.3) |
局域网无凭据 GET / | 200(内网免密) |
局域网无凭据 POST /api/upload/* | 400(已通过认证闸门,仅因请求体不合法;证明内网免密对上传同样生效) |
内网伪造 X-DL-Relay: 1 且无凭据 | 401(防伪造,见 §三·内网免密) |
局域网/本机 GET /healthz | 200(巡检脚本不受影响) |
| HTTPS 证书 | CN=111.231.0.226,SAN 含 IP:111.231.0.226,有效期至 2036-09-26 |
| 视角 | 结果 |
|---|---|
| 本机直连(家庭宽带 → 公网) | 401 / 200 ✅ |
经 v2ray 境外出口(美国洛杉矶 176.122.137.2)→ 上海 | 401 / 200 / healthz 200 ✅ |
第二条是真正的「站外」验证 —— 证明公网可达,而不是只在服务器自己身上自测。
| 路径 | 实测速度 |
|---|---|
| 公网(手机/在外) | ~360 KB/s(约 2.9 Mbps) ← 腾讯云出口带宽上限 |
| 经 v2ray 境外出口 | ~343 KB/s |
| 局域网直连(对照) | 717 MB/s |
换算:10 MB ≈ 30 秒;100 MB ≈ 5 分钟;1 GB ≈ 48 分钟;5 GB 4K 电影 ≈ 4 小时。
⚠️ 这条路的定位是「传文档、随手取文件」,不是「在外拉大视频/模型」。要全速请走 Windows 笔记本的 Tailscale 直连(家宽上行 ~48 Mbps,快 15 倍,且不暴露任何端口)。
| 入口 | 地址 | 速度 | 加密 | 适用 |
|---|---|---|---|---|
| 局域网 | http://192.168.31.76:8899/ | 本地 | ❌ | 家里 |
| 公网中继 | https://111.231.0.226:8899/ | ~2.9 Mbps | ✅ TLS(自签) | 手机/在外应急 |
| Tailscale 私网 | http://100.67.143.34:8899/ 或 http://dsh-76:8899/ | ~48 Mbps | ✅ WireGuard | Windows 笔记本 |
https://dsh-76.tailcbc4d1.ts.net/ | — | ✅ 真证书 | ❌ 已判定不可用,见踩坑 §6.1 |
认证(2026-09-30 二次调整:改为「内网免密、公网要密码」):
| 入口 | 是否要密码 |
|---|---|
局域网 192.168.31.76:8899 | 不要(含本机回环;上传也免密) |
公网 https://111.231.0.226:8899/ | 要,账号 zyw |
Tailscale 私网 100.67.143.34:8899 | 要 |
密码存放于 /home/zyw/Downloads/.dl-hub-auth(权限 600,被 systemd 单元以 EnvironmentFile 挂载),改完 systemctl restart dl-server 即生效。
不能只按来源 IP 判断内网。公网请求是经 SSH 反向隧道进来的,remoteAddress 同样是 127.0.0.1,与本机浏览器完全无法区分 —— 若写成「私网/回环就免密」,公网入口的认证会被静默关掉。
正确做法(已实现):
dl-tls-proxy.js 转发时强制覆盖写入 X-DL-Relay: 1;免密网段可用环境变量调整(默认 127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16):
# 想让 Tailscale 私网也免密,就在 dl-server.service 里加:
Environment=DL_AUTH_EXEMPT=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10
# 想回到「谁都要密码」,设成 none
Environment=DL_AUTH_EXEMPT=none
# 公网链路总控(本机 + 云端一起看)
bash ~/Downloads/dl-remote-ctl.sh status # 两端服务 + 端口 + 地址
bash ~/Downloads/dl-remote-ctl.sh test # 链路自测(认证校验)
bash ~/Downloads/dl-remote-ctl.sh ext # 境外出口验证公网真可达
bash ~/Downloads/dl-remote-ctl.sh restart # 两端一起重启
bash ~/Downloads/dl-remote-ctl.sh log 50 # 本机隧道日志
bash ~/Downloads/dl-remote-ctl.sh cloud-log # 云端 TLS 代理日志
# Tailscale 私网(Windows 用)
bash ~/Downloads/tailscale-ctl.sh status|url|test|funnel-on|funnel-off
# 下载中心本身
systemctl status dl-server
bash ~/Downloads/install-dl-service.sh # 重装单元(已同步认证配置,不会冲掉)
| 位置 | 文件 | 说明 |
|---|---|---|
| 本机 | /etc/systemd/system/dl-tunnel.service | SSH 反向隧道单元 |
| 本机 | ~/.ssh/id_ed25519_dltunnel{,.pub} | 隧道专用密钥 |
| 本机 | ~/Downloads/.dl-hub-auth | Basic 认证凭据(600) |
| 本机 | ~/Downloads/dl-remote-ctl.sh | 链路控制脚本 |
| 本机 | /etc/systemd/system/dl-server.service.bak-20260930-preauth | 加认证前的单元备份 |
| 云端 | /root/dl-tls-proxy.js | TLS 终止代理(零依赖 Node) |
| 云端 | /root/dl-tls/{cert,key}.pem | 自签证书(key 600) |
| 云端 | /etc/systemd/system/dl-tls-proxy.service | 代理单元 |
| 云端 | /root/.ssh/authorized_keys | 含受限隧道公钥(原为空文件) |
| 本机 | ~/Downloads/tailscale-ctl.sh | Tailscale 控制脚本 |
# 1) 本机停隧道
sudo systemctl disable --now dl-tunnel
# 2) 云端停代理并收回密钥
~/Downloads/ssh_cloud.sh 'systemctl disable --now dl-tls-proxy; rm -f /root/.ssh/authorized_keys'
# 3) 腾讯云控制台删除 TCP 8899 入站规则
# 4) 若要连 Basic 认证一起撤:sudo rm /home/zyw/Downloads/.dl-hub-auth
# && sudo systemctl daemon-reload && sudo systemctl restart dl-server
https://dsh-76.tailcbc4d1.ts.net/ 看起来完美(真证书、无需装客户端),实测约一半请求失败。逐 IP 排查结论:
| Funnel 入口 IP | 结果 |
|---|---|
208.111.34.11 | 5/5 成功 |
208.111.35.209 | 0/5 成功 |
失败报文是 GnuTLS, handshake failed: TLS 链接非正常地终止了 —— TLS 握手被异常终止,GFW 按 IP 重置。DNS 在这两个 IP 间轮询,所以表现是"刷一下能开、再刷就超时"。手机浏览器无法固定 IP,故这条路放弃。
如果哪天要救 Funnel:只能在客户端侧把域名解析钉到可用 IP,浏览器做不到;不要浪费时间反复开关 Funnel(Let's Encrypt 有速率限制)。
排查过程与结论:
MappingVariesByDestIP: false(全锥型 NAT)+ UPnP/NAT-PMP/PCP 全部可用,看起来直连大有希望。192.168.1.2(内网地址),而 192.168.1.1 是"中国电信智能网关"(HTTP 200,标题可辨)。→ 双层 NAT,公网 IP 14.145.49.106 挂在电信网关上。/tmp/upnp-igd.py)后,从腾讯云打回 14.145.49.106:18899 仍然超时。37419(别的设备建的)从公网同样不通 → 是双层 NAT 的普遍现象,不是我们配错。附带发现:家宽没有可用的 IPv6(本机唯一全局 v6 地址
fd7a:115c:a1e0::是 Tailscale 自建的 ULA),所以走 IPv6 直连这条捷径也不成立。
/healthz 必须对认证豁免加 Basic 认证后如果 /healthz 也需要凭据,dl-healthcheck.timer 每 30s 的无凭据探活就会失败 → 连败 2 次自动重启服务 → 自愈机制变成每 60 秒自毁一次。已在 download-server.js 里显式放行 /healthz,并实跑 dl-healthcheck.sh 验证退出码 0。
/usr 是只读 overlay装 Tailscale 时 cp 到 /usr/sbin/tailscaled 报 只读文件系统:Deepin 25 把 /usr 挂成只读 overlay(系统保护 + OSTree)。 /usr/local/bin 是可写的(它实际落在 /var 下的一个 bind 挂载),所以二进制装那里、systemd 单元里的 ExecStart 路径也相应改写。 另外官方 apt 源里没有 tailscale 包(Deepin 不是 Ubuntu),用官方静态二进制 tarball 最稳。
pkill -f "http.server 8899" 清理云端探针pkill -f 会匹配到这条 ssh 命令自身的命令行(里面就含该字符串),结果把自己的 SSH 会话杀掉、清理动作半途而废。 正确写法(正则打断自匹配):pkill -f "http 0 serve[r] 8899"。同样的坑在 llmste[r] 那次已经踩过一回。
dl-hub 加了认证后的连带影响install-dl-service.sh 里原先是旧版单元(缺 DL_MERGE_CONCURRENCY/DL_UP_PARALLEL/UV_*),谁跑一次就会把线上配置冲掉。已同步为线上版本并加入认证挂载。restart-dl-server.sh / install-dl-service.sh 末尾那条 curl http://127.0.0.1:8899/ 从回环发起,属内网免密范围,返回 200 是正常的;只有公网入口才是 401。/healthz 探活),其余引用都只是打印 URL。所以加认证没有破坏任何自动化。--accept-dns=false本机是 v2ray + 内网混合环境,若让 Tailscale 接管 DNS(/etc/resolv.conf)有搞坏内网解析与代理的风险。故固定不接管 DNS;需要 MagicDNS 名时用 tailscale-ctl.sh url 查看,或直接用 100.67.143.34。
| 项 | 说明 | 收益 |
|---|---|---|
| 换成真证书(推荐) | 注册免费 DuckDNS 子域 → 解析到 111.231.0.226 → 用 DNS-01 签 Let's Encrypt 证书 | 去掉浏览器的证书警告,不用域名备案 |
| 攻双层 NAT | 登录电信智能网关(192.168.1.1,需管理员密码)加端口映射或 DMZ | 家宽直连,~48 Mbps(但运营商可能仍封入站,有白折腾风险) |
| 关掉 Funnel | bash ~/Downloads/tailscale-ctl.sh funnel-off | 少一个公网入口(当前它有一半概率可用,且受 Basic 认证保护) |
| 证书到期监控 | 自签证书 10 年有效(2036 到期),实际不用管;若换 Let's Encrypt 需自动续期 | — |
| 公网入口来源限制 | 腾讯云安全组把 8899 来源从 0.0.0.0/0 收窄到常用出口 IP | 减少暴露面(代价:出门换网络就连不上) |
| 内容 | 位置 |
|---|---|
| 下载中心 Basic 认证 | ~/Downloads/.dl-hub-auth(600);账号 zyw |
| 隧道专用 SSH 密钥 | ~/.ssh/id_ed25519_dltunnel(600) |
| Tailscale 账号 | zhouyuwenprc@gmail.com;本机节点 dsh-76 = 100.67.143.34 |
| 腾讯云 SSH | 沿用 ~/Downloads/ssh_cloud.sh(root / 密码方式) |