# Athenix Secrets System Design ## Overview The Athenix secrets management system integrates ragenix (agenix) with automatic host discovery based on the repository's fleet inventory structure. It provides a seamless workflow for managing encrypted secrets across all systems. ## Architecture ### Auto-Discovery Module (`sw/secrets.nix`) **Purpose**: Automatically load and configure secrets at system deployment time. **Features**: - Discovers `.age` encrypted files from `secrets/` directories - Loads global secrets from `secrets/global/` on ALL systems - Loads host-specific secrets from `secrets/{hostname}/` on matching hosts - Auto-configures decryption keys based on `.pub` files in directories - Supports custom secret configuration via `default.nix` in each directory **Key Behaviors**: - Secrets are decrypted to `/run/agenix/{name}` at boot - Identity paths include: system SSH keys + global keys + host-specific keys - Host-specific secrets override global secrets with the same name ### Dynamic Recipients Configuration (`secrets/secrets.nix`) **Purpose**: Generate ragenix recipient configuration from directory structure. **Features**: - Automatically discovers hosts from `secrets/` subdirectories - Reads age public keys from `.age.pub` files (converted from SSH keys) - Generates recipient lists based on secret location: - `secrets/global/*.age` → ALL hosts + admins - `secrets/{hostname}/*.age` → that host + global keys + admins - Supports admin keys in `secrets/admins/` for secret editing **Key Behaviors**: - No manual recipient list maintenance required - Adding a new host = create directory + add .pub key + run `update-age-keys.sh` - Works with ragenix CLI: `ragenix -e`, `ragenix -r` ## Workflow ### Adding a New Host 1. **Capture SSH host key**: ```bash # From the running system cat /etc/ssh/ssh_host_ed25519_key.pub > secrets/new-host/ssh_host_ed25519_key.pub ``` 2. **Convert to age format**: ```bash cd secrets/ ./update-age-keys.sh ``` 3. **Re-key existing secrets** (if needed): ```bash ragenix -r ``` ### Creating a New Secret 1. **Choose location**: - `secrets/global/` → all systems can decrypt - `secrets/{hostname}/` → only that host can decrypt 2. **Create/edit secret**: ```bash ragenix -e secrets/global/my-secret.age ``` 3. **Recipients are auto-determined** from `secrets.nix`: - Global secrets: all host keys + admin keys - Host-specific: that host + global keys + admin keys ### Cross-Host Secret Management Any Athenix host can manage secrets for other hosts because: - All public keys are in the repository (`*.age.pub` files) - `secrets/secrets.nix` auto-generates recipient lists - Hosts decrypt using their own private keys (not shared) Example: From `nix-builder`, create a secret for `usda-dash`: ```bash ragenix -e secrets/usda-dash/database-password.age # Encrypted for usda-dash's public key + admins # usda-dash will decrypt using its private key at /etc/ssh/ssh_host_ed25519_key ``` ## Directory Structure ``` secrets/ ├── secrets.nix # Auto-generated recipient config ├── update-age-keys.sh # Helper to convert SSH → age keys ├── README.md # User documentation ├── DESIGN.md # This file │ ├── global/ # Secrets for ALL hosts │ ├── *.pub # SSH public keys │ ├── *.age.pub # Age public keys (generated) │ ├── *.age # Encrypted secrets │ └── default.nix # Optional: custom secret config │ ├── {hostname}/ # Host-specific secrets │ ├── *.pub │ ├── *.age.pub │ ├── *.age │ └── default.nix │ └── admins/ # Admin keys for editing └── *.age.pub ``` ## Security Model 1. **Public keys in git**: Safe to commit (only public keys, `.age.pub` and `.pub`) 2. **Private keys on hosts**: Never leave the system (`/etc/ssh/ssh_host_*_key`, `/etc/age/identity.key`) 3. **Encrypted secrets in git**: Safe to commit (`.age` files) 4. **Decrypted secrets**: Only in memory/tmpfs (`/run/agenix/*`) ## Integration Points ### With NixOS Configuration ```nix # Access decrypted secrets in any NixOS module config.age.secrets.my-secret.path # => /run/agenix/my-secret # Example usage services.myapp.passwordFile = config.age.secrets.database-password.path; ``` ### With Inventory System The system automatically matches `secrets/{hostname}/` to hostnames from `inventory.nix`. No manual configuration needed. ### With External Modules External user/system modules can reference secrets: ```nix # In external module { config, ... }: { programs.git.extraConfig.credential.helper = "store --file ${config.age.secrets.git-credentials.path}"; } ``` ## Advantages 1. **Zero manual recipient management**: Just add directories and keys 2. **Cross-host secret creation**: Any host can manage secrets for others 3. **Automatic host discovery**: Syncs with inventory structure 4. **Flexible permission model**: Global vs host-specific + custom configs 5. **Version controlled**: All public data in git, auditable history 6. **Secure by default**: Private keys never shared, secrets encrypted at rest ## Limitations 1. **Requires age key conversion**: SSH keys must be converted to age format (automated by script) 2. **Bootstrap chicken-egg**: Need initial host key before encrypting secrets (capture from first boot or generate locally) 3. **No secret rotation automation**: Must manually re-key with `ragenix -r` 4. **Git history contains old encrypted versions**: Rotating keys doesn't remove old ciphertexts from history ## Future Enhancements - Auto-run `update-age-keys.sh` in pre-commit hook - Integrate with inventory.nix to auto-generate host directories - Support for multiple identity types per host - Automated secret rotation scheduling - Integration with hardware security modules (YubiKey, etc.)