All checks were successful
CI / Format Check (push) Successful in 2s
CI / Flake Check (push) Successful in 1m42s
CI / Evaluate Key Configurations (nix-builder) (push) Successful in 14s
CI / Evaluate Key Configurations (nix-desktop1) (push) Successful in 7s
CI / Evaluate Key Configurations (nix-laptop1) (push) Successful in 8s
CI / Evaluate Artifacts (installer-iso-nix-laptop1) (push) Successful in 20s
CI / Evaluate Artifacts (lxc-nix-builder) (push) Successful in 13s
CI / Build and Publish Documentation (push) Successful in 11s
175 lines
5.9 KiB
Markdown
175 lines
5.9 KiB
Markdown
# 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.)
|