mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-23 11:38:09 +09:00
dev was ~149 commits ahead of the last release with no changelog and several stale docs. This brings the user-facing docs in line with what actually ships. New: - CHANGELOG.md — curates the unreleased panel work (agent fleet, plugins, status pages, cloud provisioning, RHEL support, installer hardening). - agent/CHANGELOG.md — the agent release workflow links to this on every published release; it was a broken link. Now documents the agent's independent release line. - SECURITY.md — vulnerability reporting policy + agent trust model (agent.key is host-equivalent; Features.Exec off by default; single-worker). Fixes: - Stale version stamps: docs/README.md and docs/API.md said 0.9.0 -> 1.6.7. - Supported OS: README/INSTALLATION said Ubuntu/Debian only; installer also covers Fedora and RHEL/Rocky/AlmaLinux. - Dead links/placeholders: API.md wiki link, INSTALLATION.md "yourusername". - Windows agent + short-code pairing were undocumented; added to README, agent/README.md, and docs/pairing.md (incl. corrected host-stable credential-key derivation — USERNAME was dropped in agent 1.6.14). - Documented the single gevent-websocket-worker deployment constraint in CLAUDE.md and docs/ARCHITECTURE.md. - CONTRIBUTING.md: removed reference to a non-existent frontend test suite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
155 lines
6.3 KiB
Markdown
155 lines
6.3 KiB
Markdown
# Agent Pairing (Short-Code Flow)
|
|
|
|
ServerKit supports two ways to attach an agent to the panel:
|
|
|
|
1. **Pair (recommended)** — install the agent once, then claim it from the panel
|
|
with a 6-character rotating code + your operator passphrase. RustDesk-style.
|
|
2. **Token install (legacy)** — generate a registration token in the panel,
|
|
paste it into a curl/PowerShell one-liner. Still supported.
|
|
|
|
This document covers the pair flow.
|
|
|
|
## Why pairing?
|
|
|
|
The pre-shared registration token approach has several downsides:
|
|
|
|
- The token is a long-lived secret embedded in a copy-pasted shell command.
|
|
- If anyone observes the install command, they can pre-empt the registration.
|
|
- Re-pairing after a wipe means generating a fresh token, repeating the dance.
|
|
|
|
Pairing replaces this with:
|
|
|
|
- A **per-machine passphrase** the operator sets once on the agent host.
|
|
- A **rotating 6-character pair code** the agent displays (CLI banner / tray).
|
|
- A **public-key fingerprint** that's verifiable out-of-band.
|
|
|
|
The panel claims the agent only after both the code and passphrase match.
|
|
|
|
## Operator flow
|
|
|
|
### 1. On the target machine
|
|
|
|
After installing the agent (e.g. via the install script, package, or MSI), run:
|
|
|
|
```bash
|
|
serverkit-agent pair --server https://panel.example.com
|
|
```
|
|
|
|
You'll be prompted for a passphrase (4+ characters). Pick something memorable —
|
|
you only need to type it once on the panel side. The agent then:
|
|
|
|
- Generates an Ed25519 keypair (encrypted at rest with the machine key).
|
|
- Submits its public key + bcrypt'd passphrase to the panel.
|
|
- Prints a banner with the rotating 6-char pair code and the public-key fingerprint:
|
|
|
|
```
|
|
┌──────────────────────────────────────────────┐
|
|
│ ServerKit Agent — Pairing │
|
|
├──────────────────────────────────────────────┤
|
|
│ Pair code: ABC-123 │
|
|
│ Fingerprint: 1A2B3C4D5E6F7A8B │
|
|
├──────────────────────────────────────────────┤
|
|
│ 1. Open ServerKit panel → Add Server │
|
|
│ 2. Enter the pair code + your passphrase │
|
|
│ 3. Verify the fingerprint matches │
|
|
└──────────────────────────────────────────────┘
|
|
Waiting for claim...
|
|
```
|
|
|
|
The pair code rotates every ~5 minutes until the agent is claimed.
|
|
|
|
### 2. In the panel
|
|
|
|
Go to **Servers → Add Server** and select **Pair existing agent**. Enter:
|
|
|
|
- The 6-character pair code
|
|
- The passphrase you set
|
|
- (Optional) A friendly name and group
|
|
|
|
Verify the fingerprint shown after lookup matches what the agent printed.
|
|
Click **Pair Agent**. The agent receives credentials over its existing
|
|
long-poll, saves them, and prints `✓ Pairing successful!`.
|
|
|
|
### 3. Start the agent
|
|
|
|
```bash
|
|
sudo systemctl start serverkit-agent # Linux
|
|
# or use Services.msc on Windows
|
|
```
|
|
|
|
The agent connects to the panel and the server flips from "Pending" to "Online".
|
|
|
|
## Headless / unattended pairing
|
|
|
|
For Docker, Kubernetes, or unattended provisioning:
|
|
|
|
```bash
|
|
SERVERKIT_AGENT_PASSPHRASE='hunter2' \
|
|
serverkit-agent pair --server https://panel.example.com --headless
|
|
```
|
|
|
|
You still need an operator to claim the code in the panel — the headless flag
|
|
just suppresses interactive prompts.
|
|
|
|
## Local dev / ngrok
|
|
|
|
If you're testing the panel locally, expose it via ngrok and set:
|
|
|
|
```bash
|
|
export SERVERKIT_PUBLIC_URL=https://abcd1234.ngrok.app
|
|
```
|
|
|
|
Use that URL as the `--server` flag. For self-signed TLS in dev,
|
|
`SERVERKIT_INSECURE_TLS=true` skips cert verification on the agent side
|
|
(don't use this in production).
|
|
|
|
## Security notes
|
|
|
|
- Pair codes are 6 characters from a 31-symbol alphabet
|
|
(Crockford-ish base32 minus 0/O/1/I/L). Codes rotate ~every 5 minutes.
|
|
- Passphrases are bcrypt'd on the panel before storage.
|
|
- Failed claim attempts trigger per-enrollment exponential backoff
|
|
(60s → 5 min cap) plus per-IP rate limits (5 claims / 10 minutes).
|
|
- All claim/lookup events are written to the audit log.
|
|
- Enrollments expire 24h after creation; un-claimed records are pruned hourly.
|
|
- The agent's keypair is encrypted at rest with a **host-stable** key derived
|
|
from `/etc/machine-id` + hostname (Linux) or hostname + COMPUTERNAME (Windows).
|
|
The key is intentionally independent of the logged-in user so the Windows
|
|
service (running as `LocalSystem`) can decrypt credentials written during
|
|
user-context pairing — mixing in the Windows username broke this and was
|
|
removed in agent 1.6.14 (a legacy USERNAME-based key is retained only for
|
|
one-time migration). Treat the stored key file as a host-equivalent secret;
|
|
see [../SECURITY.md](../SECURITY.md) for the full agent trust model.
|
|
- Compare the displayed fingerprint side-by-side before claiming. Mismatch
|
|
means the wrong agent — abort and re-run `pair`.
|
|
|
|
> **Treat the stored credential/key file as a host-equivalent secret.** Combined
|
|
> with the agent's remote-execution capability, recovering it is equivalent to
|
|
> controlling the host. See [../SECURITY.md](../SECURITY.md) for the full agent
|
|
> trust model.
|
|
|
|
## API reference
|
|
|
|
| Endpoint | Purpose | Auth |
|
|
| --- | --- | --- |
|
|
| `POST /api/v1/pairing/enroll` | Agent submits pubkey + passphrase | none |
|
|
| `POST /api/v1/pairing/code/refresh` | Agent rotates its pair code | enrollment headers |
|
|
| `POST /api/v1/pairing/code/freeze` | Agent freezes/unfreezes rotation | enrollment headers |
|
|
| `GET /api/v1/pairing/poll` | Agent long-polls for claim (≤25s) | enrollment headers |
|
|
| `POST /api/v1/pairing/lookup` | Panel resolves code → fingerprint | JWT |
|
|
| `POST /api/v1/pairing/claim` | Panel claims an enrolled agent | JWT |
|
|
|
|
Headers used by the agent:
|
|
- `X-Enrollment-Id`: opaque enrollment identifier
|
|
- `X-Enrollment-Secret`: long-lived secret returned at enroll time
|
|
|
|
## Comparison with token install
|
|
|
|
| | Pair | Token install |
|
|
| --- | --- | --- |
|
|
| Out-of-band step | passphrase typed into panel | token pasted into shell |
|
|
| Compromise window | 5 min (rotating code) | 24h (token TTL) |
|
|
| Re-pair after wipe | re-run `pair` | regenerate token |
|
|
| Headless friendly | yes (`--headless` + env) | yes (curl one-liner) |
|
|
| Recommended? | ✅ | only for legacy/scripted flows |
|