Skip to content

Latest commit

 

History

History
154 lines (114 loc) · 6.34 KB

File metadata and controls

154 lines (114 loc) · 6.34 KB

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:

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

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:

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:

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 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 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