dsh-plugin-mobile-ux

接入指南

服务器上的 dsh web,怎么在自己电脑和手机上用起来

dsh web 默认只监听 127.0.0.1 —— 这是有意的:它没有内建的用户体系,谁连上谁就是你的会话。所以接入方式的选择,本质是「你打算把这扇门开多大」。下面三种方案按暴露面从小到大排列,都假定服务器有独立公网 IP。

前置条件

三种方案共用的四件事,先确认它们,能省掉后面一大半排错时间。

服务器有独立公网 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,用域名访问就要加域名

怎么确认服务活着:

server 探测
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 映射到你本机,流量全程加密,服务器不需要开任何额外端口 —— 推荐先从这个开始。

1

在你的电脑上开一条隧道

适用于 笔记本 / 台式机
本机终端 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 即可
2

在本机浏览器打开并认证

无需改配置
  1. 先拿到带 token 的完整链接 在服务器上执行下面的命令;dsh web 打印的那行里 token= 后面就是启动令牌。
    server journalctl
    journalctl -u dsh-web -n 100 | grep -o "token=[A-Za-z0-9_-]*" | tail -1
  2. 把链接的域名换成 127.0.0.1,然后打开
    本机浏览器 url
    http://127.0.0.1:3081/?token=把上一步的令牌粘在这里
    第一次带 token 打开会种下一天的会话 cookie,之后直接访问 http://127.0.0.1:3081 就行。

这条路上不需要动 --trusted-host:请求的 Host 是 127.0.0.1:3081,本来就在白名单里。这也是它最省事的原因。

3

让它更好用:别名与自动重连

可选
~/.ssh/config ssh
# 本机 ~/.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
4

手机也想用隧道?

注意流量归属

手机的浏览器自己不会开隧道,得靠手机上的 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 里。

1

装 nginx 并加一个站点

nginx
server apt
apt update && apt install -y nginx
/etc/nginx/sites-available/dsh 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 更省心。

2

启用站点、放行端口

防火墙别忘
server nginx
ln -s /etc/nginx/sites-available/dsh /etc/nginx/sites-enabled/dsh
nginx -t && systemctl reload nginx
server 防火墙
ufw allow 8080/tcp                       # Ubuntu / Debian
firewall-cmd --add-port=8080/tcp --permanent && firewall-cmd --reload   # CentOS / RHEL
  • 云服务器还要在控制台的「安全组 / 网络 ACL」里放行同一个端口 —— 这一步经常被漏掉,表现为「本机 curl 通、外网连不上」
3

把公网 IP 加进 --trusted-host

403 的根源

用 IP 访问时,浏览器的 Host 是 IP —— dsh web 只对白名单里的 Host 提供 /api/*,所以必须显式加上:

/etc/systemd/system/dsh-web.service systemd
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
server systemctl
systemctl daemon-reload && systemctl restart dsh-web
  1. 拿 token 并访问
    你的电脑或手机浏览器 url
    http://203.0.113.10:8080/?token=启动令牌

明文 HTTP 的代价说清楚:token 会出现在 URL 里(浏览器历史、中间设备日志都会留痕),同网络的人可以嗅探;而且 http:// 不是安全上下文,剪贴板、部分 PWA 能力会被浏览器限制。临时用可以,长期用请走 ③。

③ nginx + 域名 + TLS:可以长期用的方案

一个免费域名配 Let's Encrypt 证书,浏览器不报警告,手机上也能「添加到主屏幕」当应用用。

1

把域名解析到服务器 IP

DNS
  • 没有域名可以用免费二级域名,例如 DuckDNS、afraid.org 的 FreeDNS;也可以直接买一个最便宜的后缀
  • 加一条 A 记录:主机名 dsh,值 = 服务器公网 IP。没有其他解析时用 @ 指向根域名
  • 生效后先自查:dig +short dsh.example.com 应该返回你的公网 IP
2

签发证书并把 nginx 切到 443

TLS

最省事的是 certbot:它自己申请证书,还会把 nginx 配置改好并装好自动续期。

server certbot
apt install -y certbot python3-certbot-nginx
certbot --nginx -d dsh.example.com        # 自动申请 + 改写配置 + 配置续期
systemctl list-timers | grep certbot      # 确认续期定时器已就位

想自己掌控配置,就用 acme.sh 的 webroot 模式签发,再手写一个 443 站点:

/etc/nginx/sites-available/dsh nginx
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;            # 流式输出不要被缓冲
    }
}
server 防火墙
ufw allow 80/tcp && ufw allow 443/tcp
nginx -t && systemctl reload nginx
3

把 --trusted-host 换成域名

不加就 403

这一步和方案 ② 是同一个道理,只是白名单里换成域名:

/etc/systemd/system/dsh-web.service systemd
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     # ← 你的域名
任意设备浏览器 url
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