Skip to content

feat(security): systemd-creds fallback for token + audit seed#15

Merged
amiwrpremium merged 1 commit into
masterfrom
feat/systemd-creds-secrets
Apr 22, 2026
Merged

feat(security): systemd-creds fallback for token + audit seed#15
amiwrpremium merged 1 commit into
masterfrom
feat/systemd-creds-secrets

Conversation

@amiwrpremium
Copy link
Copy Markdown
Owner

Optional at-rest-encryption mode for SHELLBOTO_TOKEN and SHELLBOTO_AUDIT_SEED.

  • ResolveSecret(envVar, credName) helper: env-var first, then $CREDENTIALS_DIRECTORY/<name>.
  • deploy/enable-credentials.sh one-shot migration (reversible with --revert).
  • Doctor reports which mode is active.
  • New docs/security/secrets-at-rest.md explains the threat model + trade-offs.

Env-file mode stays the default. Nothing breaks for existing installs.

Adds an optional at-rest-encryption mode for the bot token and
audit seed. The current env-file mode stays the default; flipping
to credentials mode is a single helper-script invocation.

Background: with the env-file approach, `/etc/shellboto/env` is
plaintext root-0600 on disk. Backup / snapshot / disk-image theft
exposes the token verbatim — the attacker doesn't need runtime
access to the VPS. systemd-creds (systemd 250+) encrypts the
secrets at rest, keying to the TPM2 if present or to the system
credential-secret otherwise. Decrypted values live only in a
per-invocation memfd, never on disk.

What this doesn't fix: runtime root on the VPS can still
`cat /proc/<pid>/environ` (but env var isn't set here) or read
`$CREDENTIALS_DIRECTORY/<name>`. Memory dump of the running VM
captures the decrypted secret. Those are fundamentally runtime-
trust issues; `systemd-creds` targets the at-rest threat.

Changes:

- `internal/config/secret.go` (new)
  - `ResolveSecret(envVar, credName) (string, error)` — env-var
    first, then `$CREDENTIALS_DIRECTORY/<credName>` file, else
    empty. A missing cred file is not an error; the caller's
    "was this required?" logic decides.
  - `ResolveSecretWithSource(...)` — same, plus a `SecretSource`
    tag (env / systemd-creds / unset) so doctor + startup log
    can report which mode is active without echoing the value.
- `internal/config/config.go` — token loading routes through
  the new helper. Superadmin ID stays env-only (it's a public
  identifier, not a secret).
- `cmd/shellboto/main.go:resolveAuditSeed` — same helper; logs
  the source on success.
- `cmd/shellboto/cmd_doctor.go` — reports
  `SHELLBOTO_TOKEN set  source=env|systemd-creds` and
  `SHELLBOTO_AUDIT_SEED valid  32 bytes, source=env|systemd-creds`.
- `deploy/enable-credentials.sh` (new) — one-shot migration:
  reads plaintext secrets from /etc/shellboto/env, encrypts
  via systemd-creds, drops a
  /etc/systemd/system/shellboto.service.d/credentials.conf
  override with LoadCredentialEncrypted= lines, strips the
  plaintext secrets from env file (keeps SUPERADMIN_ID),
  daemon-reload + restart, runs doctor. Reversible with
  `--revert`. Non-interactive with `-y`.
- Tests in `internal/config/secret_test.go` covering every
  source combination (env only, creds only, both set, neither,
  file unreadable, whitespace trimming).
- Docs:
  - New `docs/security/secrets-at-rest.md` — full treatment of
    both modes + threat-model table + rotation + migration-to-
    new-host guidance.
  - `docs/security/README.md` indexes it.
  - `docs/configuration/environment.md` + `docs/reference/env-vars.md`
    document the `$CREDENTIALS_DIRECTORY` fallback for both
    sensitive variables.
  - `docs/security/audit-seed.md` — seed-storage section expanded
    to cover both modes.
  - `docs/deployment/production-checklist.md` — optional-hardening
    step pointing at `enable-credentials.sh`.
@amiwrpremium amiwrpremium enabled auto-merge (squash) April 22, 2026 07:22
@amiwrpremium amiwrpremium merged commit a5a27dc into master Apr 22, 2026
13 checks passed
@amiwrpremium amiwrpremium deleted the feat/systemd-creds-secrets branch April 22, 2026 07:24
amiwrpremium added a commit that referenced this pull request Apr 22, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.1.2](v0.1.1...v0.1.2)
(2026-04-22)


### Features

* **security:** systemd-creds fallback for token + audit seed
([#15](#15))
([a5a27dc](a5a27dc))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant