Files
hq/04-ISSUES/041-a-sealed-credential-ends-up-in-the-process-environment/00-report.md
T
jschoubben 61f61b5d06 Reconcile the open issues against main
An audit of the six code repositories found eleven open issues fixed on main
with commits and beds to show (025, 027, 033, 036, 037, 040, 045, 047, 050,
052, 053), three partly (007, 026, 035), nine not (020, 031, 041, 046, 049,
054, 064, 065, 066) and one whose fix would live outside those repos (006).
Resolved ones name their evidence; partly ones say what remains; 041 records
that the exposure has widened since it was reported.
2026-09-21 01:52:53 +02:00

3.7 KiB

status, opened, located-in, fixed-by, amended-design
status opened located-in fixed-by amended-design
open 2026-09-10

041 — A credential the mesh took care to seal ends up in the process environment

Symptom

The mesh generates a module's own secret, seals it to the machine and discards the plaintext — it cannot read the value back even if asked. The host unseals it into a file the module names, at 0600.

Then the module hands it to its container as an environment variable, and the runtime puts it where anything on that machine that can talk to the runtime can read it: docker inspect prints it, and /proc/<pid>/environ holds it for the life of the process.

Observed while making the control plane an ordinary module. Its store connections were moved to files, read by a …_FILE variable naming the path. Its broker credentials have no such variable, so they are still delivered through an env-file — which the runtime turns into exactly the environment above. Same credential handling, same machine, two different exposures, decided by whether the program that reads it happens to accept a path.

Why this matters

The care taken elsewhere is what makes this stand out. Sealing to a machine and discarding the plaintext is expensive and deliberate: it exists so that a credential is readable only where it is used. Handing that same value to the runtime as an environment variable gives it back to anything that can run inspect — and inspect is a routine operation. It lands in support output, in captured logs, in a screenshot of a terminal, and in any tooling that dumps container state.

It is not a module's mistake. Nothing in the manifest format is being misused: placing a secret into an env-file with ${secret:…} is a supported shape and other modules use it. So each module is correct on its own, and the property — a sealed credential is not readable by everything on the machine — holds or fails per variable, by accident of what each program accepts.

And the two halves now disagree inside one module. The control plane reads its store connection from a file and its broker credential from the environment. A reader cannot tell from the manifest which secrets are protected from inspect and which are not, because the manifest looks the same either way.

Open questions

  • Should every program the mesh runs accept a path for anything secret — a …_FILE twin as a convention rather than a thing each program decides? That is a small change in several programs and a large one in what the manifest can promise.
  • Should the manifest layer refuse ${secret:…} inside a container's env, or inside an env-file, once a path-shaped alternative exists? A rule nothing enforces is the shape this repository keeps finding.
  • Is there a case where the environment is genuinely the only channel — a program that cannot be changed and reads no file? If so, what should the mesh say about that module, out loud, rather than treating it as equivalent?
  • What is the actual reach of the exposure on a node — which identities can talk to the container runtime, and is that set smaller than "anything running as the operator"? The answer decides whether this is a hardening item or something sharper.

Reconciled 2026-09-21

Still open, and worse than reported: the controller's own manifest now delivers its store connections and its broker credentials through an env-file, so both halves reach the process environment; 28 catalogue manifests use env-file for a secret, and nothing in the catalogue engine refuses a ${secret:…} placeholder in one. The vault work of ADR 0085 rests on the seal this weakens.