Skip to content

D260 — sops runs in a hermetic environment: allowlisted variables, an empty home, the per-KB key file

Decision. Every sops child (Decrypt, Set in internal/sops) gets an environment built from scratch: PATH, TMPDIR, TMP, TEMP, SYSTEMROOT, LANG, LC_ALL copied from the server when set; HOME and XDG_CONFIG_HOME (on Windows also USERPROFILE and APPDATA) pointing at a fresh empty temporary directory removed after the call; then the caller's entries, which carry the per-KB SOPS_AGE_KEY_FILE and win. This applies unconditionally, also when the caller passes no env. Decrypt parses sops stdout only.

Why. sops does not use only the key file it is given: it collects every identity it can find — SOPS_AGE_KEY, SOPS_AGE_KEY_CMD, SOPS_AGE_SSH_PRIVATE_KEY_FILE, the default sops/age/keys.txt under the user config dir, default SSH keys under $HOME/.ssh, cloud KMS / Vault credentials. With cmd.Env = append(os.Environ(), env...) any KB on a multi-KB server, or on a workstation whose shell profile exports SOPS_AGE_KEY, was decryptable with the ambient key whatever sops_age_key_file said. D47's claim that passing the key via env "prevents one KB's age key from contaminating requests on another" was therefore partial: it held for the key Cartographer passes, not for the ambient ones. Separately, CombinedOutput fed any stderr warning of a successful decrypt into the YAML parse. The cost: an operator who relied on an ambient key instead of a matching sops_age_key_file now gets a decrypt failure until the key file is configured.

Alternatives rejected. - Denylist SOPS_* and cloud variables — misses the next identity source sops adds, and does nothing about default files under the real home. - Keep the real HOME — the default keys.txt and ~/.ssh keys stay reachable. Decrypting an existing file and sops set on one never need it: creation rules are read from the KB root, which stays cmd.Dir. - Fall back to the real home when the temp dir cannot be created — silently reopens every ambient key; the call fails instead. - Keep cloud KMS / Vault variables — they are not a supported key source (the config knows only sops_age_key_file, and resolution refuses without it), so dropping them breaks no supported setup.

Consequences. The configured age key file is the only identity sops can use; supporting another key source (KMS, Vault) means adding it to config and to the child environment explicitly, never re-inheriting the server's. Test fakes of sops cannot receive knobs through the test process environment — bake them into the script. secret_set still never surfaces sops diagnostics; Decrypt errors keep sops decrypt <path>: <err>: <stderr>.