Both carried a topic outside the six the index knows, so neither had a place to be read in — 0067 had none at all. Both are 'the tiers', beside 0036 (bootstrap ends at a usable mesh) and 0007 (connectivity), which is what they extend. 0067 cited 0041 for tier 0's property; on this trunk 0041 is events, and the record it meant is 0005. A citation that resolves to the wrong record reads as corroboration, which is worse than a dead link. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
59 lines
3.2 KiB
Markdown
59 lines
3.2 KiB
Markdown
---
|
|
status: open
|
|
opened: 2026-09-10
|
|
located-in: []
|
|
fixed-by:
|
|
amended-design:
|
|
---
|
|
|
|
# 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.
|