接入指南
服务器上的 dsh web,怎么在自己电脑和手机上用起来
dsh web 默认只监听 127.0.0.1 —— 这是有意的:它没有内建的用户体系,谁连上谁就是你的会话。所以接入方式的选择,本质是「你打算把这扇门开多大」。下面三种方案按暴露面从小到大排列,都假定服务器有独立公网 IP。
- ① SSH 隧道 · 零暴露
- ② nginx + IP · 无域名
- ③ 域名 + TLS · 可长期用
前置条件
三种方案共用的四件事,先确认它们,能省掉后面一大半排错时间。
服务器有独立公网 IP
方案 ②③ 必需;方案 ① 不需要
- 云服务器控制台的「弹性公网 IP / 固定 IP」都算;只有内网 IP 或位于 NAT 后面不算
- 自查命令:
curl -s ifconfig.me的结果要和你在控制台看到的一致
能用 SSH 登录
三种方案共用
- 方案 ① 是在 SSH 之上做转发,所以这条路必须先通
- 方案 ②③ 也要靠它装 nginx、签发证书
dsh web 以 systemd 常驻
监听 127.0.0.1:3081
- 下文假定单元名是
dsh-web;能重启、能看日志就行 - dsh web 每次启动会打印一条带 token 的访问链接,务必留着
认得清「谁是 Host」
最容易踩的一条
- dsh web 只接受
--trusted-host白名单里的主机名,否则/api/*一律 403 - 用 IP 访问就要加 IP,用域名访问就要加域名
怎么确认服务活着:
systemctl is-active dsh-web
journalctl -u dsh-web -n 50 | grep "dsh web:" # 这里会打印带 token 的链接
ss -ltnp | grep 3081 # 应该能看到 127.0.0.1:3081
curl -sI http://127.0.0.1:3081 | head -1 # 401 属正常:没带 token 就进不去
① SSH 隧道:不对外开放任何端口
把服务器的 127.0.0.1:3081 通过 SSH 映射到你本机,流量全程加密,服务器不需要开任何额外端口 —— 推荐先从这个开始。
在你的电脑上开一条隧道
适用于 笔记本 / 台式机ssh -N -L 3081:127.0.0.1:3081 root@SERVER_IP
-N:只做转发,不在服务器上开交互 shell-L 3081:127.0.0.1:3081:本机 3081 →(经 SSH)→ 服务器的 127.0.0.1:3081- 本机 3081 被占用时,换成
-L 8080:127.0.0.1:3081即可
在本机浏览器打开并认证
无需改配置-
先拿到带 token 的完整链接
在服务器上执行下面的命令;dsh web 打印的那行里
token=后面就是启动令牌。journalctl -u dsh-web -n 100 | grep -o "token=[A-Za-z0-9_-]*" | tail -1 -
把链接的域名换成 127.0.0.1,然后打开
第一次带 token 打开会种下一天的会话 cookie,之后直接访问
http://127.0.0.1:3081/?token=把上一步的令牌粘在这里http://127.0.0.1:3081就行。
这条路上不需要动 --trusted-host:请求的 Host 是 127.0.0.1:3081,本来就在白名单里。这也是它最省事的原因。
让它更好用:别名与自动重连
可选# 本机 ~/.ssh/config
Host dsh
HostName SERVER_IP
User root
LocalForward 3081 127.0.0.1:3081
ServerAliveInterval 30
ServerAliveCountMax 3
- 之后只需要
ssh -N dsh,端口转发写在配置里 ServerAliveInterval让空闲隧道定期发心跳,避免被中间设备掐断- 断线要自动重连就装 autossh:
autossh -M 0 -N dsh
手机也想用隧道?
注意流量归属手机的浏览器自己不会开隧道,得靠手机上的 SSH 客户端来开:
- iOS:Termius(免费版够用)等 SSH 客户端,新建连接后配置本地端口转发
3081 → 127.0.0.1:3081 - 建好之后在手机浏览器访问
http://127.0.0.1:3081—— 注意这个 127.0.0.1 是手机自己 - 有些客户端在切到后台时会断开隧道,需要允许它常驻后台
想让同一局域网里的其它设备(比如平板)也走这条隧道,必须让转发监听在 0.0.0.0 上:服务器加 GatewayPorts clientspecified,命令加 -g。这等于把你那台机器的 3081 端口对局域网公开,只建议在可信网络里临时这么做。
② nginx 反向代理:有公网 IP,没有域名
让服务器直接对外提供访问,用 http://IP:端口 打开。配置最少,代价是明文 HTTP,而且认证令牌会出现在 URL 里。
装 nginx 并加一个站点
nginxapt update && apt install -y nginx
map $arg_token $dsh_ok {
default 0;
"PASTE_LAUNCH_TOKEN" 1; # 与 dsh web 打印的 token 完全一致
}
server {
listen 8080;
server_name _;
# 带 token 的首次访问放行,其余要求已有会话 cookie
if ($dsh_ok = 0) {
set $dsh_need_auth 1;
}
location / {
proxy_pass http://127.0.0.1:3081;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade; # WebSocket / SSE 需要
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s; # 长连接不要被 nginx 掐断
}
}
上面这段 if 只是占位示意,真正的访问控制交给 dsh 自己的 token 会话 cookie 就够了 —— nginx 只负责转发。要额外的门禁,用 ③ 里的 basic auth 或 Cloudflare Tunnel 更省心。
启用站点、放行端口
防火墙别忘ln -s /etc/nginx/sites-available/dsh /etc/nginx/sites-enabled/dsh
nginx -t && systemctl reload nginx
ufw allow 8080/tcp # Ubuntu / Debian
firewall-cmd --add-port=8080/tcp --permanent && firewall-cmd --reload # CentOS / RHEL
- 云服务器还要在控制台的「安全组 / 网络 ACL」里放行同一个端口 —— 这一步经常被漏掉,表现为「本机 curl 通、外网连不上」
把公网 IP 加进 --trusted-host
403 的根源用 IP 访问时,浏览器的 Host 是 IP —— dsh web 只对白名单里的 Host 提供 /api/*,所以必须显式加上:
ExecStart=/usr/bin/dsh web --no-open \
--host 127.0.0.1 --port 3081 \
--trusted-host 127.0.0.1 \
--trusted-host localhost \
--trusted-host 203.0.113.10 # ← 你的公网 IP
systemctl daemon-reload && systemctl restart dsh-web
-
拿 token 并访问
http://203.0.113.10:8080/?token=启动令牌
明文 HTTP 的代价说清楚:token 会出现在 URL 里(浏览器历史、中间设备日志都会留痕),同网络的人可以嗅探;而且 http:// 不是安全上下文,剪贴板、部分 PWA 能力会被浏览器限制。临时用可以,长期用请走 ③。
③ nginx + 域名 + TLS:可以长期用的方案
一个免费域名配 Let's Encrypt 证书,浏览器不报警告,手机上也能「添加到主屏幕」当应用用。
把域名解析到服务器 IP
DNS- 没有域名可以用免费二级域名,例如 DuckDNS、afraid.org 的 FreeDNS;也可以直接买一个最便宜的后缀
- 加一条 A 记录:主机名
dsh,值 = 服务器公网 IP。没有其他解析时用@指向根域名 - 生效后先自查:
dig +short dsh.example.com应该返回你的公网 IP
签发证书并把 nginx 切到 443
TLS最省事的是 certbot:它自己申请证书,还会把 nginx 配置改好并装好自动续期。
apt install -y certbot python3-certbot-nginx
certbot --nginx -d dsh.example.com # 自动申请 + 改写配置 + 配置续期
systemctl list-timers | grep certbot # 确认续期定时器已就位
想自己掌控配置,就用 acme.sh 的 webroot 模式签发,再手写一个 443 站点:
server {
listen 80;
server_name dsh.example.com;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
http2 on;
server_name dsh.example.com;
ssl_certificate /etc/letsencrypt/live/dsh.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/dsh.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# 可选:再上一道 basic auth(会与 dsh 的 token 叠加成两次提示)
# auth_basic "DSH"; auth_basic_user_file /etc/nginx/.htpasswd_dsh;
location / {
proxy_pass http://127.0.0.1:3081;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
proxy_buffering off; # 流式输出不要被缓冲
}
}
ufw allow 80/tcp && ufw allow 443/tcp
nginx -t && systemctl reload nginx
把 --trusted-host 换成域名
不加就 403这一步和方案 ② 是同一个道理,只是白名单里换成域名:
ExecStart=/usr/bin/dsh web --no-open \
--host 127.0.0.1 --port 3081 \
--trusted-host 127.0.0.1 --trusted-host localhost \
--trusted-host dsh.example.com # ← 你的域名
https://dsh.example.com/?token=启动令牌
到这一步,电脑和手机用的是同一个地址,证书受信任,token 也不再出现在长期使用的 URL 里(会话 cookie 顶一天)。
不想自己管证书和防火墙,也可以用 Cloudflare Tunnel:把 cloudflared 装在服务器上,出一条隧道指向 127.0.0.1:3081,公网侧不用开任何端口,TLS 由 Cloudflare 终止。代价是多一个外部依赖,并且要记得同样把域名加进 --trusted-host。
手机端:三种方案各自的体验
手机浏览器没有「终端」,所以方案 ① 在手机上要用 SSH 客户端;②③ 则只要有浏览器就能用。
加到主屏幕当应用
仅 ②③
- iOS Safari:分享 → 添加到主屏幕;Android Chrome:菜单 → 添加到主屏幕 / 安装应用
- 独立窗口没有地址栏,视觉上更接近原生应用;但注意 iOS 的软键盘行为与浏览器标签页略有差别
一次认证,长期可用
会话 cookie 有效期一天
- 每次带 token 打开一次,就重新获得一天的会话
- 把带 token 的链接存在自己的密码管理器里,比留在浏览器历史里安全
手机上更顺手的配合
与本项目的插件互补
- 装上 dsh-plugin-mobile-ux 插件后,窄屏布局、软键盘跟随、侧边栏单击切换会话都已处理
- 用 https 访问时浏览器才允许把内容写进剪贴板,这也是方案 ③ 更实用的原因之一
怎么选
按「谁需要访问」和「能不能接受开端口」两个问题决定。
| 方案 | 适用 | 服务器开端口 | 加密 | 主要代价 |
|---|---|---|---|---|
| ① SSH 隧道 | 只有你自己、一两台设备 | 不需要(只用 22) | SSH 全程加密 | 每次要先连隧道;手机端要装 SSH 客户端 |
| ② nginx + IP | 临时给别人看、没有域名 | 8080 | 无(明文 HTTP) | token 走明文;非安全上下文功能受限 |
| ③ 域名 + TLS | 长期自用、多设备、要装到主屏幕 | 80 / 443 | TLS,证书受信任 | 需要域名;证书与续期要维护(certbot 可自动化) |
只给自己用,就选 ①;想省心又要长期用,就选 ③。② 更像是「先看看它长什么样」的临时手段。
排错:症状 → 原因
这几条覆盖了 90% 的「连不上」。
| 症状 | 多半是 |
|---|---|
| 页面能开,但界面一直报错 / 空白 | Host 不在 --trusted-host 白名单,/api/* 返回 403 |
| “dsh web authentication required” | 没带 token 打开,或 cookie 过期(一天) |
| 本机 curl 通,外部连不上 | 云控制台安全组 / 网络 ACL 没放行,或系统防火墙没开 |
| SSH 隧道连上了,浏览器打不开 | 本地端口被占用;或转发写成了服务器的公网 IP(应为 127.0.0.1) |
| 能用,但流式输出卡住不动 | nginx 缓冲或超时:加 proxy_buffering off 与更长的 proxy_read_timeout |
| 改了插件代码,浏览器没变化 | bundle 版本号由文件 mtime/size 算出且缓存 immutable —— 重启 dsh-web 并用带 rev 的新 URL |