You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
🤖 CoderProvisioner: a rotation at the target deletes another provisioner's same-named key #414
Two CoderProvisioner objects that resolve to the same key name in the same Coder organization at the same Coder endpoint delete each other's provisioner key. The key name defaults to metadata.name, and spec.key.name can be set explicitly, so this can happen across namespaces, or with two objects that set the same spec.key.name.
When provisioner P reconciles a target where Coder already holds a key with P's key name, EnsureProvisionerKey returns that key without plaintext. P then deletes the key by name and creates its own. If the key belongs to provisioner Q, Q's Secret now holds a deleted key. Q's reconcile does not notice, because its Secret is still non-empty and its spec did not drift, so Q's provisioner daemons fail to authenticate until something else rotates Q's key.
Where
On main (internal/controller/coderprovisioner_controller.go at ef86c32), the by-name delete followed by a create runs in these paths:
Secret loss, "provisioner key exists in coderd but secret is missing, rotating key to recover" (lines 427-430);
metadata backfill for status from older operators (line 519).
None of them checks that the key ID is one that this provisioner recorded.
Scope note
PR #413 (issue #259) closes the same hazard for the cleanup of a status.pendingKeys entry without key IDs: that cleanup now deletes no key and reports a same-named key with the UnverifiedKeyKept condition. The target paths above are older than #413 and are not changed by it.
Possible direction
Delete by name at the target only when the existing key's ID is one that this provisioner recorded (status.provisionerKeyID or a status.pendingKeys entry). Otherwise do not delete it: set a condition that names the conflicting key, and stop before the create. Status from older operators (no IDs) and a create whose answer was lost both need a path, for example a documented manual delete.
Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh
Activity