@eduwork/dsh-remote-access 0.1.0 → 0.1.1
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 +8 -0
- package/README.md +9 -7
- package/README_EN.md +9 -7
- package/docs/DEPLOYMENT.md +168 -0
- package/docs/DEPLOYMENT_EN.md +168 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# 变更记录 / Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1 — 2026-10-24
|
|
4
|
+
|
|
5
|
+
- 随 npm 包提供中英文自托管中继部署指南:固定版本启动、Key、HTTPS/Nginx、路径前缀、systemd、OIDC、iframe 与排错。
|
|
6
|
+
- 澄清同源集成的信任范围,以及直接终端接口与 Agent 权限的区别;运行代码与协议不变。
|
|
7
|
+
|
|
8
|
+
- Include bilingual relay deployment guides covering pinned npm startup, access keys, HTTPS/Nginx, path prefixes, systemd, OIDC, iframe integration and troubleshooting.
|
|
9
|
+
- Clarify same-origin trust boundaries and direct terminal APIs versus Agent permissions; runtime code and protocol are unchanged.
|
|
10
|
+
|
|
3
11
|
## 0.1.0 — 2026-10-23
|
|
4
12
|
|
|
5
13
|
- 首个版本:在手机或其他电脑的浏览器中使用本机的 DSH,包含 DSH 插件与设置页、可自托管的中继和网页客户端。
|
package/README.md
CHANGED
|
@@ -16,13 +16,15 @@
|
|
|
16
16
|
需要 Node.js 24.22.1 或更新版本,以及为中继域名提供 HTTPS 并转发 WebSocket 的反向代理。
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
RELAY_URL=https://remote.example.edu RELAY_ACCESS_KEYS
|
|
20
|
-
npx
|
|
19
|
+
RELAY_URL=https://remote.example.edu/ RELAY_ACCESS_KEYS=替换成随机Key \
|
|
20
|
+
npx --yes --package=@eduwork/dsh-remote-access@0.1.1 eduwork-remote-relay
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
`RELAY_URL` 是用户访问中继的公开地址;`RELAY_ACCESS_KEYS` 是发给电脑主人的接入密钥,多个用逗号分隔。中继默认监听 `127.0.0.1:8787`,可用 `HOST`、`PORT` 修改;`<RELAY_URL>healthz` 返回 `ok`,可用于监控。
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
完整的 [中继部署指南](docs/DEPLOYMENT.md) 包含 Key 生成、Windows PowerShell 命令、HTTPS/Nginx、路径前缀、systemd 常驻运行、机构登录和 iframe 集成,以及启动后的验证与排错步骤。
|
|
26
|
+
|
|
27
|
+
中继也可以部署在同域的单独路径,例如 `RELAY_URL=https://example.edu/eduwork-remote/`。反向代理须保留此前缀,电脑上填写同一个完整地址。同域路径不隔离浏览器权限,部署者须确认站点与被配对电脑处于可接受的共同信任范围(见[安全](#安全));作用于整个站点的 Service Worker 还须放过这段路径。
|
|
26
28
|
|
|
27
29
|
学校或企业也可以让成员凭自己的登录使用中继,不必逐个发接入密钥:`RELAY_OIDC_ISSUER` 填机构 OpenID Connect 登录的 issuer(例如 `https://sso.example.edu`),`RELAY_OIDC_CLIENT_IDS` 填桌面应用在该登录中的 client ID,多个用逗号分隔。电脑以登录得到的 ID Token 注册,中继用 issuer 公布的公钥验证,只记录用户标识,不记录 Token。Token 到期前电脑会自动换新的;到期仍未换新时,中继断开这台电脑现有的远程连接。机构登录失效、需要重新登录时,电脑停止连接中继,设置页提示重新登录。两种方式可以同时启用。
|
|
28
30
|
|
|
@@ -56,11 +58,11 @@ dsh plugin --profile web add @eduwork/dsh-remote-access
|
|
|
56
58
|
|
|
57
59
|
## 远程设备能做什么
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
可以选择已有工作区,查看或新建会话,继续和取消任务,审批或回答提问,查看和下载结果;宿主已安装相应插件时,还可以使用 Studio 成果和日历。
|
|
60
62
|
|
|
61
63
|
手机上看到的就是 DSH 自己的界面,只按窄屏做了调整:会话列表(工作区目录、搜索、新会话等)从左上角打开并铺满屏幕,选中会话或面板后自动收起;设置同样铺满屏幕,各分区排成一行标签,在远程设备上只能查看;对话中的耗时、重试、审批和提问与电脑上一致。
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
远端不能直接调用打开本机窗口、程序或发出通知的接口,不能注册或删除工作区、修改设置、管理账号、插件、凭据、技能、记忆或远程访问本身,也不能直接使用终端与代码执行接口。**通过 Agent 发起的任务仍可能执行命令,沿用该会话的权限与审批规则**;禁止直接调用终端接口不等于禁止 Agent 使用工具。完整清单见 [src/gate/gate.ts](src/gate/gate.ts),未列出的接口一律拒绝。
|
|
64
66
|
|
|
65
67
|
## 安全
|
|
66
68
|
|
|
@@ -68,8 +70,8 @@ dsh plugin --profile web add @eduwork/dsh-remote-access
|
|
|
68
70
|
- 设备密钥由浏览器生成且不可导出,电脑只保存设备公钥。每次连接都要用设备密钥签名。
|
|
69
71
|
- 接入密钥和机构登录只决定谁能使用这台中继,不能用来连接别人的电脑。接入密钥保存在 DSH 的凭据里(`EDUWORK_REMOTE_ACCESS_KEY`),不写进插件设置;机构登录的 ID Token 只发给发行版预置的中继,设置页不能更改这个地址。
|
|
70
72
|
- 手机上打开的网页由中继提供,部署中继的人可以修改它。请只使用你信任的中继。
|
|
71
|
-
- 手机上显示的 DSH
|
|
72
|
-
-
|
|
73
|
+
- 手机上显示的 DSH 界面由所连的电脑提供,与中继网页同源。浏览器按来源(协议、主机、端口)而非路径隔离数据,因此一台电脑提供的界面能使用此浏览器与其他电脑的配对去连接它们。请只与自己的电脑配对。
|
|
74
|
+
- 同理,中继与其他网站同源时,那些网站的脚本能使用这里的配对连接电脑;任何一台被配对的电脑提供的界面,也能以用户的身份访问那些网站及脚本可访问的浏览器数据。独立域名能缩小信任范围;若选择同域路径集成,需要接受这项共同信任边界,路径前缀和普通 iframe 均不提供隔离。
|
|
73
75
|
- 允许其他页面嵌入中继的网页后,那些页面能把它显示在自己的页面里并加以遮挡,诱导用户点击(例如批准命令)。只允许你信任的页面嵌入。
|
|
74
76
|
|
|
75
77
|
协议细节见[协议文档](docs/PROTOCOL.md)。
|
package/README_EN.md
CHANGED
|
@@ -16,13 +16,15 @@ The package has two parts:
|
|
|
16
16
|
You need Node.js 24.22.1 or later and a reverse proxy that serves HTTPS for the relay's domain and forwards WebSockets.
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
RELAY_URL=https://remote.example.edu RELAY_ACCESS_KEYS=
|
|
20
|
-
npx
|
|
19
|
+
RELAY_URL=https://remote.example.edu/ RELAY_ACCESS_KEYS=replace-with-a-random-key \
|
|
20
|
+
npx --yes --package=@eduwork/dsh-remote-access@0.1.1 eduwork-remote-relay
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
`RELAY_URL` is the public address users reach the relay at; `RELAY_ACCESS_KEYS` are the comma-separated access keys you give to computer owners. The relay listens on `127.0.0.1:8787` by default; change it with `HOST` and `PORT`. `<RELAY_URL>healthz` answers `ok` for monitoring.
|
|
24
24
|
|
|
25
|
-
The relay
|
|
25
|
+
The [relay deployment guide](docs/DEPLOYMENT_EN.md) covers key generation, Windows PowerShell commands, HTTPS/Nginx, path prefixes, persistent systemd operation, organization sign-in, iframe integration, verification and troubleshooting.
|
|
26
|
+
|
|
27
|
+
The relay can also share a domain under its own path, such as `RELAY_URL=https://example.edu/eduwork-remote/`. Preserve the prefix in proxy requests and enter the same complete URL on the computer. Paths do not isolate browser privileges: the operator must accept the shared trust boundary between the site's applications and paired computers (see [Security](#security)). A site-wide Service Worker must also leave this path alone.
|
|
26
28
|
|
|
27
29
|
A school or company can instead let its members use the relay with their own sign-in, without handing out access keys: set `RELAY_OIDC_ISSUER` to the issuer of the organization's OpenID Connect sign-in (for example `https://sso.example.edu`) and `RELAY_OIDC_CLIENT_IDS` to the desktop application's client IDs in it, separated by commas. Computers register with the ID Token their sign-in issued; the relay checks it against the keys the issuer publishes and keeps only the user's identifier, never the token. Computers switch to a new token before theirs expires; if one expires without being renewed, the relay ends that computer's remote connections. When the organization sign-in has lapsed and needs signing in again, the computer stops connecting to the relay and the settings page asks the user to sign in again. Both ways can be enabled together.
|
|
28
30
|
|
|
@@ -56,11 +58,11 @@ Choose "Remove" under "Paired devices" on the computer to disconnect a device at
|
|
|
56
58
|
|
|
57
59
|
## What remote devices can do
|
|
58
60
|
|
|
59
|
-
They can choose an existing workspace, view and start conversations, continue and cancel tasks, approve or answer questions, and view and download results
|
|
61
|
+
They can choose an existing workspace, view and start conversations, continue and cancel tasks, approve or answer questions, and view and download results. Studio results and the calendar are also available when those plugins are installed on the host.
|
|
60
62
|
|
|
61
63
|
A phone shows DSH's own interface, only fitted to the narrow screen: the session list (workspace folders, search, new session and so on) opens full screen from the top-left corner and closes once you pick a session or a panel; settings also fill the screen, with the sections as a row of tabs, and are view only on remote devices; conversations show durations, retries, approvals and questions as on the computer.
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
Remote devices cannot directly call APIs that open local windows or programs or show notifications, register or remove workspaces, change settings, manage accounts, plugins, credentials, skills, memories or remote access, or access terminal and code-execution APIs. **Tasks submitted through the Agent can still execute commands under the session's permission and approval rules**; blocking direct terminal APIs does not disable Agent tools. The full list is in [src/gate/gate.ts](src/gate/gate.ts); anything not listed is refused.
|
|
64
66
|
|
|
65
67
|
## Security
|
|
66
68
|
|
|
@@ -68,8 +70,8 @@ They cannot open windows or programs or show notifications on the computer, regi
|
|
|
68
70
|
- The browser generates the device key as non-extractable; the computer stores only its public key. Every connection is signed with the device key.
|
|
69
71
|
- An access key or an organization sign-in only decides who may use the relay; neither can be used to connect to someone else's computer. The access key is kept in DSH's credentials (`EDUWORK_REMOTE_ACCESS_KEY`), not in the plugin's settings; the ID Token of an organization sign-in goes only to the relay the distribution preset, which the settings page cannot change.
|
|
70
72
|
- The relay serves the page you open on your phone, so whoever runs the relay can change it. Use only relays you trust.
|
|
71
|
-
- The DSH interface on your phone
|
|
72
|
-
- Likewise,
|
|
73
|
+
- The DSH interface on your phone comes from the paired computer and shares the relay's origin. Browsers isolate data by origin (scheme, host and port), not path, so one computer's interface can use this browser's pairings with other computers to connect to them. Pair only with your own computers.
|
|
74
|
+
- Likewise, sites sharing the relay's origin can use its pairings, and the interface of any paired computer can access those sites as the user and read browser data available to scripts. A separate domain narrows this trust boundary. Same-origin path integration accepts the shared boundary; neither path prefixes nor ordinary iframes provide isolation.
|
|
73
75
|
- Pages allowed to embed the relay's page can show it within their own and cover parts of it to trick users into clicking (approving a command, say). Allow only pages you trust.
|
|
74
76
|
|
|
75
77
|
See the [protocol document](docs/PROTOCOL.md) (Chinese) for details.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# 自托管中继部署
|
|
2
|
+
|
|
3
|
+
[English](DEPLOYMENT_EN.md) · [插件使用与配对](../README.md)
|
|
4
|
+
|
|
5
|
+
中继和桌面插件由同一个 npm 包 `@eduwork/dsh-remote-access` 交付。服务器只需运行包里的 `eduwork-remote-relay`,无需安装 EduWork 桌面客户端、模型服务或数据库。被访问的电脑运行 DSH/EduWork 和远控插件,其他终端通过浏览器访问中继的 HTTPS 地址。
|
|
6
|
+
|
|
7
|
+
## 1. 启动服务
|
|
8
|
+
|
|
9
|
+
准备 Node.js **24.22.1 或更新的受支持版本**、npm,以及可以转发 WebSocket 的 HTTPS 反向代理。以下命令固定使用 `0.1.1`,升级时明确修改版本号。
|
|
10
|
+
|
|
11
|
+
先生成接入 Key,保存好并交给需要注册电脑的用户:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Linux/macOS 前台试运行:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
export RELAY_URL='https://remote.example.edu/'
|
|
21
|
+
export RELAY_ACCESS_KEYS='替换成刚才生成的Key'
|
|
22
|
+
npx --yes --package=@eduwork/dsh-remote-access@0.1.1 eduwork-remote-relay
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Windows PowerShell:
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
$env:RELAY_URL = 'https://remote.example.edu/'
|
|
29
|
+
$env:RELAY_ACCESS_KEYS = '替换成刚才生成的Key'
|
|
30
|
+
npx --yes --package=@eduwork/dsh-remote-access@0.1.1 eduwork-remote-relay
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
默认监听 `127.0.0.1:8787`。`RELAY_URL` 是**用户访问的外部地址**,不是监听地址;TLS 在反向代理终止。前台命令退出就会停止服务,长期运行见第 3 节。只在本机调试时可使用 `http://localhost:8787/`;手机访问应使用具有有效证书的 HTTPS 地址,以便浏览器使用 Service Worker 和加密 API。
|
|
34
|
+
|
|
35
|
+
## 2. HTTPS 与路径前缀
|
|
36
|
+
|
|
37
|
+
独立域名可用 `RELAY_URL=https://remote.example.edu/`。同域路径可用:
|
|
38
|
+
|
|
39
|
+
```dotenv
|
|
40
|
+
RELAY_URL=https://example.edu/eduwork-remote/
|
|
41
|
+
RELAY_ACCESS_KEYS=替换成随机Key
|
|
42
|
+
RELAY_CLIENT_IP_HEADER=x-real-ip
|
|
43
|
+
HOST=127.0.0.1
|
|
44
|
+
PORT=8787
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
路径前缀由 `RELAY_URL` 决定,**没有另一个 PREFIX 环境变量**。客户端填写同一个完整地址。反向代理必须保留 `/eduwork-remote/`,网页、Service Worker、健康检查和 WebSocket 共用此前缀。
|
|
48
|
+
|
|
49
|
+
Nginx 示例:把 `map` 放在 `http` 块中;把 `location` 放进已有证书配置的 HTTPS `server` 块中。根路径部署时把两个 `location` 替换为一个 `location /`,其余代理设置相同。
|
|
50
|
+
|
|
51
|
+
```nginx
|
|
52
|
+
# http 块内
|
|
53
|
+
map $http_upgrade $eduwork_connection_upgrade {
|
|
54
|
+
default upgrade;
|
|
55
|
+
'' close;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# 已有 HTTPS server 块内
|
|
59
|
+
location = /eduwork-remote {
|
|
60
|
+
return 301 /eduwork-remote/;
|
|
61
|
+
}
|
|
62
|
+
location /eduwork-remote/ {
|
|
63
|
+
# 不在 8787 后加 /,否则 Nginx 会去掉路径前缀。
|
|
64
|
+
proxy_pass http://127.0.0.1:8787;
|
|
65
|
+
proxy_http_version 1.1;
|
|
66
|
+
proxy_set_header Host $host;
|
|
67
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
68
|
+
proxy_set_header Connection $eduwork_connection_upgrade;
|
|
69
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
70
|
+
proxy_read_timeout 3600s;
|
|
71
|
+
proxy_send_timeout 3600s;
|
|
72
|
+
proxy_buffering off;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
设置 `RELAY_CLIENT_IP_HEADER` 时,由可信代理覆盖该请求头,并让中继端口仅对该代理可达。同域已有 Service Worker 若控制整个站点,需要让它放过这段路径。
|
|
77
|
+
|
|
78
|
+
路径划分不会隔离浏览器的同源存储和权限。远端 DSH 界面来自所配对的电脑,与中继页面同源;同域业务和被配对电脑必须处于部署者接受的共同信任范围。独立域名可以缩小这个范围。同域集成前请阅读 [安全说明](../README.md#安全)。
|
|
79
|
+
|
|
80
|
+
## 3. 长期运行(Linux / systemd)
|
|
81
|
+
|
|
82
|
+
由维护者安装固定版本;服务本身使用普通系统账号运行:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
sudo useradd --system --no-create-home --shell /usr/sbin/nologin eduwork-remote
|
|
86
|
+
sudo install -d /opt/eduwork-remote /etc/eduwork-remote
|
|
87
|
+
sudo npm install --prefix /opt/eduwork-remote --omit=dev --ignore-scripts --save-exact @eduwork/dsh-remote-access@0.1.1
|
|
88
|
+
sudo install -m 600 /dev/null /etc/eduwork-remote/relay.env
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
账号已存在时跳过 `useradd`。将第 2 节的配置写入 `/etc/eduwork-remote/relay.env`,填写实际 URL 和随机 Key。环境文件不要提交到仓库。将以下内容保存为 `/etc/systemd/system/eduwork-remote.service`:
|
|
92
|
+
|
|
93
|
+
```ini
|
|
94
|
+
[Unit]
|
|
95
|
+
Description=EduWork remote relay
|
|
96
|
+
After=network-online.target
|
|
97
|
+
Wants=network-online.target
|
|
98
|
+
|
|
99
|
+
[Service]
|
|
100
|
+
Type=simple
|
|
101
|
+
User=eduwork-remote
|
|
102
|
+
Group=eduwork-remote
|
|
103
|
+
WorkingDirectory=/opt/eduwork-remote
|
|
104
|
+
EnvironmentFile=/etc/eduwork-remote/relay.env
|
|
105
|
+
ExecStart=/usr/bin/node /opt/eduwork-remote/node_modules/@eduwork/dsh-remote-access/lib/relay.js
|
|
106
|
+
Restart=on-failure
|
|
107
|
+
RestartSec=3
|
|
108
|
+
|
|
109
|
+
[Install]
|
|
110
|
+
WantedBy=multi-user.target
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
先用 `command -v node` 核对 `ExecStart` 中的路径,确认该 Node 版本满足要求;不要依赖交互式 shell 才会加载的版本管理器。
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
sudo systemctl daemon-reload
|
|
117
|
+
sudo systemctl enable --now eduwork-remote
|
|
118
|
+
sudo systemctl status eduwork-remote
|
|
119
|
+
sudo journalctl -u eduwork-remote -n 100 --no-pager
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
更改环境文件后执行 `sudo systemctl restart eduwork-remote`。更新 npm 包时在相同目录安装指定新版本后重启;保留 `package.json`、`package-lock.json` 和环境文件以便回退。中继重启会断开现有连接,在线电脑和设备会尝试重连。配对身份分别保存在电脑和远端浏览器中,中继不保存会话或配对数据库。
|
|
123
|
+
|
|
124
|
+
## 4. 验证与配对
|
|
125
|
+
|
|
126
|
+
路径部署示例:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
curl -fsS http://127.0.0.1:8787/eduwork-remote/healthz
|
|
130
|
+
curl -fsS https://example.edu/eduwork-remote/healthz
|
|
131
|
+
curl -I https://example.edu/eduwork-remote/sw.js
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
前两项应返回 `ok`,第三项应返回 200 和 JavaScript 内容类型。健康检查只说明 HTTP 服务可达;还须在桌面“设置 → 远程访问”填写同一外部地址和 Key,启用后确认“已连接”。随后按[配对步骤](../README.md#添加和移除设备)从另一终端接入,并在本机核对配对码、确认授权。接入 Key 允许电脑使用中继,不能替代这次设备配对。
|
|
135
|
+
|
|
136
|
+
## 5. 机构登录与 iframe(可选)
|
|
137
|
+
|
|
138
|
+
机构登录可替代 Key,也可与 Key 并存:
|
|
139
|
+
|
|
140
|
+
```dotenv
|
|
141
|
+
RELAY_OIDC_ISSUER=https://sso.example.edu
|
|
142
|
+
RELAY_OIDC_CLIENT_IDS=desktop-client-id
|
|
143
|
+
RELAY_FRAME_ANCESTORS=self https://portal.example.edu
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
单用机构登录时不设 `RELAY_ACCESS_KEYS`。`issuer` 必须与 Token 的 `iss` 完全一致,client ID 是桌面应用的 OIDC 客户端标识;多个 ID 用逗号分隔。中继从 discovery/JWKS 获取公钥,当前支持 **RS256 ID Token**,校验签名、issuer、audience/authorized party 和有效期;这里不是模型 API Key,也不是拿 access token 充当 ID Token。
|
|
147
|
+
|
|
148
|
+
电脑需安装 `@eduwork/dsh-oidc@0.3.1` 或后续兼容版本,并由发行版配置远控插件的 `organization: { relay, oidcProfileId }`。单纯设置服务端的 issuer 不会自动配置桌面登录。中继验证已有身份,不保存 refresh token,也不替用户登录;续期由电脑的 OIDC 插件处理。
|
|
149
|
+
|
|
150
|
+
`RELAY_FRAME_ANCESTORS` 默认 `none`,禁止 iframe 嵌入。允许同源父页面时填 `self`,其他父页面填完整来源(协议、主机和可选端口,不含路径)。在 shell 中给多个来源加引号:`RELAY_FRAME_ANCESTORS="self https://portal.example.edu"`。父站自己的 CSP 也要允许加载中继,代理不能再给此路径附加冲突的 `X-Frame-Options: DENY` 或 `frame-ancestors 'none'`。
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<iframe src="https://example.edu/eduwork-remote/" title="远程访问"></iframe>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
当前方案需要脚本、Service Worker 和同源身份,不能套一个移除同源身份的 sandbox;上述普通 iframe 示例不会提供安全隔离。跨站 iframe 在 Safari/WebKit 中可能无法加载电脑界面,优先使用同站集成或顶层打开中继。开放嵌入不改变本机配对与设备撤销要求。
|
|
157
|
+
|
|
158
|
+
## 常见问题
|
|
159
|
+
|
|
160
|
+
| 现象 | 优先检查 |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| 启动即退出,提示配置不完整 | `RELAY_URL`、Key 或完整 OIDC 配置是否实际进入进程环境。 |
|
|
163
|
+
| healthz 正常,电脑仍无法连接 | HTTPS 证书、WebSocket Upgrade、外部 URL/前缀一致性、接入 Key;OIDC 模式还要检查 issuer/JWKS 可达性和 ID Token。 |
|
|
164
|
+
| 子路径页面或 sw.js 返回 404 | `proxy_pass` 是否去掉了前缀,站点是否把此路径误转到另一个 SPA。 |
|
|
165
|
+
| 手机卡在加载页面 | 有效 HTTPS、Service Worker 未被父站拦截、电脑在线且未休眠、浏览器站点数据未被清理。 |
|
|
166
|
+
| 顶层能打开,iframe 打不开 | 中继 `RELAY_FRAME_ANCESTORS`、父页面 CSP、代理额外响应头,以及 WebKit 跨站限制。 |
|
|
167
|
+
|
|
168
|
+
使用接入 Key 时不需要机构登录插件。API、帧格式和加密机制见[协议文档](PROTOCOL.md)。
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Self-hosted relay deployment
|
|
2
|
+
|
|
3
|
+
[简体中文](DEPLOYMENT.md) · [Plugin usage and pairing](../README_EN.md)
|
|
4
|
+
|
|
5
|
+
The desktop plugin and relay ship in the same npm package, `@eduwork/dsh-remote-access`. The server runs `eduwork-remote-relay`; it does not need the EduWork desktop app, model services or a database. The accessed computer runs DSH/EduWork and the plugin, while other devices open the relay's HTTPS URL in a browser.
|
|
6
|
+
|
|
7
|
+
## 1. Start the service
|
|
8
|
+
|
|
9
|
+
Use Node.js **24.22.1 or a later supported version**, npm, and an HTTPS reverse proxy supporting WebSockets. These examples pin `0.1.1`; change the version explicitly when upgrading.
|
|
10
|
+
|
|
11
|
+
Generate an access key, retain it and give it to the users registering computers:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Linux/macOS foreground run:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
export RELAY_URL='https://remote.example.edu/'
|
|
21
|
+
export RELAY_ACCESS_KEYS='replace-with-the-generated-key'
|
|
22
|
+
npx --yes --package=@eduwork/dsh-remote-access@0.1.1 eduwork-remote-relay
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Windows PowerShell:
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
$env:RELAY_URL = 'https://remote.example.edu/'
|
|
29
|
+
$env:RELAY_ACCESS_KEYS = 'replace-with-the-generated-key'
|
|
30
|
+
npx --yes --package=@eduwork/dsh-remote-access@0.1.1 eduwork-remote-relay
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The default listener is `127.0.0.1:8787`. `RELAY_URL` is the **external address users visit**, not the listening address. The proxy terminates TLS. Exiting the foreground command stops the service; see section 3 for persistent operation. Local-only debugging can use `http://localhost:8787/`; phones should use HTTPS with a valid certificate for Service Worker and cryptographic APIs.
|
|
34
|
+
|
|
35
|
+
## 2. HTTPS and a path prefix
|
|
36
|
+
|
|
37
|
+
A dedicated domain uses `RELAY_URL=https://remote.example.edu/`. To share a domain under a path:
|
|
38
|
+
|
|
39
|
+
```dotenv
|
|
40
|
+
RELAY_URL=https://example.edu/eduwork-remote/
|
|
41
|
+
RELAY_ACCESS_KEYS=replace-with-a-random-key
|
|
42
|
+
RELAY_CLIENT_IP_HEADER=x-real-ip
|
|
43
|
+
HOST=127.0.0.1
|
|
44
|
+
PORT=8787
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`RELAY_URL` determines the prefix; **there is no separate PREFIX environment variable**. Enter the same complete URL on the computer. Preserve `/eduwork-remote/` in proxy requests: pages, Service Worker, health checks and WebSockets share that prefix.
|
|
48
|
+
|
|
49
|
+
For Nginx, put `map` in the `http` block and the locations in an existing HTTPS `server` block with certificates configured. For a root-path deployment, replace the two locations with one `location /`, keeping the same proxy settings.
|
|
50
|
+
|
|
51
|
+
```nginx
|
|
52
|
+
# Inside http
|
|
53
|
+
map $http_upgrade $eduwork_connection_upgrade {
|
|
54
|
+
default upgrade;
|
|
55
|
+
'' close;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Inside the existing HTTPS server
|
|
59
|
+
location = /eduwork-remote {
|
|
60
|
+
return 301 /eduwork-remote/;
|
|
61
|
+
}
|
|
62
|
+
location /eduwork-remote/ {
|
|
63
|
+
# No trailing / after 8787: preserve the path prefix.
|
|
64
|
+
proxy_pass http://127.0.0.1:8787;
|
|
65
|
+
proxy_http_version 1.1;
|
|
66
|
+
proxy_set_header Host $host;
|
|
67
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
68
|
+
proxy_set_header Connection $eduwork_connection_upgrade;
|
|
69
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
70
|
+
proxy_read_timeout 3600s;
|
|
71
|
+
proxy_send_timeout 3600s;
|
|
72
|
+
proxy_buffering off;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
When using `RELAY_CLIENT_IP_HEADER`, let the trusted proxy overwrite that header and keep the relay port reachable only by that proxy. An existing site-wide Service Worker must leave this path alone.
|
|
77
|
+
|
|
78
|
+
Paths do not isolate same-origin browser storage or privileges. The remote DSH interface comes from the paired computer and shares the relay's origin. The site's applications and paired computers must therefore belong to a trust boundary the operator accepts. A dedicated domain narrows that boundary. Read [Security](../README_EN.md#security) before sharing an origin.
|
|
79
|
+
|
|
80
|
+
## 3. Persistent service (Linux / systemd)
|
|
81
|
+
|
|
82
|
+
Have a maintainer install the pinned package; run the service as an ordinary system account:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
sudo useradd --system --no-create-home --shell /usr/sbin/nologin eduwork-remote
|
|
86
|
+
sudo install -d /opt/eduwork-remote /etc/eduwork-remote
|
|
87
|
+
sudo npm install --prefix /opt/eduwork-remote --omit=dev --ignore-scripts --save-exact @eduwork/dsh-remote-access@0.1.1
|
|
88
|
+
sudo install -m 600 /dev/null /etc/eduwork-remote/relay.env
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Skip `useradd` if the account exists. Write the section 2 configuration to `/etc/eduwork-remote/relay.env`, using your URL and generated key. Do not commit the environment file. Save the following as `/etc/systemd/system/eduwork-remote.service`:
|
|
92
|
+
|
|
93
|
+
```ini
|
|
94
|
+
[Unit]
|
|
95
|
+
Description=EduWork remote relay
|
|
96
|
+
After=network-online.target
|
|
97
|
+
Wants=network-online.target
|
|
98
|
+
|
|
99
|
+
[Service]
|
|
100
|
+
Type=simple
|
|
101
|
+
User=eduwork-remote
|
|
102
|
+
Group=eduwork-remote
|
|
103
|
+
WorkingDirectory=/opt/eduwork-remote
|
|
104
|
+
EnvironmentFile=/etc/eduwork-remote/relay.env
|
|
105
|
+
ExecStart=/usr/bin/node /opt/eduwork-remote/node_modules/@eduwork/dsh-remote-access/lib/relay.js
|
|
106
|
+
Restart=on-failure
|
|
107
|
+
RestartSec=3
|
|
108
|
+
|
|
109
|
+
[Install]
|
|
110
|
+
WantedBy=multi-user.target
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Check the Node path using `command -v node` and adjust `ExecStart` as needed. Verify that binary meets the version requirement; do not rely on a version manager loaded only by interactive shells.
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
sudo systemctl daemon-reload
|
|
117
|
+
sudo systemctl enable --now eduwork-remote
|
|
118
|
+
sudo systemctl status eduwork-remote
|
|
119
|
+
sudo journalctl -u eduwork-remote -n 100 --no-pager
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
After environment changes, run `sudo systemctl restart eduwork-remote`. To upgrade, install a specific new npm version in the same directory and restart. Retain `package.json`, `package-lock.json` and the environment file for rollback. A relay restart disconnects current connections; online computers and devices attempt to reconnect. Pairing identities live on the computer and remote browser; the relay stores no session or pairing database.
|
|
123
|
+
|
|
124
|
+
## 4. Verify and pair
|
|
125
|
+
|
|
126
|
+
For a path deployment:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
curl -fsS http://127.0.0.1:8787/eduwork-remote/healthz
|
|
130
|
+
curl -fsS https://example.edu/eduwork-remote/healthz
|
|
131
|
+
curl -I https://example.edu/eduwork-remote/sw.js
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The first two return `ok`; the last returns 200 with a JavaScript content type. Health checks establish HTTP reachability only. Enter the same external URL and key under desktop Settings → Remote access, enable it, and check for “Connected”. Follow the [pairing steps](../README_EN.md#add-and-remove-devices) on another device, compare codes and approve on the computer. An access key admits a computer to the relay; it does not replace device pairing.
|
|
135
|
+
|
|
136
|
+
## 5. Organization sign-in and iframe (optional)
|
|
137
|
+
|
|
138
|
+
Organization sign-in can replace access keys or coexist with them:
|
|
139
|
+
|
|
140
|
+
```dotenv
|
|
141
|
+
RELAY_OIDC_ISSUER=https://sso.example.edu
|
|
142
|
+
RELAY_OIDC_CLIENT_IDS=desktop-client-id
|
|
143
|
+
RELAY_FRAME_ANCESTORS=self https://portal.example.edu
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Omit `RELAY_ACCESS_KEYS` when using organization sign-in alone. The issuer must exactly match the token's `iss`. Client IDs identify desktop applications registered with the issuer; separate multiple IDs with commas. Discovery/JWKS supplies public keys. The relay currently accepts **RS256 ID Tokens** and checks signatures, issuer, audience/authorized party and validity. This is neither a model API key nor an access token used as an ID Token.
|
|
147
|
+
|
|
148
|
+
The computer needs `@eduwork/dsh-oidc@0.3.1` or a later compatible version, and distribution configuration for `organization: { relay, oidcProfileId }`. Setting an issuer on the server does not configure desktop login. The relay verifies existing identity, stores no refresh token and does not sign users in; the computer's OIDC plugin handles renewal.
|
|
149
|
+
|
|
150
|
+
`RELAY_FRAME_ANCESTORS` defaults to `none`, blocking iframe embedding. Use `self` for same-origin parents, or complete origins (scheme, host and optional port, without paths). Quote multiple sources in shell commands: `RELAY_FRAME_ANCESTORS="self https://portal.example.edu"`. The parent's CSP must also permit the relay, and the proxy must not add a conflicting `X-Frame-Options: DENY` or `frame-ancestors 'none'` to this path.
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<iframe src="https://example.edu/eduwork-remote/" title="Remote access"></iframe>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The current design needs scripts, Service Worker and same-origin identity; an opaque-origin sandbox is incompatible. The ordinary iframe above provides no security isolation. Cross-site iframes may fail to load the computer's interface in Safari/WebKit; use same-site integration or open the relay at the top level. Allowing embedding does not replace local pairing or device revocation.
|
|
157
|
+
|
|
158
|
+
## Troubleshooting
|
|
159
|
+
|
|
160
|
+
| Symptom | Check first |
|
|
161
|
+
| --- | --- |
|
|
162
|
+
| Immediate exit with incomplete-configuration error | Ensure the process receives `RELAY_URL` and either access keys or complete OIDC settings. |
|
|
163
|
+
| healthz works but the computer cannot connect | HTTPS certificate, WebSocket Upgrade, matching external URL/prefix and key; for OIDC, issuer/JWKS reachability and ID Token. |
|
|
164
|
+
| Path page or sw.js returns 404 | Check for prefix removal by `proxy_pass` or routing to another site's SPA. |
|
|
165
|
+
| Phone stays on the loading page | Valid HTTPS, no parent Service Worker interference, computer online and awake, browser site data retained. |
|
|
166
|
+
| Top-level page works but iframe does not | Relay frame ancestors, parent CSP, proxy response headers and WebKit cross-site restrictions. |
|
|
167
|
+
|
|
168
|
+
Access-key deployments do not need an organization login plugin. See the [protocol](PROTOCOL.md) for APIs, frames and encryption.
|