22
33## What They Are
44
5- systemd credentials are encrypted, service-scoped secrets injected into
6- services at startup by the service manager. They are the systemd-native answer
7- to the question: * how does a service get a secret without storing it in a
8- config file?*
5+ systemd credentials are service-scoped secrets injected into services at
6+ startup by the service manager. They can optionally be encrypted at rest.
7+ They are the systemd-native answer to the question: * how does a service get
8+ a secret without storing it in a config file?*
99
10- Introduced in systemd v250 (released December 2021, standard in RHEL 9,
11- Debian 12, Ubuntu 22.04+), credentials are available on every current
12- enterprise Linux distribution .
10+ Introduced in systemd v250 (released December 2021), credentials are
11+ available on distributions shipping systemd v250 or newer — including
12+ RHEL 9+, Debian 12+, and Ubuntu 22.04+ .
1313
1414## How They Work
1515
@@ -20,9 +20,9 @@ enterprise Linux distribution.
2020systemd-creds encrypt --name=vault-token - /etc/srvguard/vault-token.cred
2121```
2222
23- The credential is encrypted using the machine's TPM2 binding or, on machines
24- without TPM2, a machine-specific key derived from ` /var/lib/systemd/credential.secret ` .
25- The resulting ` .cred ` file is unreadable on any other machine .
23+ The credential is encrypted using TPM2 (if available) or a machine-specific
24+ secret stored in ` /var/lib/systemd/credential.secret ` . The resulting ` .cred `
25+ file cannot be decrypted elsewhere unless that machine secret is also copied .
2626
2727** Delivery (automatic, at service start):**
2828
@@ -34,44 +34,48 @@ LoadCredentialEncrypted=vault-token:/etc/srvguard/vault-token.cred
3434
3535systemd decrypts the credential before starting the service and places the
3636plaintext in ` $CREDENTIALS_DIRECTORY/<name> ` . The directory is:
37- - On a ` tmpfs ` mount (memory only, never hits disk)
37+ - On a ` tmpfs ` mount — memory-backed, not intentionally written to disk
38+ (though kernel memory may still be captured in swap or hibernation images)
3839- Owned by the service user
39- - Mode ` 0500 ` — no other user or service can read it
40- - Destroyed when the service stops
40+ - Mode ` 0500 ` — inaccessible to other unprivileged users and services
41+ - Removed when the service stops as part of systemd cleanup
4142
4243The service reads the secret from ` $CREDENTIALS_DIRECTORY/vault-token ` . By the
4344time the service sees it, decryption is complete and the secret is in memory.
4445
4546## Security Properties
4647
4748** Encrypted at rest:** the ` .cred ` file stored in ` /etc/srvguard/ ` is
48- ciphertext. Even full disk access does not yield the secret.
49+ ciphertext. Disk access alone does not reveal the secret unless the
50+ machine-specific key (` credential.secret ` or TPM2) is also compromised.
4951
50- ** Machine-bound:** the encryption key is derived from TPM2 or a machine secret
51- that does not leave the host. The credential cannot be decrypted on another
52- machine.
52+ ** Machine-bound (with TPM2):** when TPM2 is available, the encryption key
53+ is hardware-bound and the credential cannot be decrypted on another machine.
54+ Without TPM2, the binding is only as strong as the protection of
55+ ` /var/lib/systemd/credential.secret ` .
5356
5457** Memory-only at runtime:** ` $CREDENTIALS_DIRECTORY ` lives in tmpfs. The
55- plaintext secret is never written to any persistent storage during service
56- execution.
58+ plaintext secret is not intentionally written to persistent storage during
59+ service execution, though it may appear in swap or hibernation images .
5760
58- ** Service-scoped:** each service gets its own ` $CREDENTIALS_DIRECTORY ` . Other
59- services on the same host cannot read another service's credentials — enforced
60- by the kernel via mount namespaces and file permissions.
61+ ** Service-scoped:** each service gets its own ` $CREDENTIALS_DIRECTORY ` .
62+ Other unprivileged services on the same host cannot read another service's
63+ credentials — enforced via mount namespaces and file permissions. Privileged
64+ processes (root) can bypass these controls.
6165
6266** No daemon process required:** unlike Vault Agent or similar, systemd
6367credentials need no running sidecar. The service manager handles decryption as
6468part of normal service startup.
6569
6670## Comparison with Alternatives
6771
68- | Mechanism | Encrypted at rest | Memory-only runtime | Machine-bound | Service-scoped |
69- | ---| ---| ---| ---| ---|
70- | systemd credential | ✓ | ✓ | ✓ | ✓ |
71- | Environment variable | ✗ | ✗ | ✗ | ✗ |
72- | File on disk | ✗ | ✗ | ✗ | ✗ |
73- | Vault Agent | ✓ | Optional | ✗ | Optional |
74- | Linux kernel keyring | N/A | ✓ | ✗ | ✓ (session) |
72+ | Mechanism | Encrypted at rest | Memory-only runtime | Machine-bound | Service-scoped |
73+ | ---------------------- | ------------------- | --------------------- | ------------------ | -------------------- ---|
74+ | systemd credential | ✓ (with Encrypted=) | ✓ (tmpfs) | ✓ with TPM2 | ✓ |
75+ | Environment variable | ✗ | ✗ | ✗ | ✗ |
76+ | File on disk | ✗ | ✗ | ✗ | ✗ |
77+ | Vault Agent | ✓ | Optional | ✗ | Optional |
78+ | Linux kernel keyring | N/A | ✓ | ✗ | ✓ (per user/ session) |
7579
7680## How srvguard Uses Them
7781
@@ -81,34 +85,34 @@ uses the content as a pre-issued Vault token. No login HTTP call is made — the
8185token is the credential.
8286
8387This means the Vault authentication step has no network round-trip: the token
84- is already present in memory before srvguard starts. Vault sees an
85- authenticated request for the secret; it does not need to validate a role or
86- certificate .
88+ is already present in memory before srvguard starts. No authentication
89+ exchange is required — the token is pre-issued and Vault validates it
90+ directly on the first secret request .
8791
8892The typical deployment on a VM:
8993
9094```
9195operator systemd srvguard Vault
9296 │ │ │ │
93- │ systemd-creds encrypt │ │ │
94- │ vault-token.cred │ │ │
95- │──────────────────────── ►│ │ │
96- │ │ service start │ │
97- │ │ decrypt → tmpfs │ │
98- │ │───────────────────────►│ │
99- │ │ │ GET /v1/secret/.. │
100- │ │ │───────────────────►│
101- │ │ │◄───────────────────│
102- │ │ │ write to tmpfs │
103- │ │ │ signal NGINX │
97+ │ systemd-creds encrypt │ │ │
98+ │ vault-token.cred │ │ │
99+ │───────────────────────►│ │ │
100+ │ │ service start │ │
101+ │ │ decrypt → tmpfs │ │
102+ │ │───────────────────────►│ │
103+ │ │ │ GET /v1/secret/.. │
104+ │ │ │───────────────────►│
105+ │ │ │◄───────────────────│
106+ │ │ │ write to tmpfs │
107+ │ │ │ signal NGINX │
104108```
105109
106110## Practical Notes
107111
108112- ` systemd-creds ` is in the ` systemd ` package — no additional install needed
109113- Use ` LoadCredentialEncrypted= ` for secrets (TPM2/machine-bound encryption)
110114- Use ` LoadCredential= ` for non-secret data that must just be injected
111- - On containers: systemd is usually not PID 1; use the file-on-tmpfs approach
115+ - On containers: systemd is often not PID 1; use the file-on-tmpfs approach
112116 instead and let the host VM manage credentials for the container
113117- Re-provisioning: encrypt a new token, replace the ` .cred ` file, restart the
114118 service — no host reboot required
0 commit comments