fix(secrets): stop the editor keeping a decrypted copy of the secrets - #41
Merged
Conversation
`make secrets-edit` ran `sops` directly. sops decrypts to a temp file and opens $EDITOR on it, and a vim or neovim with `undofile` set then writes that buffer — the decrypted secrets — into a permanent undodir. sops shreds its own temp file on exit; nothing shreds the undo file. Found in the wild on the monitoring host: three files in ~/.local/state/nvim/undodir/ named %tmp%<pid>%observability.sops.yaml, mode 664, on an unencrypted disk, holding the live SNMP community strings for pfSense, the APC NMC and iLO. They had been there since the last `make secrets-edit` and nothing in the repo knew. Shredded, and recorded in docs/security.md's historical exposure table. Not rotated: the exposure never left the host and never reached git. scripts/secrets-edit.sh now silences the editor before sops hands it plaintext — no undo file, no swap file, no backup, no viminfo/shada — for the vim family, and warns for anything else rather than pretending to have hardened it. Measured: sops joins $EDITOR and the temp path and runs them through `sh -c`, so the flags survive; a baseline edit reproduces the undo file and a hardened one does not. The same mechanism was in the age-key runbook, which told you to transcribe a paper backup with `vi /dev/shm/restore-test.txt` — writing the *private key* to a persistent undo file that the following `shred -u` does not remove. Both verification flows now use `cat >`, and the runbook says why. It also gains a password-manager flow, since that is the destination it recommends, and stops lumping end-to-end-encrypted vaults in with plaintext cloud sync, which contradicted its own recommendation two sections earlier. Incidental, in the same target: sops exits 200 when the editor quits without changing anything, which surfaced as `make: *** [secrets-edit] Error 200` and read like a broken target. Mapped to success; every other status passes through. Refs #11
This was referenced Aug 20, 2026
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.
Found while working #11.
The leak
make secrets-editransopsdirectly. sops decrypts to a temp file and opens$EDITORon it — and a vim or neovim withundofileset then writes that buffer, the decrypted secrets, into a permanent undodir. sops shreds its own temp file on exit. Nothing shreds the undo file.Three of them were sitting on the monitoring host:
holding the live SNMP community strings for pfSense, the APC NMC and iLO, in plaintext, on an unencrypted disk, since the last
make secrets-edit. Confirmed by matching the live values fromsops -dagainst the files inside a shell variable — never printed. Shredded, and recorded indocs/security.md's historical exposure table.Not rotated. The exposure never left the host and never reached git, and rotating SNMP communities means revisiting four devices — including the switch that
SECURITY.mdalready records as refusing to drop its old community.The fix
scripts/secrets-edit.shhardens the editor before sops hands it plaintext:Measured rather than assumed — sops joins
$EDITORwith the temp path and runs the result throughsh -c, so flags and quoting survive:A baseline headless edit reproduces the undo file; the hardened one produces nothing. Non-vim editors get a warning naming what to check, rather than a false claim of having been hardened.
The same bug was in the runbook
docs/runbooks/back-up-the-age-key.mdtold you to transcribe a paper backup withvi /dev/shm/restore-test.txt— writing the private key into a persistent undo file that the very next line'sshred -udoes not remove. The shipped cleanup step gave false assurance.Both verification flows now use
cat >, with the reason stated. Also in that file:Incidental
sopsexits 200 when the editor quits unchanged, which surfaced asmake: *** [secrets-edit] Error 200and read like a broken target. Mapped to success; every other status passes through untouched.secrets/README.mdclaimed "the plaintext never lands on disk unencrypted". That was not true until this commit.Verification
shellcheck scripts/*.shclean;markdownlint-cli20 issues;make validateall checks passedgitleaks v8.24.0 detect --no-git— no leaks foundmake secrets-editend-to-end against the real secrets file, editor receiving the hardening flags, exit 0 on no-opRefs #11
🤖 Generated with Claude Code