Host credentials can rotate themselves without an admin request, locking out the real agent #97

Closed
opened 2026-10-09 15:31:09 -04:00 by goddard · 1 comment
Collaborator

A host credential can rotate itself at any time without an admin asking for it, which revokes every other credential for that host.

  • agent/credentials/+server.ts:9 calls rotateCredential on any authenticated POST.
  • index.ts:358-370 revokes all of the host's credentials and issues the one the caller supplied.
  • Nothing checks that an admin queued credentials.rotate (api/hosts/[id]/credentials/+server.ts:11), and credentials never expire (index.ts:339-351 only checks revoked_at).

Someone holding a copied credential, from a backup for example, can rotate once and keep exclusive access. The real agent just starts getting 401s, with no conflicting heartbeats to show something is wrong.

Fix

  • Accept /api/agent/credentials only while a credentials.rotate operation for that host is open, and close the operation when rotation completes
A host credential can rotate itself at any time without an admin asking for it, which revokes every other credential for that host. - `agent/credentials/+server.ts:9` calls `rotateCredential` on any authenticated POST. - `index.ts:358-370` revokes all of the host's credentials and issues the one the caller supplied. - Nothing checks that an admin queued `credentials.rotate` (`api/hosts/[id]/credentials/+server.ts:11`), and credentials never expire (`index.ts:339-351` only checks `revoked_at`). Someone holding a copied credential, from a backup for example, can rotate once and keep exclusive access. The real agent just starts getting 401s, with no conflicting heartbeats to show something is wrong. **Fix** - [x] Accept `/api/agent/credentials` only while a `credentials.rotate` operation for that host is open, and close the operation when rotation completes
goddard added the
priority
low
type/hardening
labels 2026-10-09 15:31:09 -04:00
josh was assigned by goddard 2026-10-09 15:31:09 -04:00
Author
Collaborator

Reviewed 1b908f891f. All fix items are complete; closing this issue.

Checklist

  • Administrator authorization. hosts/index.ts:888 requires an open rotation operation for this host before issuing a new credential; completion and revocation occur in the same transaction.
  • Retries and rollback. Tests cover absent/other-host/other-kind/terminal operations, all accepted open states, idempotent acknowledgment, one-time authorization consumption and rollback if completion fails.

Review notes

  • Reviewed current EC main at fa0baa8db5. Full application suite passed: 230 files, 3,120 tests, including migration-memory coverage; no failures or skips.
  • Python agent suite: 332 tests passed, no skips. Type/style checks: zero errors and warnings.
  • Acknowledging an already-active replacement does not rotate again or consume another authorization.
Reviewed 1b908f891fc6c6bba7e7b8a63d1400a2dbc86df8. All fix items are complete; closing this issue. **Checklist** - [x] **Administrator authorization.** `hosts/index.ts:888` requires an open rotation operation for this host before issuing a new credential; completion and revocation occur in the same transaction. - [x] **Retries and rollback.** Tests cover absent/other-host/other-kind/terminal operations, all accepted open states, idempotent acknowledgment, one-time authorization consumption and rollback if completion fails. **Review notes** - Reviewed current EC main at fa0baa8db5f7629de3f58f2e6bf8da0568018be0. Full application suite passed: 230 files, 3,120 tests, including migration-memory coverage; no failures or skips. - Python agent suite: 332 tests passed, no skips. Type/style checks: zero errors and warnings. - Acknowledging an already-active replacement does not rotate again or consume another authorization.
Sign in to join this conversation.