远程访问部署与实测报告-20260930.md

下载中心「公网远程访问」部署与实测报告

部署日期: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 认证)

设计要点:

二、实测数据(都是实测,不是估算)

认证与可用性

检查项结果
公网无凭据 GET /401
公网有凭据 GET /200,页面标题「文件下载」
公网旧密码401(改密后旧密码立即失效)
公网 GET /healthz200(故意免认证,见踩坑 §6.3)
局域网无凭据 GET /200(内网免密)
局域网无凭据 POST /api/upload/*400(已通过认证闸门,仅因请求体不合法;证明内网免密对上传同样生效)
内网伪造 X-DL-Relay: 1 且无凭据401(防伪造,见 §三·内网免密)
局域网/本机 GET /healthz200(巡检脚本不受影响)
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✅ WireGuardWindows 笔记本
Tailscale Funnelhttps://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,与本机浏览器完全无法区分 —— 若写成「私网/回环就免密」,公网入口的认证会被静默关掉。

正确做法(已实现):

  1. 云上的 dl-tls-proxy.js 转发时强制覆盖写入 X-DL-Relay: 1;
  2. 下载中心见到该头 → 一律要认证(不看来源 IP);没有该头且来源在免密网段内 → 免密;
  3. 因为代理是覆盖写入,外部伪造该头只会让自己被要求输密码,无法绕过认证(已实测:内网伪造该头 + 无凭据 → 401)。

免密网段可用环境变量调整(默认 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.serviceSSH 反向隧道单元
本机~/.ssh/id_ed25519_dltunnel{,.pub}隧道专用密钥
本机~/Downloads/.dl-hub-authBasic 认证凭据(600)
本机~/Downloads/dl-remote-ctl.sh链路控制脚本
本机/etc/systemd/system/dl-server.service.bak-20260930-preauth加认证前的单元备份
云端/root/dl-tls-proxy.jsTLS 终止代理(零依赖 Node)
云端/root/dl-tls/{cert,key}.pem自签证书(key 600)
云端/etc/systemd/system/dl-tls-proxy.service代理单元
云端/root/.ssh/authorized_keys含受限隧道公钥(原为空文件)
本机~/Downloads/tailscale-ctl.shTailscale 控制脚本

回滚(彻底撤掉公网入口)

# 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

六、踩坑记录(都是真踩过的,别再走一遍)

6.1 Tailscale Funnel 在国内不可用(重要)

https://dsh-76.tailcbc4d1.ts.net/ 看起来完美(真证书、无需装客户端),实测约一半请求失败。逐 IP 排查结论:

Funnel 入口 IP结果
208.111.34.115/5 成功
208.111.35.2090/5 成功

失败报文是 GnuTLS, handshake failed: TLS 链接非正常地终止了 —— TLS 握手被异常终止,GFW 按 IP 重置。DNS 在这两个 IP 间轮询,所以表现是"刷一下能开、再刷就超时"。手机浏览器无法固定 IP,故这条路放弃。

如果哪天要救 Funnel:只能在客户端侧把域名解析钉到可用 IP,浏览器做不到;不要浪费时间反复开关 Funnel(Let's Encrypt 有速率限制)。

6.2 家宽直连被「双层 NAT」堵死

排查过程与结论:

附带发现:家宽没有可用的 IPv6(本机唯一全局 v6 地址 fd7a:115c:a1e0:: 是 Tailscale 自建的 ULA),所以走 IPv6 直连这条捷径也不成立。

6.3 /healthz 必须对认证豁免

加 Basic 认证后如果 /healthz 也需要凭据,dl-healthcheck.timer 每 30s 的无凭据探活就会失败 → 连败 2 次自动重启服务 → 自愈机制变成每 60 秒自毁一次。已在 download-server.js 里显式放行 /healthz,并实跑 dl-healthcheck.sh 验证退出码 0。

6.4 Deepin 25 的 /usr 是只读 overlay

装 Tailscale 时 cp 到 /usr/sbin/tailscaled 报 只读文件系统:Deepin 25 把 /usr 挂成只读 overlay(系统保护 + OSTree)。 /usr/local/bin 是可写的(它实际落在 /var 下的一个 bind 挂载),所以二进制装那里、systemd 单元里的 ExecStart 路径也相应改写。 另外官方 apt 源里没有 tailscale 包(Deepin 不是 Ubuntu),用官方静态二进制 tarball 最稳。

6.5 别用 pkill -f "http.server 8899" 清理云端探针

pkill -f 会匹配到这条 ssh 命令自身的命令行(里面就含该字符串),结果把自己的 SSH 会话杀掉、清理动作半途而废。 正确写法(正则打断自匹配):pkill -f "http0serve[r] 8899"。同样的坑在 llmste[r] 那次已经踩过一回。

6.6 dl-hub 加了认证后的连带影响

6.7 Tailscale 登录参数固定 --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(但运营商可能仍封入站,有白折腾风险)
关掉 Funnelbash ~/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 / 密码方式)
下载此文件