diff --git a/.gitignore b/.gitignore index 9dde607..169808c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,13 @@ *.zip apps/gitea/gitea-docker-backup +# Ignore decrypted secret material +*.dec.yaml +*.decrypted.yaml +*.plain.yaml +*.secret.local.yaml +.age-key.txt +sops-age.key + # Ignore older source iterations *.old diff --git a/.sops.yaml.example b/.sops.yaml.example new file mode 100644 index 0000000..c3c6510 --- /dev/null +++ b/.sops.yaml.example @@ -0,0 +1,6 @@ +# Copy this file to .sops.yaml after replacing the age recipient with the +# public key generated on the Debian homelab server. +creation_rules: + - path_regex: '(^|/).*\.(secret|enc)\.ya?ml$' + encrypted_regex: '^(data|stringData|values)$' + age: age1replacewithyourpublicrecipient diff --git a/README.md b/README.md index 4e8056b..e5a2557 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ The lab is intentionally small but production-shaped: - Argo CD continuously reconciles Kubernetes manifests from this repo - a local registry stores the website and demos images built for the worker architecture +- SOPS with age is the committed secret-management path for future encrypted + Kubernetes secrets - an OCI jump box provides the public edge path back into the homelab over Tailscale @@ -198,6 +200,13 @@ settings without blocking existing pods during the first rollout. After reports are clean, individual policies can be promoted to `Enforce` in `bootstrap/platform/main.tf`. +## Secrets + +Use SOPS with age for secrets that need to live in Git. Start from +`.sops.yaml.example`, replace the age recipient with the public key generated on +the Debian host, and commit the resulting `.sops.yaml`. Keep the private age key +outside the repo. Operational notes are in `docs/secrets.md`. + ## Edge Services The OCI jump box runs the public edge path: diff --git a/docs/secrets.md b/docs/secrets.md new file mode 100644 index 0000000..a817821 --- /dev/null +++ b/docs/secrets.md @@ -0,0 +1,56 @@ +# Secret Management + +This repo uses SOPS with age for secrets that must be stored in Git. The +encrypted files can be committed, while the age private key stays on the Debian +homelab server or in a deliberately scoped CI secret. + +## First-Time Setup + +Install the tools on the Debian host: + +```bash +sudo apt-get update +sudo apt-get install -y --no-install-recommends age sops +``` + +Generate the local age identity: + +```bash +mkdir -p ~/.config/sops/age +age-keygen -o ~/.config/sops/age/keys.txt +grep '^# public key:' ~/.config/sops/age/keys.txt +``` + +Copy `.sops.yaml.example` to `.sops.yaml`, replace the placeholder recipient +with the printed public key, and commit `.sops.yaml`. The public recipient is +not sensitive; the private identity in `~/.config/sops/age/keys.txt` is. + +## File Naming + +Use one of these suffixes for encrypted YAML: + +```text +*.secret.yaml +*.enc.yaml +``` + +For Kubernetes `Secret` manifests, keep sensitive values under `stringData` or +`data` so the example `encrypted_regex` encrypts the right fields without +obscuring resource metadata needed by Argo CD and review diffs. + +## Editing + +Create or edit an encrypted file: + +```bash +SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops apps/example/app.secret.yaml +``` + +Check the decrypted render locally without writing it to the repo: + +```bash +SOPS_AGE_KEY_FILE=~/.config/sops/age/keys.txt sops -d apps/example/app.secret.yaml +``` + +Decrypted scratch files are intentionally ignored by `.gitignore`; encrypted +files are not.