docs: INCIDENT — I printed the console password to the transcript
gate / gate (push) Has been cancelled
gate / conformance (push) Has been cancelled

A kubectl jsonpath with {range .data}{@}{end} dumped the unifi Secret's data
map, putting the base64 console-password in the session transcript. Exactly the
constraint I put in every agent brief, broken by my own careless verification
command.

Exposure bounded: 0 commits contain it (git log -S), the capturing scratch file
was shredded, and a repo-wide grep finds nothing on disk. Critically it is not
yet a live credential — the Secret was created two days after the running VMI,
whose volume list is rootdisk and cloudinit only, so the UOSDATA disk was never
attached, install-uos never ran, and the ubuntu account is still locked.

Action recorded with the ordering that matters: rotate console-password BEFORE
recreating the VMI, so the leaked value never becomes valid.

Lesson: checking a Secret's existence never needs its values. Listing .data's
keys or .metadata.name answers the question; dumping .data answers one nobody
asked.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
jakman85andClaude Opus 5 committed 2026-09-18 21:39:42 -05:00
1 parent 14d5ccbadd
commit 6a4dc0ada7
1 file changed
+36
+36
View File
@@ -4229,3 +4229,39 @@ both fields as literal `REPLACE-ME`, unlike every other `*.sops.yaml` here. `sop
failed with "sops metadata not found", and Flux would have created the Secret containing the
placeholder strings. Encrypted in place, round-trip verified. Both fields need setting, not just the
password — I had omitted `installer-url` from the operator instructions.
---
## 2026-09-18 — INCIDENT (mine): I printed the console password to the transcript
**What happened.** Verifying that the `unifi-uos-installer` Secret had reached the cluster, I ran a
`kubectl get secret -o jsonpath` that included `{range .data}{@}{end}`. That dumps the whole `data`
map, so the base64 of `console-password` was printed to the session transcript. This is precisely the
constraint I have written into every agent brief in this repo — *never print decrypted secrets* — and I
broke it myself with a careless jsonpath while checking someone else's work.
**Exposure, stated precisely rather than minimised or inflated:**
- The value reached the session transcript, which I cannot scrub.
- It did **not** reach git: `git log --all -S<value>` returns **0 commits**.
- The scratch file that captured it (`tasks/*.output`) was shredded; a repo- and scratch-wide grep for
the string now returns nothing on disk.
**Why it is not yet a working credential — the one thing that limits this.** The Secret was created
`2026-09-14T07:26:43Z`; the running VMI was created `2026-09-12T20:48:30Z`, and its live volume list is
only `rootdisk` and `cloudinit` — **the `UOSDATA` disk is not attached to the running VMI at all**. So
the guest has never read the Secret, `install-uos` has never executed, and the `ubuntu` account remains
locked. The leaked string authenticates nothing today. It would only become valid when the VMI is
recreated against the new spec.
**Required action, and the ordering matters:** rotate `console-password` in the SOPS Secret **before**
recreating the VMI. Then the leaked value never becomes a working password anywhere:
sops apps/vms/unifi/unifi-uos-installer.sops.yaml # set a NEW console-password
git commit && git push # let Flux update the Secret
kubectl -n unifi delete vmi unifi # only after the rotation has landed
**The lesson, generalised.** Reading a Secret to check *existence* never requires reading its
*values*. `-o jsonpath='{.metadata.name}'` or listing `.data`'s KEYS answers "did it land?"; dumping
`.data` answers a question nobody asked. Every previous secret check in this session used a
keys-only or boolean form — this one regressed because I appended a convenience field to an
otherwise-safe command without re-reading what it expanded to.