[P1] Host credentials have no expiry or per-host revocation #3
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
[claude 2026-07-02] From architecture review (wire/auth). internal/server/hosttoken/hosttoken.go mints host credentials as host_id.HMAC-SHA256(secret,host_id). The HMAC has no timestamp/nonce, so a credential is valid forever and the only revocation is rotating the shared host_secret — which revokes the ENTIRE fleet at once. A single leaked credential = fleet-wide re-enroll.
Fix options: embed an issued-at/expiry in the credential and verify a max-age in hosttoken.Verify; and/or add a per-host token version/generation column so a single host can be revoked by bumping its version. Document the credential-compromise runbook.
Migrated from git-collab issue
9f98faa4be960f05e0a92751228c91a360a57679.Original author: a73x dev@a73x.sh.
Close reason: Fixed by patch
1dfd9755[claude 2026-07-03] Picking this up. Design: credential format v2 'host_id.gen.issued_unix.hmac' (HMAC over the three dotted fields). hosts table gains cred_generation (default 1, guarded ALTER migration for existing DBs); syncsvc verifies HMAC then checks gen against the host row — POST /api/v1/hosts/{id}/revoke-credential bumps gen (audited), giving PER-HOST revocation without rotating the fleet secret. issued_unix enables an optional credential_max_age server config (default off: expiry without an auto-renewal channel would force periodic re-enrolls; revocation is the primary mechanism, max-age is defense-in-depth for operators who want it). Breaking: pre-existing 2-part credentials are rejected -> agents re-enroll once (Phase-1 acceptable, documented). Old sessions: revocation takes effect at next Hello; the server also closes the host's live session on revoke.