dsh-mobile 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -4
- package/README.en.md +59 -25
- package/README.md +57 -25
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/bin/dsh-mobile-funnel-linux-arm64 +0 -0
- package/bin/dsh-mobile-funnel-linux-x64 +0 -0
- package/docs/CLOUDFLARE_TUNNEL.en.md +113 -0
- package/docs/CLOUDFLARE_TUNNEL.md +113 -0
- package/docs/README.md +25 -0
- package/docs/SELF_HOSTED_FRP.en.md +2 -0
- package/docs/SELF_HOSTED_ORIGIN.en.md +34 -0
- package/docs/SELF_HOSTED_ORIGIN.md +85 -0
- package/lib/cli.js.map +1 -1
- package/lib/client.js +1124 -101
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +400 -3
- package/lib/index.mjs +2129 -237
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-compat.js +2 -0
- package/lib/mobile-compat.js.map +1 -0
- package/package.json +17 -7
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Cloudflare named tunnel (a stable public hostname)
|
|
2
|
+
|
|
3
|
+
The built-in cloudflared provider runs in one of two modes, chosen in the panel under **Mobile access → Remote → cloudflared → Tunnel type**:
|
|
4
|
+
|
|
5
|
+
| | Quick tunnel (default) | Named tunnel |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Account | Not needed | Cloudflare account required |
|
|
8
|
+
| Address | Random `*.trycloudflare.com` on every start | A fixed hostname under your own domain |
|
|
9
|
+
| After a restart | Address changes; pair again | Address is unchanged; paired devices keep working |
|
|
10
|
+
| Availability | Officially for testing: rate-limited, no uptime guarantee | Carried by your own Cloudflare account |
|
|
11
|
+
| Settings | None | Connector token, public hostname, local forward port |
|
|
12
|
+
|
|
13
|
+
Cloudflare terminates DNS and TLS for the public hostname. The plugin only runs `cloudflared` locally and hands traffic to its authenticated private gateway, so **the phone still pairs through the app's Remote access flow** — a tunnel does not change how devices pair.
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
1. A domain already on Cloudflare. Its nameservers must point at the pair Cloudflare assigned, changed at your registrar; that usually takes minutes and up to 24 hours.
|
|
18
|
+
2. Cloudflare Zero Trust (a team domain) enabled, because the tunnel console lives inside it.
|
|
19
|
+
3. The cloudflared component installed in the panel. Named and quick tunnels share the same official client.
|
|
20
|
+
|
|
21
|
+
## Create the tunnel in the Cloudflare dashboard
|
|
22
|
+
|
|
23
|
+
1. Open **Zero Trust → Networks → Tunnels** and choose **Create a tunnel** → **Cloudflared**.
|
|
24
|
+
2. Name it, for example `dsh-mobile`, and save; the dashboard then shows a connector install command.
|
|
25
|
+
3. On the **Public Hostname** tab add one entry:
|
|
26
|
+
- Subdomain `dsh`, and pick your domain, giving `dsh.example.com`
|
|
27
|
+
- Service: **HTTP** → `127.0.0.1:3444`
|
|
28
|
+
4. On the **Overview** tab copy the connector token — the long string starting with `eyJ`. It already contains the account, tunnel id and tunnel secret, so **it is a credential**.
|
|
29
|
+
|
|
30
|
+
> `127.0.0.1:3444` is the panel's "Local forward port". Cloudflare routes the public hostname to exactly that port, so it must match what you enter in the panel and must not change afterwards.
|
|
31
|
+
|
|
32
|
+
## Fill in the DSH Mobile panel
|
|
33
|
+
|
|
34
|
+
1. **Mobile access → Remote → cloudflared**.
|
|
35
|
+
2. Set the tunnel type to **Named**.
|
|
36
|
+
3. Enter:
|
|
37
|
+
- **Public hostname**: `dsh.example.com`
|
|
38
|
+
- **Local forward port**: `3444`, matching the Service port from step 3
|
|
39
|
+
- **Connector token**: the token you copied
|
|
40
|
+
4. Choose **Save and connect**.
|
|
41
|
+
|
|
42
|
+
Once saved, leaving the token field blank keeps the stored token, and the field never echoes a saved token back. **Remove the saved token** also **stops the cloudflared channel** and returns the provider to a quick tunnel. Nothing reconnects on its own: switch the channel back on afterwards.
|
|
43
|
+
|
|
44
|
+
## Connect the phone to a named tunnel
|
|
45
|
+
|
|
46
|
+
Moving to a fixed hostname means **an already paired phone must pair again**: a DSH Mobile device credential is only ever sent to the exact origin that first received it, which is the design that stops a swapped-in domain from harvesting it. A credential issued for the old address therefore never authenticates at the new one.
|
|
47
|
+
|
|
48
|
+
1. On the computer, open **Remote** in the panel and choose **Create remote pairing QR code**. That is what opens the pairing window, which is time-limited; while it is closed nothing can pair.
|
|
49
|
+
2. In the app, **open the Remote entry first** (the remote access setup page in the connection center), then scan the code.
|
|
50
|
+
|
|
51
|
+
> **A named tunnel must be scanned from inside the Remote flow.** The app treats platform suffixes such as `.ts.net`, cpolar and `.trycloudflare.com` as remote without asking. A domain you own is recognised as a remote candidate too, but the Local network flow accepts only non-candidate addresses, so scanning there is rejected as **Invalid QR code** and looks exactly like "cannot connect". Enter the Remote flow first, then scan.
|
|
52
|
+
>
|
|
53
|
+
> Scanning is optional: the full link (`https://your-domain/mobile-access/pair#instance=…&token=…`) can be copied to the phone and pasted, because the app accepts a complete link in its input field.
|
|
54
|
+
|
|
55
|
+
The old remote entry in the device list will show **Address may have changed** or unreachable; delete it once the new pairing succeeds.
|
|
56
|
+
|
|
57
|
+
## Security boundary
|
|
58
|
+
|
|
59
|
+
- The token is written only into the DSH Mobile private directory (`~/.dsh/mobile-access/remote/cloudflared/tunnel.json`, mode 0600) and is passed to `cloudflared` **only** through the `TUNNEL_TOKEN` environment variable, never on the command line, which any local process can read.
|
|
60
|
+
- The token is never returned to a browser or a phone. Panel status carries only whether it is configured, plus the hostname and port.
|
|
61
|
+
- Behind the tunnel sits DSH Mobile's own authenticated gateway: the public hostname exposes that gateway, and DSH still requires paired-device credentials.
|
|
62
|
+
- The plugin adds no system service, startup item, registry entry or PATH entry. Disabling the channel ends the process.
|
|
63
|
+
|
|
64
|
+
## Error codes
|
|
65
|
+
|
|
66
|
+
| Panel message | Code | Meaning and remedy |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| Local forward port unavailable | `cloudflared_tunnel_port_unavailable` | The configured port is taken. A named tunnel cannot move to another port, because Cloudflare routes to that exact one: free the port, or pick another and update the Service in Cloudflare to match. |
|
|
69
|
+
| Invalid public hostname | `cloudflared_tunnel_hostname_invalid` | Must be a real hostname under a domain on this account. IP literals, wildcards, `.trycloudflare.com` and `.cfargotunnel.com` are refused. |
|
|
70
|
+
| Invalid forward port | `cloudflared_tunnel_port_invalid` | The port must be between 1024 and 65535. |
|
|
71
|
+
| Reserved forward port | `cloudflared_tunnel_port_reserved` | **3443** cannot be used: it is the DSH Mobile LAN gateway's HTTPS port, held for as long as DSH runs. It is not a transient conflict, so pick 3444 or 3445. |
|
|
72
|
+
| Invalid token | `cloudflared_tunnel_token_invalid` | Copy the whole token from the dashboard, with no spaces or newlines. |
|
|
73
|
+
| Settings rejected | `cloudflared_tunnel_settings_invalid` | The request carried a field that does not belong to the mode, such as a port in quick mode. |
|
|
74
|
+
| Token, hostname and port are all required | `cloudflared_tunnel_config_missing` | First-time named-tunnel setup needs all three. |
|
|
75
|
+
| Saved configuration is unreadable | `cloudflared_tunnel_config_invalid` | The stored file is corrupt or malformed. The provider falls back to a quick tunnel; save the settings again. |
|
|
76
|
+
| Saved configuration is not a regular file | `cloudflared_tunnel_target_invalid` | `tunnel.json` became a symlink or a non-regular file. Remove it and save the settings again. |
|
|
77
|
+
| Could not reserve a local port | `cloudflared_port_reservation_failed` | Reserving the loopback port failed for a reason other than the port being busy. Retry, and check system resources if it persists. |
|
|
78
|
+
| Component download failed verification | `cloudflared_download_hash_mismatch` / `cloudflared_download_size_mismatch` | The downloaded binary does not match the pinned size or SHA-256. Install again; repeated failures mean something is rewriting the transfer. |
|
|
79
|
+
| Installed component failed verification | `cloudflared_executable_hash_mismatch` | The local cloudflared no longer matches the verified build. Remove it completely and install again. |
|
|
80
|
+
| This build cannot run the component | `cloudflared_component_unsupported` | The platform is outside the supported set (currently Windows x64 and Linux x64/arm64). |
|
|
81
|
+
| Timed out waiting for the tunnel | `cloudflared_start_timeout` | The connector did not print `Registered tunnel connection` within the startup budget. In named mode a live connector keeps waiting (up to about 5 minutes); usually it cannot reach a Cloudflare edge. |
|
|
82
|
+
| Component not installed | `cloudflared_component_missing` | The official component is absent. Complete the preparation steps above. |
|
|
83
|
+
| Component verification failed | `cloudflared_component_invalid` | The local component does not match the verified build. Remove it completely and install again. |
|
|
84
|
+
| Could not allocate a port | `cloudflared_port_unavailable` | Quick mode could not allocate the loopback gateway port. Retry. |
|
|
85
|
+
| Client failed to launch | `cloudflared_launch_failed` | The cloudflared process did not start. Check the local logs. |
|
|
86
|
+
| Connection stopped or exited | `cloudflared_stopped` / `cloudflared_exited` | The channel dropped. Reconnect. |
|
|
87
|
+
| Unrecognized output | `cloudflared_invalid_output` / `cloudflared_invalid_origin` | cloudflared output, or the public address it printed, failed validation. Reconnect and copy the diagnostic report. |
|
|
88
|
+
| Gateway start failed | `gateway_start_failed` | The authentication gateway behind the tunnel did not start; this is not a port conflict. Check the local logs. |
|
|
89
|
+
| Download redirect problem | `cloudflared_download_redirect_missing` / `_invalid` / `_rejected` | The official download page did not redirect to a release asset, or the redirect did not point at a GitHub release asset. Retry later, or install from the official page manually. |
|
|
90
|
+
|
|
91
|
+
## Troubleshooting
|
|
92
|
+
|
|
93
|
+
- **Stuck on "connecting"**: a named tunnel prints no banner, so it only becomes ready once the connector registers. Read the cloudflared output in the DSH log; the `CONNECTIVITY PRE-CHECKS` table says whether DNS, UDP/QUIC or TCP is the problem.
|
|
94
|
+
- **Public access returns 1033**: Cloudflare believes no connector is healthy for that hostname. Check the tunnel shows Healthy in Zero Trust, and that its ingress port matches the panel.
|
|
95
|
+
- **`cloudflared` reports `Unauthorized`, or the tunnel id does not exist**: the token belongs to a different tunnel, for example one that was deleted and recreated. Copy the token again.
|
|
96
|
+
- **With a TUN or transparent proxy running (Clash, Mihomo, Clash Verge), the phone sticks on "Loading plugins" and retries forever.** This is a real failure that was measured, and it is easy to misread as a pairing or plugin problem:
|
|
97
|
+
- `cloudflared`'s connection to `region*.v2.argotunnel.com` has no dedicated rule, so it falls through to the catch-all `Match`/`MATCH` rule and is sent into a proxy node. The core's own API shows it plainly:
|
|
98
|
+
```
|
|
99
|
+
cloudflared.exe -> region2.v2.argotunnel.com
|
|
100
|
+
rule=Match chains=["<some airport node>","漏网之鱼"] up/down = 103 MB / 2.1 MB
|
|
101
|
+
```
|
|
102
|
+
- Downstream throughput then collapses to **8–100 KB/s**. The phone's DSH boot pulls a **4.5 MB** (compressed) boot bundle in one shot, the loader gives up, and the app shows "load, fail, retry".
|
|
103
|
+
- The same file took **0.2 s** on the LAN gateway, and **9.9 s / 454 KB/s** through the tunnel once routing was fixed — two orders of magnitude apart.
|
|
104
|
+
- Fix: route cloudflared and its edge domains directly. In Clash Verge's **global merge profile** (`profiles/Merge.yaml`, which subscription updates do not overwrite):
|
|
105
|
+
```yaml
|
|
106
|
+
prepend-rules:
|
|
107
|
+
- PROCESS-NAME,cloudflared.exe,DIRECT
|
|
108
|
+
- DOMAIN-SUFFIX,argotunnel.com,DIRECT
|
|
109
|
+
- DOMAIN-SUFFIX,trycloudflare.com,DIRECT
|
|
110
|
+
```
|
|
111
|
+
Then make cloudflared **reconnect**: hot-reloading the core does not migrate long-lived connections (use the panel's reconnect action, or toggle the provider off and on).
|
|
112
|
+
- To confirm it is this and not the phone: fetch the boot bundle from the computer with the same session and watch the rate. If the computer is just as slow, the phone and pairing are innocent.
|
|
113
|
+
- **The domain still resolves to the old address**: after the nameserver change Cloudflare must move the zone from Pending to Active, and records in a pending zone are not served publicly.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Cloudflare 命名隧道(固定公网域名)
|
|
2
|
+
|
|
3
|
+
内置的 cloudflared 通道有两种模式,在面板 **移动访问 → 远程 → cloudflared → 隧道类型** 中切换:
|
|
4
|
+
|
|
5
|
+
| | 快速隧道(默认) | 命名隧道 |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| 账号 | 不需要 | 需要 Cloudflare 账号 |
|
|
8
|
+
| 地址 | 每次启动随机分配 `*.trycloudflare.com` | 你自己域名下的固定主机名 |
|
|
9
|
+
| 重启后 | 地址变化,需要重新扫码 | 地址不变,已配对设备继续可用 |
|
|
10
|
+
| 可用性 | 官方定位为测试用途:有限流、无可用性保证 | 由你的 Cloudflare 账号承载 |
|
|
11
|
+
| 配置项 | 无 | 连接器令牌、公网域名、本机转发端口 |
|
|
12
|
+
|
|
13
|
+
命名隧道的公网域名由 Cloudflare 负责解析与 TLS,插件只负责在本机运行 `cloudflared` 并把流量交给已认证的私有网关;**手机端仍然要走 App 的远程访问扫码流程**,隧道不改变配对方式。
|
|
14
|
+
|
|
15
|
+
## 前置条件
|
|
16
|
+
|
|
17
|
+
1. 一个已接入 Cloudflare 的域名(该域名的 NS 必须指向 Cloudflare 分配的名称服务器,在域名注册商处修改;改完通常几分钟到 24 小时生效)。
|
|
18
|
+
2. Cloudflare Zero Trust(团队域名)已启用——隧道控制台位于其中。
|
|
19
|
+
3. 面板中 cloudflared 组件已安装(命名隧道和快速隧道使用同一个官方客户端)。
|
|
20
|
+
|
|
21
|
+
## 在 Cloudflare 控制台创建隧道
|
|
22
|
+
|
|
23
|
+
1. 打开 **Zero Trust → Networks → Tunnels**,选择 **Create a tunnel**,类型选 **Cloudflared**。
|
|
24
|
+
2. 命名(例如 `dsh-mobile`),保存后会显示 connector 安装命令。
|
|
25
|
+
3. 在 **Public Hostname** 页签添加一条:
|
|
26
|
+
- Subdomain:`dsh`,Domain:选你的域名(得到 `dsh.example.com`)
|
|
27
|
+
- Service:**HTTP** → `127.0.0.1:3444`
|
|
28
|
+
4. 回到 **Overview** 页签复制连接器令牌(一长串以 `eyJ` 开头的字符串)。令牌里已经包含账号、隧道 ID 和隧道密钥,**等同于密码**。
|
|
29
|
+
|
|
30
|
+
> `127.0.0.1:3444` 就是面板里的「本机转发端口」。Cloudflare 把公网主机名固定转发到这个端口,所以它必须与面板中填写的端口一致,并且长期不变。
|
|
31
|
+
|
|
32
|
+
## 在 DSH Mobile 面板中填写
|
|
33
|
+
|
|
34
|
+
1. **移动访问 → 远程 → cloudflared**。
|
|
35
|
+
2. 隧道类型选 **命名隧道**。
|
|
36
|
+
3. 依次填写:
|
|
37
|
+
- **公网域名**:`dsh.example.com`
|
|
38
|
+
- **本机转发端口**:`3444`(与第 3 步的 Service 端口一致)
|
|
39
|
+
- **连接器令牌**:粘贴上一步复制的令牌
|
|
40
|
+
4. 点击 **保存并连接**。
|
|
41
|
+
|
|
42
|
+
已保存后令牌输入框留空表示不更改;输入框里不会再回显已保存的令牌。「移除已保存的令牌」会**同时停止 cloudflared 通道**并把配置改回快速隧道。不会自动重连,之后需要手动重新打开开关。
|
|
43
|
+
|
|
44
|
+
## 用手机连上命名隧道
|
|
45
|
+
|
|
46
|
+
切到固定域名后,**已经配对过的手机必须重新配对**:DSH Mobile 的设备凭据只发给它当初保存的那个精确 Origin(这是防止换域名骗走凭据的安全设计),所以旧地址上的凭据在新域名上一律无效。
|
|
47
|
+
|
|
48
|
+
1. 电脑面板 → **远程** → 点 **生成远程配对二维码**。这一步才会打开配对窗口,窗口有时限;没打开时任何设备都进不来。
|
|
49
|
+
2. 手机 App → **先进入「远程」入口**(连接中心的远程访问设置页)→ 再扫描二维码。
|
|
50
|
+
|
|
51
|
+
> **命名隧道必须在「远程」入口里扫码。** App 只把 `.ts.net`、cpolar、`.trycloudflare.com` 这类平台后缀当作「无需询问即为远程」;你自己的域名同样会被识别为可远程的候选地址,但「局域网」流程只接受非候选地址,所以在局域网界面扫码会被判为无效并提示**无效二维码 / Invalid QR code**,看起来就像「连不上」。先进入「远程」流程再扫即可。
|
|
52
|
+
>
|
|
53
|
+
> 不想扫码时,完整链接(`https://你的域名/mobile-access/pair#instance=…&token=…`)可以整条复制到手机上粘贴,App 的输入框接受完整链接。
|
|
54
|
+
|
|
55
|
+
设备列表里原来那条远程记录会显示「地址可能已变化 / Address may have changed」或不可达,重新配对成功后删掉即可。
|
|
56
|
+
|
|
57
|
+
## 安全边界
|
|
58
|
+
|
|
59
|
+
- 令牌只写入 DSH Mobile 私有目录(`~/.dsh/mobile-access/remote/cloudflared/tunnel.json`,权限 0600),并且**只**通过子进程环境变量 `TUNNEL_TOKEN` 传给 `cloudflared`,不出现在命令行里(本机任何进程都能读到命令行)。
|
|
60
|
+
- 令牌从不回传给浏览器或手机端;面板状态里只有「是否已配置」、域名和端口。
|
|
61
|
+
- 隧道背后是 DSH Mobile 自己的认证网关:公网主机名只暴露该网关,DSH 本体仍然需要配对设备凭据。
|
|
62
|
+
- 插件不会创建系统服务、开机启动项、注册表项或 PATH 项;关闭通道即结束进程。
|
|
63
|
+
|
|
64
|
+
## 错误码
|
|
65
|
+
|
|
66
|
+
| 面板提示 | 实际错误码 | 含义与处理 |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| 本机转发端口不可用 | `cloudflared_tunnel_port_unavailable` | 配置的端口已被占用。命名隧道不能改用其他端口(Cloudflare 固定转发到该端口),请释放端口或换一个端口并同步修改 Cloudflare 的 Service。 |
|
|
69
|
+
| 公网域名无效 | `cloudflared_tunnel_hostname_invalid` | 必须是本账号下域名的真实主机名;不接受 IP、通配符、`.trycloudflare.com` 与 `.cfargotunnel.com`。 |
|
|
70
|
+
| 转发端口无效 | `cloudflared_tunnel_port_invalid` | 端口需在 1024–65535 之间。 |
|
|
71
|
+
| 转发端口被保留 | `cloudflared_tunnel_port_reserved` | 不能填 **3443**:那是 DSH Mobile 局域网网关的 HTTPS 端口,只要 DSH 在运行就一直被占用,不是"临时被别的程序占了"。请换 3444 或 3445。 |
|
|
72
|
+
| 令牌无效 | `cloudflared_tunnel_token_invalid` | 令牌需从控制台完整复制,不能有空格或换行。 |
|
|
73
|
+
| 设置未通过校验 | `cloudflared_tunnel_settings_invalid` | 请求里带了不该有的字段(例如快速隧道模式下携带端口)。 |
|
|
74
|
+
| 需要同时填写令牌、域名和端口 | `cloudflared_tunnel_config_missing` | 首次配置命名隧道时三项都必填。 |
|
|
75
|
+
| 已保存配置无法读取 | `cloudflared_tunnel_config_invalid` | 配置文件损坏或不符合格式;插件会退回快速隧道,重新保存即可。 |
|
|
76
|
+
| 已保存配置不是普通文件 | `cloudflared_tunnel_target_invalid` | `tunnel.json` 变成了符号链接或非常规文件。移除它并重新保存设置。 |
|
|
77
|
+
| 无法预留本机端口 | `cloudflared_port_reservation_failed` | 本机端口预留本身失败(不是端口被占)。重试;若持续出现请检查系统资源。 |
|
|
78
|
+
| 组件下载校验失败 | `cloudflared_download_hash_mismatch` / `cloudflared_download_size_mismatch` | 下载到的二进制与固定版本的大小或 SHA-256 不符。重新安装;若反复失败说明中间链路在改包。 |
|
|
79
|
+
| 已安装组件校验失败 | `cloudflared_executable_hash_mismatch` | 本机那份 cloudflared 与校验过的版本不一致。彻底移除后重新安装。 |
|
|
80
|
+
| 当前构建不支持该组件 | `cloudflared_component_unsupported` | 当前平台不在支持范围内(目前支持 Windows x64 与 Linux x64/arm64)。 |
|
|
81
|
+
| 等待隧道可用超时 | `cloudflared_start_timeout` | connector 在超时预算内没有打印 `Registered tunnel connection`。命名隧道下进程活着会继续等(最多约 5 分钟);常见原因是网络到 Cloudflare 边缘不通。 |
|
|
82
|
+
| 组件未安装 | `cloudflared_component_missing` | 还没安装官方组件。按上面的准备步骤安装。 |
|
|
83
|
+
| 组件校验失败 | `cloudflared_component_invalid` | 本机组件与校验过的版本不符。彻底移除后重新安装。 |
|
|
84
|
+
| 无法分配端口 | `cloudflared_port_unavailable` | 快速隧道模式下无法分配本机网关端口。重试。 |
|
|
85
|
+
| 客户端启动失败 | `cloudflared_launch_failed` | cloudflared 进程没能启动。检查本地日志。 |
|
|
86
|
+
| 连接已停止 / 意外退出 | `cloudflared_stopped` / `cloudflared_exited` | 通道已断开,点重新连接。 |
|
|
87
|
+
| 返回内容无法识别 | `cloudflared_invalid_output` / `cloudflared_invalid_origin` | cloudflared 输出或公网地址未通过校验。重新连接并复制诊断报告。 |
|
|
88
|
+
| 网关启动失败 | `gateway_start_failed` | 隧道背后的认证网关没能启动(不是端口冲突)。检查本地日志。 |
|
|
89
|
+
| 下载跳转异常 | `cloudflared_download_redirect_missing` / `_invalid` / `_rejected` | 官方下载页没有跳到发布资源,或跳转目标不是 GitHub 发布资源。稍后重试,或按官方页面手动安装。 |
|
|
90
|
+
|
|
91
|
+
## 排错
|
|
92
|
+
|
|
93
|
+
- **连接一直停在「正在连接」**:命名隧道没有横幅,只有 connector 真正注册后才算就绪。查看 DSH 日志里 cloudflared 的输出;预检表(`CONNECTIVITY PRE-CHECKS`)会指出是 DNS、UDP/QUIC 还是 TCP 不通。
|
|
94
|
+
- **公网访问返回 1033**:Cloudflare 认为该主机名没有健康的 connector。确认隧道在 Zero Trust 里显示 Healthy,且 ingress 指向的端口与面板一致。
|
|
95
|
+
- **`cloudflared` 报 `Unauthorized` 或隧道 ID 不存在**:令牌与控制台里的隧道不匹配(例如隧道被删除后重建)。重新复制令牌。
|
|
96
|
+
- **本机开着 TUN/透明代理(Clash、Mihomo、Clash Verge 等):手机端会卡在「正在加载插件」并反复重试。** 这是实测到过的一类真实故障,症状很容易被误判成配对或插件问题:
|
|
97
|
+
- `cloudflared` 到 `region*.v2.argotunnel.com` 的连接没有任何专属规则,于是落到兜底的 `Match`/`MATCH` 规则,被送进代理节点。用内核 API 看得很清楚:
|
|
98
|
+
```
|
|
99
|
+
cloudflared.exe -> region2.v2.argotunnel.com
|
|
100
|
+
rule=Match chains=["<某个机场节点>","漏网之鱼"] up/down = 103 MB / 2.1 MB
|
|
101
|
+
```
|
|
102
|
+
- 结果下行被压到 **8–100 KB/s**。而手机端 DSH 启动要一次拉 **4.5 MB**(压缩后)的 boot bundle,加载器等不到就报 `bundle script failed to load`,App 表现为「加载 → 失败 → 重试」。
|
|
103
|
+
- 同一份文件在局域网网关上是 **0.2 秒**,加上直连规则后走隧道是 **9.9 秒 / 454 KB/s** —— 差了两个数量级。
|
|
104
|
+
- 修法:让 cloudflared 与其边缘域名走直连。Clash Verge 的**全局扩展配置**(`profiles/Merge.yaml`,订阅更新不会覆盖):
|
|
105
|
+
```yaml
|
|
106
|
+
prepend-rules:
|
|
107
|
+
- PROCESS-NAME,cloudflared.exe,DIRECT
|
|
108
|
+
- DOMAIN-SUFFIX,argotunnel.com,DIRECT
|
|
109
|
+
- DOMAIN-SUFFIX,trycloudflare.com,DIRECT
|
|
110
|
+
```
|
|
111
|
+
改完要让 cloudflared **重连**才会换路由:内核热重载不会迁移已建立的长连接(面板里点重连,或开关一次提供方)。
|
|
112
|
+
- 判断方法:如果手机端一直卡在加载插件,先在**本机**用同一份会话拉一次 boot bundle 看速率。若本机也很慢,就不是手机或配对的问题。
|
|
113
|
+
- **域名解析还是旧地址**:改完 NS 后 Cloudflare 需要把 zone 从 Pending 变为 Active;A/CNAME 在 zone 激活前不会对外生效。
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# DSH Mobile 文档 / Documentation
|
|
2
|
+
|
|
3
|
+
本目录收录需要展开说明的指南。内置的远程通道(Tailscale Funnel、cpolar、cloudflared)为零配置或按需安装,说明见根目录 [README](../README.md#连接教程),不单独成文;cloudflared 的命名隧道需要在 Cloudflare 控制台建隧道、配公开主机名并取令牌,步骤较多,单独成文。
|
|
4
|
+
|
|
5
|
+
## 面向用户 / User guides
|
|
6
|
+
|
|
7
|
+
| 中文 | English | 内容 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| [Cloudflare 命名隧道](CLOUDFLARE_TUNNEL.md) | [Cloudflare named tunnel](CLOUDFLARE_TUNNEL.en.md) | 用 Cloudflare 账号令牌把 cloudflared 从随机快速隧道换成固定公网域名 |
|
|
10
|
+
| [自建 FRP 使用指南](SELF_HOSTED_FRP.md) | [Self-hosted FRP guide](SELF_HOSTED_FRP.en.md) | 已有 VPS 时用 frps + Caddy 自建远程通道,避开公共隧道带宽限制 |
|
|
11
|
+
| [自有 HTTPS 反向代理](SELF_HOSTED_ORIGIN.md) | [Own HTTPS reverse proxy](SELF_HOSTED_ORIGIN.en.md) | 复用已有的 Lucky / Nginx / Caddy 公网 HTTPS 入口,无需隧道组件 |
|
|
12
|
+
|
|
13
|
+
> 命名隧道与两个自建提供方都要求手机端走 App 的**远程访问**扫码流程;`SELF_HOSTED_ORIGIN.en.md` 是精简版,字段表与排错清单以中文版为准。
|
|
14
|
+
|
|
15
|
+
## 验证记录 / Verification records
|
|
16
|
+
|
|
17
|
+
| 文档 | 内容 |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| [DSH 0.1.5 局域网验证记录](DSH_0.1.5_LAN.md) | DSH Mobile 0.3.15 对 DeepSeek Harness 0.1.5 的局域网兼容验证 |
|
|
20
|
+
|
|
21
|
+
## 面向维护者 / Maintainer notes
|
|
22
|
+
|
|
23
|
+
维护者文档面向改代码的人,**不随 npm 包发布**,只在仓库中阅读:
|
|
24
|
+
|
|
25
|
+
- `docs/HANDOFF_SELF_HOSTED_FRP.md` —— 自建 FRP 的代码地图、错误码、安全边界与真机验证记录。
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Self-hosted FRP guide
|
|
2
2
|
|
|
3
|
+
[中文指南](SELF_HOSTED_FRP.md)
|
|
4
|
+
|
|
3
5
|
Self-hosted FRP is for users who already operate a VPS and want to avoid the bandwidth limits of public tunnels. The phone reaches Caddy on the VPS over HTTPS, crosses the encrypted FRP tunnel, and then reaches DSH on the computer. FRP only transports the request; DSH pairing is still required.
|
|
4
6
|
|
|
5
7
|
```text
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Own HTTPS reverse proxy
|
|
2
|
+
|
|
3
|
+
[中文指南](SELF_HOSTED_ORIGIN.md)
|
|
4
|
+
|
|
5
|
+
> This provider ships with the package from **0.4.2** on.
|
|
6
|
+
|
|
7
|
+
This provider appears under **Mobile Access → Remote → Self-hosted connection → Own reverse proxy**. It adds a separate authenticated private HTTP origin for an HTTPS reverse proxy you already own; it does not install a tunnel or manage your proxy, DNS, certificate, firewall, or router.
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
- Public origin: `https://phone.example.com:8815` (HTTPS only, optional custom port, no path/query/fragment/credentials).
|
|
12
|
+
- Same computer: listen on `127.0.0.1:3444`, allow `127.0.0.0/8`.
|
|
13
|
+
- Separate LAN proxy: listen on the DSH computer's private IPv4, e.g. `192.168.50.10:3444`, and allow the proxy's actual direct source, e.g. `192.168.50.1/32`.
|
|
14
|
+
- Only explicit loopback/RFC1918 IPv4 binds are supported. Wildcard, public and IPv6 binds are rejected. The port must be 1024–65535 and may not be 3443 (the LAN gateway) or 3080 (DSH's own WebServer); note that 3444 is also the cloudflared named tunnel's default forward port. Source CIDRs must be canonical private/loopback networks, at most 16; prefer a single-host /32. Forwarded headers do not determine source authorization.
|
|
15
|
+
|
|
16
|
+
Select **Save and start backend**, then point the proxy at the displayed HTTP backend. **Never expose/port-forward that HTTP listener publicly**, and never bypass it by proxying to DSH 8080 (or its configured WebServer port) or the LAN 3443 gateway. HTTP between separate devices is suitable only for a trusted private network. Pairing, authentication, normalized-exact Host/Origin enforcement, CSRF, Secure cookies and WebSocket path policy remain active. Do not open the plain HTTP backend in a browser to verify it: cookies are marked `Secure`, so a browser discards them over `http://` and login or CSRF will fail with no visible reason.
|
|
17
|
+
|
|
18
|
+
## Proxy contract
|
|
19
|
+
|
|
20
|
+
Terminate public TLS with a trusted certificate on your proxy. Preserve the external **Host including the port** (`phone.example.com:8815`), Origin, cookies and authentication/CSRF headers; do not rewrite cookie security attributes or cache authentication responses. Forward WebSocket upgrades and bidirectional traffic.
|
|
21
|
+
|
|
22
|
+
[Lucky's Web module](https://lucky666.cn/docs/modules/web) supports WebSocket by default. Use **“使用请求Host” (request Host)** rather than **“使用目标地址Host” (target-address Host)**, and verify the external custom port survives. Certificates remain Lucky's responsibility. If the proxy is on another machine, configure routing and a source-restricted private firewall rule yourself; the plugin does not change either.
|
|
23
|
+
|
|
24
|
+
## Readiness, pairing and persistence
|
|
25
|
+
|
|
26
|
+
**Backend listening is not public readiness.** The provider and diagnostics make no public probe: check your domain, certificate, public port, pairing and WebSocket from the phone's intended external network. Use the existing Android **Remote access** QR flow on app **0.4.0 or later** for user-owned domains and custom HTTPS ports. App 0.3.16 rejects these pairings; no APK change is required for this provider. Local real-socket HTTPS/HTTP/WebSocket tests are not a substitute for testing your own proxy and phone.
|
|
27
|
+
|
|
28
|
+
Stopping retains settings and paired devices. **Clear proxy settings** stops only this listener and deletes only its configuration, after confirmation. The separate existing **reset remote devices** action removes shared remote pairings, but keeps proxy settings and LAN devices. Switching providers stops the previous remote provider without affecting LAN.
|
|
29
|
+
|
|
30
|
+
Under the default `$DSH_HOME/mobile-access/` root, settings live at `remote/origin/config/settings.json`, enabled state at `remote/origin/control.json`, and pairings continue to use `remote/devices.json`. Only the selected, enabled provider resumes on DSH restart. All addresses shown here are examples; supply your own configuration.
|
|
31
|
+
|
|
32
|
+
## More detail
|
|
33
|
+
|
|
34
|
+
The [Chinese guide](SELF_HOSTED_ORIGIN.md) carries the full field-by-field table, the Lucky checklist and a troubleshooting list; this English page is the condensed version.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# 自有 HTTPS 反向代理
|
|
2
|
+
|
|
3
|
+
[English guide](SELF_HOSTED_ORIGIN.en.md)
|
|
4
|
+
|
|
5
|
+
> 本功能自 **0.4.2** 起随包发布。
|
|
6
|
+
|
|
7
|
+
## 适用场景与安全边界
|
|
8
|
+
|
|
9
|
+
你已有 Lucky、Nginx、Caddy 等 HTTPS 反向代理,不需要 Funnel、cpolar 或 FRP 隧道。进入电脑端 **移动访问 → 远程 → 自建连接 → 自有反向代理**,让插件提供一个独立的、带设备配对认证的私有 HTTP 后端:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
手机 → https://phone.example.com:8815(你的代理终止 TLS)
|
|
13
|
+
→ http://192.168.50.10:3444(本插件的私有 HTTP 后端)
|
|
14
|
+
→ 当前 DSH WebServer(仍由插件内部连接)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- **只有第一段允许暴露公网。HTTP 后端不能做公网端口映射。** HTTP 段仅适用于可信私网;局域网其他设备上的代理也会接触明文流量。
|
|
18
|
+
- 后端不是裸 DSH:仍执行设备配对、认证、归一化后精确匹配的 Host/Origin 校验、CSRF、Secure Cookie 和 WebSocket 路径策略。没有配对的访问不能直接进入 DSH。**不要用浏览器直接打开这个明文 HTTP 后端做验证**:Cookie 带 `Secure`,浏览器在 `http://` 下会丢弃它们,登录或 CSRF 会无提示失败。
|
|
19
|
+
- 不要把公网反代目标改成 DSH 的 WebServer 端口(默认 3080),也不要反代现有 LAN HTTPS 3443。新后端默认 3444,**明确拒绝 3443 和 3080**,并拒绝 1024 以下需要特权的端口。注意 3444 同时是 cloudflared 命名隧道的默认转发端口:同一时刻只有一个远程提供方能运行,但切换到本提供方时请确认该端口已释放。DSH 与 LAN 的原有设置不变。
|
|
20
|
+
- 公网 TLS、证书续期、DNS、路由和代理由你管理。插件不会安装隧道组件、部署 VPS、修改代理/防火墙/路由器,也不会为该提供方发起公网探测。
|
|
21
|
+
|
|
22
|
+
## 填写四项设置
|
|
23
|
+
|
|
24
|
+
以下均为通用示例,替换成自己的地址。
|
|
25
|
+
|
|
26
|
+
| 字段 | 代理与 DSH 同机 | 代理在另一台 LAN 设备 |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| 公网 HTTPS 地址 | `https://phone.example.com:8815` | `https://phone.example.com:8815` |
|
|
29
|
+
| 私有监听 IPv4 | `127.0.0.1` | DSH 电脑的私网地址,如 `192.168.50.10` |
|
|
30
|
+
| HTTP 后端端口 | `3444` | `3444` |
|
|
31
|
+
| 允许的代理来源 CIDR | `127.0.0.0/8` | 代理的实际来源,如 `192.168.50.1/32` |
|
|
32
|
+
|
|
33
|
+
公网地址只能是 HTTPS Origin,可带非 443 端口;不包含路径、账号密码、查询参数或 fragment。私有/保留 IP、本地域名和 IPv6 字面地址不适用于此提供方。
|
|
34
|
+
|
|
35
|
+
监听地址必须是**本机已有的明确回环或 RFC1918 私有 IPv4**,不能填 `0.0.0.0`、主机名、公网 IP 或 IPv6。端口必须是 **1024–65535** 的整数,并排除 3443(局域网网关)与 3080(DSH 自身);请使用未被占用的端口。
|
|
36
|
+
|
|
37
|
+
CIDR 限制针对 TCP 连接的**直接来源地址**,不是手机 IP,也不是 `X-Forwarded-For`、`X-Real-IP` 或 `Forwarded`。Docker、桥接或 NAT 可能改变这个地址,应填写后端实际看到的私网来源。优先用单地址 `/32`,不要为了排错放宽整个局域网。最多 16 项,用空格或逗号分隔;只允许回环/私网网段,网络地址不能带主机位。LAN 监听必须显式列出私网代理来源。
|
|
38
|
+
|
|
39
|
+
点击 **保存并启动后端**。已经开启时会重新载入配置。面板会分别显示公网 HTTPS 地址与**正在监听的 HTTP 后端**;停止后仍可查看、复制已保存的后端地址。
|
|
40
|
+
|
|
41
|
+
## 反向代理要求(含 Lucky)
|
|
42
|
+
|
|
43
|
+
1. 在代理上为公网地址配置可信的 HTTPS 证书,并终止 TLS。不要让手机忽略证书错误。
|
|
44
|
+
2. 代理目标使用面板给出的 **HTTP 后端地址**,而不是手机使用的 HTTPS 地址。
|
|
45
|
+
3. **保留请求的外部 Host,包含非默认端口。** 上例后端必须收到 `Host: phone.example.com:8815`,不能改成 `192.168.50.10:3444`,也不能漏掉 `:8815`。
|
|
46
|
+
4. 透传浏览器的 Origin、Cookie、Set-Cookie,以及认证/CSRF 相关头,不要改写 Cookie 的域或安全属性;不要缓存配对或认证响应。
|
|
47
|
+
5. 透传 WebSocket 升级(HTTP/1.1、Upgrade、Connection)以及双向数据。已有的 WebSocket 路径白名单继续生效;第三方插件需要时仍在现有面板中单独允许其路径。
|
|
48
|
+
|
|
49
|
+
根据 [Lucky 官方 Web 模块文档](https://lucky666.cn/docs/modules/web),WebSocket 默认支持,不必寻找单独的开启开关。Host 自定义模式应使用 **“使用请求Host”**,不要选 **“使用目标地址Host”**,并核对非默认端口没有丢失。TLS/证书仍配置在 Lucky,不在这个 HTTP 后端上配置。
|
|
50
|
+
|
|
51
|
+
若代理在另一台机器上,还需由管理员保证网络可达,并将主机防火墙入站范围限制到代理的实际私网来源;此功能不自动改防火墙。
|
|
52
|
+
|
|
53
|
+
## 状态与手机验证
|
|
54
|
+
|
|
55
|
+
**“后端已监听”只证明本地 HTTP 监听成功,不代表公网 HTTPS、证书或 WebSocket 已成功。** 本提供方的连接诊断也只报告本地状态,不自动向公网地址探测。
|
|
56
|
+
|
|
57
|
+
在电脑端点击 **生成远程配对二维码**,再在 Android App **0.4.0 或更高版本**的 **远程访问** 流程扫码(不要使用 LAN 扫码流程)。现场验证中 0.3.16 会拒绝自有域名配对;现有自有域名逻辑保留 HTTPS 自定义端口,无需为此功能修改 APK。
|
|
58
|
+
|
|
59
|
+
上线前,必须用手机从目标外网实际检查:
|
|
60
|
+
|
|
61
|
+
- 公网地址的域名、端口和证书均正确;
|
|
62
|
+
- 扫码配对并登录 DSH 成功;
|
|
63
|
+
- 发起对话或其他实时操作,确认 WebSocket 持续工作;
|
|
64
|
+
- 关闭此后端后远程入口失效,而原有 LAN 访问仍正常。
|
|
65
|
+
|
|
66
|
+
本仓库的本地集成测试使用真实 HTTPS 代理、HTTP 后端和 WebSocket 回环链路;它不能替代你自己的 Lucky、服务器、外网和手机验收。
|
|
67
|
+
|
|
68
|
+
## 停止、清除配置与重置设备
|
|
69
|
+
|
|
70
|
+
| 操作 | 结果 |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| 关闭远程访问 | 停止此后端,保留配置与配对设备。 |
|
|
73
|
+
| 清除代理配置(需确认) | 停止此后端,只删除它的代理设置;保留远程配对设备、LAN 和其他提供方配置。 |
|
|
74
|
+
| 关闭并清除远程设备(需确认) | 现有通用重置操作;清除共享远程配对,所有远程设备需重新配对;保留代理设置与 LAN 设备。 |
|
|
75
|
+
| 切换提供方 | 现有协调器先停止前一提供方;只允许一个远程提供方运行,不影响 LAN。 |
|
|
76
|
+
|
|
77
|
+
默认数据根目录为 `$DSH_HOME/mobile-access/`(定制安装以实际路径为准)。新增设置是 `remote/origin/config/settings.json`,开启状态是 `remote/origin/control.json`;沿用 `remote/devices.json` 共享远程设备存储。正常退出后配置保留;再次启动 DSH 时,仅当前选中且已开启的提供方恢复监听。
|
|
78
|
+
|
|
79
|
+
## 常见问题
|
|
80
|
+
|
|
81
|
+
- **端口占用**:换一个空闲后端端口并同步修改代理目标;不要挪动现有 LAN 或 DSH 端口。
|
|
82
|
+
- **监听地址不可用**:填写 DSH 本机当前私有 IPv4,不是代理机器的 IP。
|
|
83
|
+
- **被拒绝 / 403**:核对直接来源 CIDR、完整外部 Host 和 HTTPS Origin;伪造转发头不会绕过这些检查。
|
|
84
|
+
- **HTTP 页面可开但实时功能失败**:核对代理 WebSocket 转发、超时及路径白名单,不要通过关闭来源验证来排错。
|
|
85
|
+
- **只有“后端已监听”**:这是预期语义;继续检查公网 DNS、端口映射到 HTTPS 代理、证书及手机实际访问。
|