192 lines
4.1 KiB
Markdown
192 lines
4.1 KiB
Markdown
|
|
# Agenix Secret Management Guide
|
||
|
|
|
||
|
|
A guide for creating and managing encrypted secrets using agenix in the nixos-config project.
|
||
|
|
|
||
|
|
## Prerequisites
|
||
|
|
|
||
|
|
- SSH key pair (ed25519 or rsa) in `~/.ssh/`
|
||
|
|
- Access to the nixos-config repository
|
||
|
|
- Secret added to `secrets.nix` with appropriate public keys
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Creating a New Secret
|
||
|
|
|
||
|
|
### Step 1: Add Secret to secrets.nix
|
||
|
|
|
||
|
|
Edit `secrets.nix` and add a new entry:
|
||
|
|
|
||
|
|
```nix
|
||
|
|
"secrets/<your-secret-name>.age".publicKeys = systems ++ users;
|
||
|
|
```
|
||
|
|
|
||
|
|
Where:
|
||
|
|
- `<your-secret-name>` is the desired filename (without `.age`)
|
||
|
|
- `systems` = hosts that can decrypt this secret
|
||
|
|
- `users` = users that can decrypt this secret
|
||
|
|
|
||
|
|
Example:
|
||
|
|
```nix
|
||
|
|
"secrets/my-service-api-key.age".publicKeys = systems ++ users;
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 2: Create the Encrypted Secret
|
||
|
|
|
||
|
|
Navigate to the nixos-config directory:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd ~/p/NIX/nixos-config
|
||
|
|
```
|
||
|
|
|
||
|
|
Generate a secure random token (if needed):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
head -c 32 /dev/urandom | base64 | tr -d '\n'
|
||
|
|
```
|
||
|
|
|
||
|
|
Or use a specific value:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
echo -n "your-secret-value-here" > /tmp/token.txt
|
||
|
|
```
|
||
|
|
|
||
|
|
Encrypt and create the secret file:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cat /tmp/token.txt | RULES=./secrets.nix nix develop . --command sh -c 'agenix -e secrets/<your-secret-name>.age'
|
||
|
|
```
|
||
|
|
|
||
|
|
The `-e` flag encrypts stdin content into the age file. If no stdin is provided, agenix opens your editor.
|
||
|
|
|
||
|
|
### Step 3: Verify the Secret
|
||
|
|
|
||
|
|
Decrypt to verify:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
RULES=./secrets.nix nix develop . --command agenix -d secrets/<your-secret-name>.age
|
||
|
|
```
|
||
|
|
|
||
|
|
You should see your secret value printed to stdout.
|
||
|
|
|
||
|
|
### Step 4: Use in NixOS Configuration
|
||
|
|
|
||
|
|
Reference the secret in your service config:
|
||
|
|
|
||
|
|
```nix
|
||
|
|
{ config, ... }:
|
||
|
|
|
||
|
|
{
|
||
|
|
# For environment files
|
||
|
|
environmentFiles = [ config.age.secrets."my-secret-name".path ];
|
||
|
|
|
||
|
|
# For file-based secrets
|
||
|
|
environmentFile = config.age.secrets."my-secret-name".path;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
The secret will be available at `/run/agenix/<your-secret-name>` when the system builds.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Common Patterns
|
||
|
|
|
||
|
|
### Token-Based Registration (e.g., Tuwunel)
|
||
|
|
|
||
|
|
```nix
|
||
|
|
# secrets.nix
|
||
|
|
"secrets/tuwunel-registration-token.age".publicKeys = systems ++ users;
|
||
|
|
```
|
||
|
|
|
||
|
|
```nix
|
||
|
|
# services/tuwunel.nix
|
||
|
|
settings.global = {
|
||
|
|
allow_registration = true;
|
||
|
|
registration_token_file = config.age.secrets."tuwunel-registration-token".path;
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### API Keys via Environment Files
|
||
|
|
|
||
|
|
```nix
|
||
|
|
# secrets.nix
|
||
|
|
"secrets/my-service-env.age".publicKeys = systems ++ users;
|
||
|
|
```
|
||
|
|
|
||
|
|
Create `my-service-env.age` containing:
|
||
|
|
```
|
||
|
|
MY_SERVICE_API_KEY=your-key-here
|
||
|
|
DATABASE_URL=postgres://...
|
||
|
|
```
|
||
|
|
|
||
|
|
Reference in config:
|
||
|
|
```nix
|
||
|
|
environmentFiles = [ config.age.secrets."my-service-env".path ];
|
||
|
|
```
|
||
|
|
|
||
|
|
### File-Based Secrets
|
||
|
|
|
||
|
|
For binary files or specific file paths:
|
||
|
|
|
||
|
|
```nix
|
||
|
|
settings = {
|
||
|
|
tls.cert = config.age.secrets."tls-cert".path;
|
||
|
|
tls.key = config.age.secrets."tls-key".path;
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Agenix Command Reference
|
||
|
|
|
||
|
|
| Command | Description |
|
||
|
|
|---------|-------------|
|
||
|
|
| `agenix -e <file.age>` | Edit/create encrypted secret (opens editor or uses stdin) |
|
||
|
|
| `agenix -d <file.age>` | Decrypt and print to stdout |
|
||
|
|
| `agenix -r` | Re-encrypt all secrets (after changing public keys) |
|
||
|
|
| `agenix --validate-config` | Validate secrets.nix syntax |
|
||
|
|
|
||
|
|
### Environment Variables
|
||
|
|
|
||
|
|
| Variable | Default | Description |
|
||
|
|
|----------|---------|-------------|
|
||
|
|
| `RULES` | `./secrets.nix` | Path to secrets.nix file |
|
||
|
|
| `EDITOR` | `$EDITOR` | Editor for interactive editing |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### "No identity found to decrypt"
|
||
|
|
|
||
|
|
**Cause**: No SSH private key available.
|
||
|
|
|
||
|
|
**Solution**: Ensure your private key is in `~/.ssh/`:
|
||
|
|
- `~/.ssh/id_rsa`
|
||
|
|
- `~/.ssh/id_ed25519`
|
||
|
|
- Or specify with `-i /path/to/private/key`
|
||
|
|
|
||
|
|
### "Failed to find config root"
|
||
|
|
|
||
|
|
**Cause**: Agenix can't find `secrets.nix`.
|
||
|
|
|
||
|
|
**Solution**: Use `RULES=./secrets.nix` or run from the nixos-config directory.
|
||
|
|
|
||
|
|
### Rekeying Secrets
|
||
|
|
|
||
|
|
After adding new public keys to `secrets.nix`, re-encrypt all secrets:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd ~/p/NIX/nixos-config
|
||
|
|
RULES=./secrets.nix nix develop . --command agenix -r
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Security Notes
|
||
|
|
|
||
|
|
- Never commit plaintext secrets to git
|
||
|
|
- Always use `secrets.nix` for encryption keys
|
||
|
|
- Include only necessary hosts/users in public keys
|
||
|
|
- Rotate secrets periodically
|
||
|
|
- Use `agenix -r` after modifying public keys
|