Feat(go): add Signer interface + License Server for multi-customer deployments
This commit is contained in:
@@ -37,6 +37,16 @@ server/go/
|
||||
│ └── assets/
|
||||
│ ├── index.html # 从 ../../web/index.html sync 而来 (gitignored)
|
||||
│ └── static/ # 第三方 xterm.js 资源 (checked in)
|
||||
├── licensing/
|
||||
│ ├── signer.go # Signer interface (Sign / Mode / Close)
|
||||
│ ├── local.go # LocalSigner — operator 部署,HMAC 直连
|
||||
│ ├── remote.go # RemoteSigner — 客户部署,HTTPS + singleflight + 24h cache + heartbeat ticker
|
||||
│ ├── noop.go # NoOpSigner — free tier,返回空签名
|
||||
│ ├── token.go # JWT RS256 校验 + RSA 公钥加载
|
||||
│ ├── quota.go # 配额追踪 (Reserve / RefreshExisting / 5min eviction)
|
||||
│ ├── server.go # License Server HTTP handlers (/license/sign, /license/heartbeat) + Issue()
|
||||
│ ├── factory.go # NewFromEnv / LicenseServerFromEnv + 模式选择
|
||||
│ └── licensing_test.go # 17 个单元测试
|
||||
└── cmd/
|
||||
└── main.go # 程序入口
|
||||
```
|
||||
@@ -148,7 +158,12 @@ VSCode F5 调试时由 `sync-web-assets` preLaunchTask 自动同步。
|
||||
| `YAMA_PWDHASH` | 密码的 SHA256 哈希值 (64位十六进制) | `61f04dd6...` |
|
||||
| `YAMA_PWD` | 超级密码,用于 HMAC 签名验证;也作为 Web admin 密码的默认来源 | `your_super_password` |
|
||||
| `YAMA_WEB_ADMIN_PASS` | Web UI 的 admin 密码(明文);优先于 `YAMA_PWD`。两者都未设置时 Web 登录禁用 | `your_admin_password` |
|
||||
| `YAMA_SIGN_PASSWORD` | HMAC-SHA256 key used to sign CMD_MASTERSETTING replies; must match the client's expected value. Provision out-of-band. Unset → client refuses screen/file ops. | `<deployment-shared-secret>` |
|
||||
| `YAMA_SIGN_PASSWORD` | **[LocalSigner 模式]** HMAC-SHA256 master key,直接给 CMD_MASTERSETTING 签名。Operator 自己的部署用。设置此变量后进入 LocalSigner 模式(见下方"签名模式")。 | `<deployment-shared-secret>` |
|
||||
| `YAMA_LICENSE_SERVER` | **[RemoteSigner 模式]** Operator 的 License Server 公开 URL。客户部署设置此变量后进入 RemoteSigner 模式 —— 每次新设备登录会 HTTPS POST 给 License Server 拿签名,本机永远看不到 HMAC master key。必须与 `YAMA_LICENSE_TOKEN` 同时设置。 | `https://license.example.com` |
|
||||
| `YAMA_LICENSE_TOKEN` | **[RemoteSigner 模式]** Operator 颁发的客户 JWT(RS256),作为 Bearer token 鉴权。每个客户一份。 | `eyJhbGciOiJSUzI1NiI...` |
|
||||
| `YAMA_LICENSE_OFFLINE_HRS` | **[RemoteSigner 模式]** License Server 短暂不可达时,本地缓存签名的宽限期(小时)。默认 24。0 → 不缓存,每次新登录必须联网。 | `24` |
|
||||
| `YAMA_LICENSE_PUBLIC_KEY` | **[License Server 模式]** Operator 自己(已经是 LocalSigner)想顺便对外提供 License Server 时,用来验证客户提交的 JWT 的 RSA 公钥 PEM 路径。必须与 `YAMA_LICENSE_HTTP_ADDR` 同时设置。 | `./license_pub.pem` |
|
||||
| `YAMA_LICENSE_HTTP_ADDR` | **[License Server 模式]** License Server HTTP 监听地址。**仅在 LocalSigner 模式下生效**(RemoteSigner 客户不能反向当 license server)。建议挂 nginx/Caddy 加 TLS 后对外。 | `:8443` |
|
||||
| `YAMA_USERS_FILE` | Path to the JSON file that persists non-admin web users (allowed_groups, password hash, salt). Default is `users.json` in the working directory. | `users.json` |
|
||||
| `YAMA_WEB_ALLOWED_ORIGINS` | Comma-separated WebSocket Origin allowlist for cross-origin upgrades. Empty (default) → only same-origin upgrades are accepted, which is correct when the web UI and `/ws` share a host. Add an entry per trusted PWA / dev origin. | `https://yama.example.com,https://yama-mobile.example.com` |
|
||||
| `YAMA_WEB_TRUST_PROXY` | Set to `1` only when running behind a reverse proxy you control (caddy / nginx / cloudflare). Switches client-IP extraction to use the last entry of `X-Forwarded-For` instead of `RemoteAddr`, so per-IP login rate limit sees the real client. Direct-exposure deployments MUST leave this unset — otherwise attackers can spoof the header to evade rate limits. | `1` |
|
||||
@@ -165,6 +180,46 @@ $env:YAMA_PWD="your_super_password"
|
||||
.\simpleremoter-server.exe
|
||||
```
|
||||
|
||||
## 签名模式(CMD_MASTERSETTING signer)
|
||||
|
||||
单个 Go 二进制按启动时的环境变量自动选择三种签名模式之一。同一个 master HMAC key 永远不会出现在客户机器上 —— 这是把 Go server 商业化部署给付费客户的核心安全前提。
|
||||
|
||||
| 模式 | 触发条件 | 用途 |
|
||||
| ---- | -------- | ---- |
|
||||
| **LocalSigner** | `YAMA_SIGN_PASSWORD` 已设 | Operator 自己的部署。master HMAC key 在本机内存,签名直连 HMAC,微秒级延迟。**可选**:再设 `YAMA_LICENSE_PUBLIC_KEY` + `YAMA_LICENSE_HTTP_ADDR` 让本进程同时对外提供 License Server HTTP 服务。 |
|
||||
| **RemoteSigner** | `YAMA_LICENSE_SERVER` + `YAMA_LICENSE_TOKEN` 已设 | 客户部署。本机**永远看不到** master HMAC key —— 每次新设备登录会 HTTPS POST 到 operator 的 License Server,拿到签名后塞进 CMD_MASTERSETTING。同 (clientID, startTime) 元组的签名缓存 24h(可调,`YAMA_LICENSE_OFFLINE_HRS`),用于扛短暂网络故障。 |
|
||||
| **NoOpSigner** | 上述都没设 | Free tier。返回空签名 → 客户端私有库拒绝启动 screen/file 功能。设备列表仍然可用。 |
|
||||
|
||||
注:`YAMA_SIGN_PASSWORD` 与 `YAMA_LICENSE_SERVER` 同时设置时 LocalSigner 优先(operator 自己的 server 不应该回连自己)。
|
||||
|
||||
### License Server endpoints(仅 LocalSigner 暴露)
|
||||
|
||||
设了 `YAMA_LICENSE_PUBLIC_KEY` + `YAMA_LICENSE_HTTP_ADDR` 后,本进程会监听 `YAMA_LICENSE_HTTP_ADDR` 提供两个端点:
|
||||
|
||||
- `POST /license/sign` — body `{"client_id":"...","start_time":"..."}`,header `Authorization: Bearer <customer-JWT>`,回 `{"signature":"<64-hex>"}`。强制按 JWT 的 `tier` + `max_devices` 配额限制。
|
||||
- `POST /license/heartbeat` — body `{"active_device_count":N,"active_device_ids":["..."]}`,回 `{"server_view_count":M,"drift":N-M}`。Drift 大于阈值时记日志,供 operator 反作弊审核。
|
||||
|
||||
部署建议:本进程只跑 plain HTTP,前面挂 nginx/Caddy/cloudflare 加 TLS。JWT 校验已经把 `alg` 锁死成 RS256,杜绝 `alg:none` 攻击。
|
||||
|
||||
### 颁发客户 JWT
|
||||
|
||||
```bash
|
||||
# 一次性生成 RSA 密钥对(私钥 operator 自己保管,公钥用于 License Server 验证)
|
||||
openssl genrsa -out license_priv.pem 2048
|
||||
openssl rsa -in license_priv.pem -pubout -out license_pub.pem
|
||||
```
|
||||
|
||||
底层 API 是 `licensing.Issue(privKey, sub, tier, maxDevices, ttl)`(见 [`licensing/server.go`](licensing/server.go))。一个开箱即用的 CLI 包装在独立仓库 [`yama-issue-token`](https://github.com/yuanyuanxiang/yama-issue-token)(go.mod `replace` 指向本仓库的 `licensing` 包),用法:
|
||||
|
||||
```bash
|
||||
yama-issue-token -priv license_priv.pem -sub acme-corp -tier paid -max 100 -days 365
|
||||
```
|
||||
|
||||
| Tier | max_devices 默认 | 备注 |
|
||||
| ---- | ---------------- | ---- |
|
||||
| `trial` | 20(JWT 未指定时) | 移植 C++ 反代理 RTT 逻辑 |
|
||||
| `paid` | JWT 必须显式指定 | 长 TTL token |
|
||||
|
||||
## 使用示例
|
||||
|
||||
完整的 TCP + Hub + Web 集成示例就是 [`cmd/main.go`](cmd/main.go),那是程序入口本身、也是最权威的范例 —— 包含 handler 装配、hub 注册、web HTTP/WS 服务、信号优雅关闭等。
|
||||
|
||||
Reference in New Issue
Block a user