Implement signed API communication to improve security
build / build-linux-amd64 (push) Successful in 1m50s

This commit is contained in:
2026-05-22 12:50:42 +02:00
parent 21b25bcc1b
commit 475da0e950
12 changed files with 906 additions and 24 deletions
+80 -1
View File
@@ -303,6 +303,85 @@ keys and what each one does.
---
## Agent API signing (per-peer)
`POST /api/heartbeat`, `POST /api/sysinfo`, and
`POST /api/unattended-password` are the three agent-facing endpoints
that write per-device state. Stock RustDesk and managed builds
(hello-agent) both call the first two; only managed builds use the
third. Each peer row has a `managed` flag that gates whether the
server requires a per-request Ed25519 signature on these endpoints;
everything else (`/api/peers`, `/api/ab/*`, audit, recordings, OIDC,
etc.) is unaffected. See [AGENT-API-AUTH.md](AGENT-API-AUTH.md) for
the full out-of-scope list.
| `peer.managed` | Heartbeat / sysinfo behaviour |
|----------------|----------------------------------------------------------------------------------------|
| `0` (default) | Unsigned posts accepted (stock-client compatible). Signed posts still verified. |
| `1` | Signature required; unsigned posts return 401. First valid sig auto-promoted to here. |
Default is `0` after the migration, so **stock RustDesk clients are not
affected by the rollout** — they keep posting unsigned, the server keeps
accepting. The first valid signature the server sees from a peer is the
TOFU promote: that peer's `managed` flips to `1` for good, and unsigned
requests claiming that `id` are rejected from then on.
The wire format and verification details live in
[AGENT-API-AUTH.md](AGENT-API-AUTH.md). What you need to know to operate:
### Dashboard
The Devices page has a per-row **Auth** column:
- *Signed* (emerald badge) — `peer.managed = 1`. The peer's heartbeat
and sysinfo posts must carry a valid signature; spoofed unsigned
requests are rejected.
- *Unsigned* (slate badge) — `peer.managed = 0`. Legacy path. Anyone
who knows the id+uuid can post inventory and heartbeats as this
device.
The row's action menu has two new entries (mutually exclusive based on
current state):
- **Require signed API** — flips `managed` to 1 (no confirm — it
strengthens security). Useful for pre-enrolling a peer record
before the agent has booted, or for force-locking a peer if you
want to fail fast when an agent is not signing yet.
- **Allow unsigned API** — flips `managed` to 0 (confirm dialog,
because this reopens the spoofing surface). Use when a managed
agent has been uninstalled and replaced with stock RustDesk on the
same hardware.
### API
`PUT /api/peers/:id/managed` with body `{"managed": true|false}`, gated
on the `is_admin` flag of the calling session, returns
`{"ok":true,"managed":<bool>}`. Same effect as the dashboard toggle —
the dashboard handler just calls this internally after reading the
current value to avoid stale-toggle races.
### Operational notes
- **Mixed fleets are fine.** Stock and hello-agent clients can target
the same hbbs. The gate is per-peer, not per-deployment.
- **Replacing hello-agent with stock RustDesk on a device.** The
device's `peer.managed` is stuck at 1; the stock client doesn't
sign and will start getting 401s. Either re-deploy a signing build
*or* flip the peer back to Unsigned in the dashboard.
- **TLS still recommended.** Signing protects against id+uuid spoof,
not against the unsigned-by-default endpoint surface elsewhere
(`/api/login`, `/api/record`, dashboard) — those still rely on
whatever TLS termination is in front of hbbs. See *TLS deployment*
earlier in this doc.
- **Clock skew tolerance is ±5 minutes.** If a host's clock drifts
past that, heartbeat starts failing 401. Keep NTP healthy on
managed peers; the server's clock is the canonical one.
- **The replay cache lives in-memory only.** A hbbs restart clears
it. The 5-minute timestamp window bounds the worst-case replay
exposure across restarts.
---
## Address books
- **Personal books** are owned per-user and managed from the user's desktop client. The dashboard surfaces them read-only.
@@ -326,7 +405,7 @@ If you set `--ab-legacy-mode=on`, `/api/ab/personal` 404s and clients fall back
| `/admin/oidc/providers` | none | JSON list of enabled providers, used by login.html |
| `/admin/login/oidc/:name` | none | Starts admin OIDC flow (302s to IdP) |
| `/admin/pages/users` | cookie + admin | Users page fragment (incl. inline edit-profile / password-reset / TOTP-disable per row) |
| `/admin/pages/devices` | cookie + admin | Devices (incl. delete) |
| `/admin/pages/devices` | cookie + admin | Devices (incl. delete, force-disconnect, force-sysinfo, toggle managed-auth — see [AGENT-API-AUTH.md](AGENT-API-AUTH.md)) |
| `/admin/pages/groups` | cookie + admin | Device groups |
| `/admin/pages/strategies` | cookie + admin | Strategy management |
| `/admin/pages/address-books` | cookie + admin | Personal + shared books |