Files
dsh_shenxian/docs/domain-config.md
T

154 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 域名配置示例 — DSH 服务端登录插件
> 🧭 [← 返回 README](../README.md) · 基础部署:[deployment](deployment.md) · 出问题:[排查手册](troubleshooting.md)
本文给出域名与 nginx 的配置示例。**注意**:默认访问方式已改为**每用户子域名**(`<用户名>.dsh.<域名>`,HTTP + WebSocket 均已支持)——因为 DSH 的 SPA 用绝对路径、子路径方案不兼容。通配 nginx 配置见 [deployment.md §6](deployment.md),常见问题见 [troubleshooting.md](troubleshooting.md)。下面的「子路径」示例仅作遗留参考。
## 1. 访问拓扑
```
用户浏览器
└─(HTTPS)─> nginx(边缘:TLS 终结 + host 路由)
└─(反向代理)─> 编排服务 Fastify (127.0.0.1:3080)
├─ /api/* /login /register /admin /desktop (编排服务自管)
└─ /u/<userId>/dsh/* ──> 127.0.0.1:<动态端口> (每用户 DSH)
```
- 每用户 DSH 只绑定**回环端口**(`127.0.0.1:<动态端口>`),不直接暴露公网;端口由编排服务在启动时分配。
- nginx 只做边缘 TLS 与转发,把 `/u/*` 原样透传给编排服务即可——**每用户 DSH 的端口映射由编排服务自己维护**,nginx 无需在每次 DSH 重启时 reload。
## 2. 默认域名(所有用户共享一个域名)
用子路径区分用户:`https://dsh.example.com/u/<userId>/dsh/`。
```nginx
# /etc/nginx/conf.d/dshs.conf
# WebSocket 升级头(DSH Web UI 依赖)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream dsh_orchestrator {
server 127.0.0.1:3080; # 编排服务绑定端口(DSHS_PORT)
keepalive 32;
}
server {
listen 80;
server_name dsh.example.com;
return 301 https://$host$request_uri; # 强制 HTTPS
}
server {
listen 443 ssl http2;
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;
# 编排服务 trustProxy=true,会读取以下头还原真实 IP / 协议
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;
location / {
proxy_pass http://dsh_orchestrator;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s; # 长连接(DSH 对话 / WebSocket)
}
}
```
**说明**:上面只配置了默认域名一条规则;`/u/<userId>/dsh/` 的转发到哪台每用户 DSH,由编排服务内部决定,nginx 不关心。
## 3. 自定义域名(每用户专属域名)
设计目标:每个用户可用自己的域名直达自己的 DSH,例如 `https://alice.example.com` → alice 的 DSH。
### 3.1 手动映射(当前可手写生效)
由于编排服务已经支持 `/u/<userId>/dsh/*`,可先在 nginx 手写一条把自定义域名根路径重写到子路径:
```nginx
server {
listen 443 ssl;
server_name alice.example.com;
ssl_certificate /etc/letsencrypt/live/alice.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/alice.example.com/privkey.pem;
location / {
# 把自定义域名根路径重写到 alice 的 DSH 子路径
proxy_pass http://127.0.0.1:3080/u/<alice-user-id>/dsh/;
proxy_http_version 1.1;
proxy_set_header Host $host;
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 $connection_upgrade;
proxy_read_timeout 3600s;
}
}
```
> `proxy_pass` 带 URI(以 `/` 结尾)时,nginx 会把匹配到的 `/` 替换为 `/u/<alice-user-id>/dsh/`,
> 于是 `https://alice.example.com/foo` → `http://127.0.0.1:3080/u/<alice-user-id>/dsh/foo`。
> 编排服务再剥掉 `/u/<userId>/dsh` 前缀转发给该用户的 DSH。
### 3.2 自动生成(已实现)
`POST /api/nginx/regen` 会根据 `domains` 表为每个已验证的自定义域名生成一个上面的 `server {}` 块,
由 [src/nginx/generate.ts](../src/nginx/generate.ts) 的 `renderServerBlock(domain, upstreamPort)` 渲染,运维写入
`/etc/nginx/conf.d/` 后 `nginx -s reload`。
## 4. HTTPS 证书(certbot / ACME)
```sh
# 默认域名
sudo certbot --nginx -d dsh.example.com
# 自定义域名(每个用户域各自签发)
sudo certbot --nginx -d alice.example.com
```
证书签发后 certbot 会自动改写对应 `server {}` 的 `ssl_certificate*`。证书的自动签发/续期(ACME)暂未接入:目前域名由管理员在管理台手动标记「已验证」,签发仍用 certbot 手动跑。后续计划把这一步与 `PUT /api/domain`(DNS/HTTP 挑战验证)串起来,实现「用户填域名 → 自动验证 → 自动签发 → 生成并热加载 nginx 配置」。
## 5. WebSocket 说明
DSH Web UI 使用 WebSocket。编排服务的反向代理已支持 WebSocket 隧道([proxy.ts](../src/supervisor/proxy.ts) 的 `app.server` `upgrade` 处理器),按 Host 头路由到该用户 DSH,与 HTTP 一致。nginx 侧只需 `map $http_upgrade` + `Upgrade/Connection` 头透传。子域名下 WS 走 `wss://<用户名>.dsh.<域名>/api/...`。
## 6. 域名配置 API(已实现;ACME 自动验证待接入)
| 方法 | 路径 | 作用 |
|---|---|---|
| `GET` | `/api/domain` | 查询当前用户的域名 + nginx 配置 |
| `PUT` | `/api/domain` | 设置自定义域名,生成对应的 nginx `server {}` 块(`verified` 重置为 0) |
| `POST` | `/api/nginx/regen` | 重新生成并预览 nginx `server {}` 块 |
| `GET` | `/api/admin/domains` | 管理员:列出所有自定义域名 |
| `POST` | `/api/admin/domains/:id/verify` | 管理员:手动标记域名「已验证」(DNS 归属校验暂未自动化) |
实现见 [src/web/routes/domain.ts](../src/web/routes/domain.ts) 与 [src/nginx/generate.ts](../src/nginx/generate.ts)。
## 7. 生产环境变量
| 变量 | 说明 |
|---|---|
| `DSHS_PORT` | 编排服务绑定端口(nginx 上游需一致),默认 `3080` |
| `DSHS_DATA_ROOT` | 每用户 home / workspace 根,生产建议 `/var/lib/dshs` |
| `DSHS_SECURE_COOKIES` | **HTTPS 部署必须设为 `true`**(否则会话 cookie 不带 `Secure`) |
| `DSHS_DSH_BIN` | 子 DSH 可执行文件(默认 `dsh`) |
编排服务 `host` 默认 `127.0.0.1`(只监听回环,由 nginx 作为唯一公网入口),保持默认即可。
## 8. 安全注意
- 每用户 DSH 只绑回环端口;不要把它们改成 `0.0.0.0`,否则绕过认证直接暴露。
- 会话 cookie 在 HTTPS 下必须启用 `Secure`(见 §7)。
- 编排服务 `trustProxy=true`,仅在与 nginx 同机且信任其 `X-Forwarded-*` 头时使用;不要把它直接暴露公网。
- 自定义域名的归属校验(DNS/HTTP 挑战)在 P6 落地前,**不要**开放 `PUT /api/domain` 给普通用户随意映射他人路径。