Skip to content

Commit f34caaf

Browse files
committed
documentation changes after peer reviews (Daniel and ChatGPT)
1 parent 8617c57 commit f34caaf

4 files changed

Lines changed: 65 additions & 56 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# srvguard
22

33
**Nash!Com Service Guard** — a universal service launcher and secret manager
4-
for [HashiCorp Vault](https://www.vaultproject.io/)-integrated workloads.
4+
for secure, automated secret delivery to any workload.
55

66
`srvguard` solves a fundamental bootstrap problem: a service needs its
77
secrets (TLS certificate, private key, password) before it can start, but
@@ -491,7 +491,7 @@ docker build --target release -t srvguard:latest .
491491

492492
`srvguard` expects a flat KV v2 secret. Field names are flexible —
493493
configure the consumer to read whichever fields are present. The defaults
494-
used by [nashcom-vault](https://github.com/nashcom/nashcom-vault) are:
494+
used by [nsh-vault-deploy](https://github.com/nashcom/nsh-vault-deploy) are:
495495

496496
**TLS credentials:**
497497

docs/architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ operator systemd srvguard Vault
3737
│ │────────────────────────►│ │
3838
│ │ │ GET /v1/secret/ │
3939
│ │ │─────────────────►│
40-
│ │◄───────────────────────────────────────────
40+
│ │◄───────────────────────────────────────────
4141
│ │ │ write to tmpfs │
4242
│ │ │ signal service │
4343
```

docs/linux-kernel-keyring.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
## What It Is
44

55
The Linux kernel keyring is an in-kernel storage facility for small secrets —
6-
passwords, tokens, cryptographic keys — that never leave kernel memory.
7-
Secrets placed in the keyring are not visible in `/proc`, are not swapped to
8-
disk, and survive only as long as the keyring that holds them exists.
6+
passwords, tokens, cryptographic keys — stored in kernel memory and not
7+
exposed via normal userspace interfaces. Secrets placed in the keyring are
8+
not directly written to disk by the keyring subsystem, and survive only as
9+
long as the keyring that holds them exists.
910

1011
It was introduced in Linux 2.6.10 and is present in every modern Linux
1112
distribution. No additional software is required.
@@ -19,8 +20,9 @@ distribution. No additional software is required.
1920
keyring is destroyed, all keys in it are destroyed.
2021

2122
**Session keyring:** created when a process session starts, destroyed when it
22-
ends. Each login session or service invocation gets its own keyring that
23-
cannot be accessed by other sessions.
23+
ends. Each login session or service invocation gets its own keyring that is
24+
isolated by default from other sessions, unless explicitly shared or accessed
25+
by a sufficiently privileged process.
2426

2527
**Key permissions:** each key has owner/group/other permission bits controlling
2628
who can read, write, search, or link it. A key created by `srvguard` with
@@ -30,13 +32,16 @@ who can read, write, search, or link it. A key created by `srvguard` with
3032

3133
The keyring provides properties that files on disk cannot:
3234

33-
- **Memory-only:** the payload never touches the filesystem, swap, or any disk
34-
buffer. Hibernation aside, it is never written anywhere persistent.
35+
- **Not written to disk by the subsystem:** the keyring itself does not write
36+
payloads to the filesystem or any disk buffer. Kernel memory can theoretically
37+
be swapped or captured in a hibernation image or crash dump, but the keyring
38+
does not actively persist secrets — unlike a file or environment variable.
3539
- **Lifecycle-bound:** a key attached to a session keyring is automatically
3640
destroyed when the session ends — no cleanup required.
3741
- **Kernel-enforced access control:** the kernel enforces permissions on every
38-
read attempt. Even root cannot read a key owned by another user without
39-
`CAP_SYS_ADMIN`.
42+
read attempt. Key payload is not exposed via `/proc` — though key metadata
43+
(serial number, description) may be visible in `/proc/keys`. Even root
44+
cannot read a key payload owned by another user without `CAP_SYS_ADMIN`.
4045
- **Read-once pattern:** the consumer reads the key payload once and
4146
immediately revokes it. After revocation the key is gone — any second read
4247
fails, even by the original owner.

docs/systemd-credentials.md

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
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.
2020
systemd-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

3535
systemd decrypts the credential before starting the service and places the
3636
plaintext 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

4243
The service reads the secret from `$CREDENTIALS_DIRECTORY/vault-token`. By the
4344
time 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
6367
credentials need no running sidecar. The service manager handles decryption as
6468
part 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
8185
token is the credential.
8286

8387
This 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

8892
The typical deployment on a VM:
8993

9094
```
9195
operator 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

Comments
 (0)