Repository navigation
feat(secrets): remove an age recipient and re-key, the other half of add-recipient (#294) - #658
Merged
Merged
Conversation
…add-recipient (#294) #294's second key, age1cutv5..., was never kept. Its replacement, age19mkg76v0..., has been a recipient since 6de8509, and nothing could take the lost key out without hand-editing .sops.yaml. add-recipient.sh calls that a trap. scripts/remove-recipient.sh, with `make secrets-remove-recipient PUBKEY=`, mirrors add-recipient.sh. It checks the key is in both the ciphertext and the policy, backs up .sops.yaml, removes the line, runs `sops updatekeys`, and then checks the ciphertext rather than the exit status. If the re-key did not happen it rolls back. Removing the last key of a list also drops the trailing comma a hand-edit would leave, which sops reads as an empty recipient. It refuses three ways a removal loses the recovery it protects: taking out this host's own key, leaving fewer than two recipients (ADR-0024), and leaving only recipients key-recipients.prom has never recorded as proved. Six fixtures cover the .sops.yaml edit: middle, last, first, absent, only-in-rule, and one key in two rules. Running the edit against a copy of the live .sops.yaml removes exactly the age1cutv5... line. back-up-the-age-key.md now describes a replacement as add, then remove, then prove. Refs #294 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gerrrt
added a commit
that referenced
this pull request
Sep 23, 2026
#652 landed ADR-0052, so with this branch's ADR-0053 the repository has 53. The changelog conflict keeps both entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#294's second key,
age1cutv5…, was never kept. Its replacement,age19mkg76v0…, has been a recipient since 6de8509 (2026-09-22). Taking the lost key out means editing.sops.yamland re-keying in one step, and there was no tool for it.add-recipient.shcalls hand-editing a trap.What this adds
scripts/remove-recipient.sh, withmake secrets-remove-recipient PUBKEY=age1…, mirrorsadd-recipient.sh:.sops.yaml. If only one lists it, it stops and says so..sops.yaml, removes the line, and runssops updatekeys. Then it checks the ciphertext: the removed key must be gone and every other recipient must still be there. If not, it rolls back. (updatekeysexits 0 when you answer "no" at its prompt.)key-recipients.prom, which drops the removed key's row and keeps every other key's proof.Refusals: each one is a way a removal would lose the recovery it's meant to protect.
key-recipients.promhas never recorded as proved.The
.sops.yamledit handles removing the last key of a list: the new last line loses its trailing comma, which sops would otherwise read as an empty recipient. Six fixtures under--self-testcover it (middle, last, first, absent, only key in its rule, and one key in two rules), andself-tests.shdiscovers them, so they run in CI. Running the edit against a copy of the live.sops.yamlremoves exactly theage1cutv5…line.back-up-the-age-key.mdnow describes a replacement as add, then remove, then prove.After merge, on
prometheus(main checkout, where the live key is)Commit and open a PR with those two files. The refusal about unproven keys won't trigger:
age1yrdu996…was proved on 2026-09-03.Then #294's last step is proving
age19mkg76v0…, notage1cutv5…. That happens at the visit, together with #573's offsite copy:make secrets-verify-backup KEY=<path>should print-- recipient age19mkg76v0…and anokline.Removal doesn't reach git history. Every old ciphertext still opens to
age1cutv5…. The key was lost, not exposed, so no credential rotation is needed. If that ever changes, the runbook's "Revocation is still rotation" section applies.Checks
--self-test(6 fixtures), shellcheck, editorconfig andcheck_docs.pyall pass. Two local failures aren't from this PR: shellcheck 0.11's SC2329 incheck_hardened_boot.sh, and the/tmpfixture inbackup-offsite.shon a host where/tmpis a tmpfs.Refs #294
🤖 Generated with Claude Code