Lion Cipher Lab
Encrypt a service's secrets at rest with systemd-creds and LoadCredential
- Cyber / Security Engineer
- Intermediate
- ~45 min
- Free · no account required
Why it matters
Secrets end up in plaintext because the easiest way to pass one to a service is also the worst. A SYSTEMD UNIT (the file that tells systemd — the program that starts and supervises everything on a modern Linux box — how to run a service, i.e. a long-running background program) can hand a password to its process with `Environment=DBPASS=hunter2` or by pointing `EnvironmentFile=` at a file full of `KEY=value` lines. It works on the first try, so it spreads everywhere. The problem is that an ENVIRONMENT VARIABLE (a named value the operating system hands to a process when it starts) is one of the…
What you'll learn
- explaining why a secret placed in Environment= or an EnvironmentFile= is exposed three different ways — in `systemctl show -p Environment`, in the plaintext file on disk, and in /proc/<pid>/environ of the running process
- building a tiny throwaway systemd service that reads a secret the insecure way, so you have a real leak to measure before you fix it
- encrypting a secret at rest with `systemd-creds encrypt --name=... - file.cred`, and understanding host-key vs TPM2 vs host+tpm2 sealing
- delivering the secret to a unit with LoadCredential= (plaintext file) and with SetCredentialEncrypted= / ImportCredential= (encrypted blob), and reading it at runtime from $CREDENTIALS_DIRECTORY
- proving the fix with the SAME probes: the secret is no longer in `systemctl show -p Environment`, not in /proc/<pid>/environ, and the on-disk blob is unreadable ciphertext
- knowing where the decrypted credential actually lives at runtime — a private, 0700, in-memory (tmpfs/ramfs) per-unit directory that is torn down when the service stops
- stating the honest limits: host-key sealing is not portable and does not stop a live local root; TPM binding only raises the bar against offline disk theft — and naming when NOT to rely on this control
- reaching for the right companion layer (sandboxing, a real secrets manager) when the threat model includes a root-level adversary
Steps in this lab
- The one idea: the environment is the worst place to put a secret
- Confirm the ground truth: systemd 250+ and the tools exist
- Stand up the demo service the INSECURE way (the finding)
- Measure the leak three ways — this is the BEFORE half of the lab
- Encrypt the secret at rest with systemd-creds
- Deliver it to the service and read it from $CREDENTIALS_DIRECTORY
- Re-run the SAME three probes — this is the AFTER half
- The honest negative: what this does NOT protect against
- Do it better — the workflow worth keeping, and clean up