- Rename SOPS key alias from generic admin_key to steffen_key in .sops.yaml and all docs (keys should identify the person, not a role) - Add step 3.6 to first-install docs: create Forgejo admin account via CLI (required since DISABLE_REGISTRATION is enabled) - Fix cryodev-pi_key comment naming in .sops.yaml
174 lines
3.3 KiB
Markdown
174 lines
3.3 KiB
Markdown
# SOPS Secret Management
|
|
|
|
Atomic secret provisioning for NixOS using [sops-nix](https://github.com/Mic92/sops-nix).
|
|
|
|
## Overview
|
|
|
|
Secrets are encrypted with `age` using SSH host keys, ensuring:
|
|
- No plaintext secrets in the repository
|
|
- Secrets are decrypted at activation time
|
|
- Each host can only decrypt its own secrets
|
|
|
|
## Setup
|
|
|
|
### 1. Get Host's Age Public Key
|
|
|
|
After a host is installed, extract its age key from the SSH host key:
|
|
|
|
```bash
|
|
nix-shell -p ssh-to-age --run 'ssh-keyscan -t ed25519 <HOST_IP> | ssh-to-age'
|
|
```
|
|
|
|
Or locally on the host:
|
|
|
|
```bash
|
|
nix-shell -p ssh-to-age --run 'cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age'
|
|
```
|
|
|
|
### 2. Configure .sops.yaml
|
|
|
|
Add the host key to `.sops.yaml`:
|
|
|
|
```yaml
|
|
keys:
|
|
- &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local)
|
|
- &main_key age1... # cryodev-main
|
|
- &pi_key age1... # cryodev-pi
|
|
|
|
creation_rules:
|
|
- path_regex: hosts/cryodev-main/secrets.yaml$
|
|
key_groups:
|
|
- age:
|
|
- *steffen_key
|
|
- *main_key
|
|
|
|
- path_regex: hosts/cryodev-pi/secrets.yaml$
|
|
key_groups:
|
|
- age:
|
|
- *steffen_key
|
|
- *pi_key
|
|
```
|
|
|
|
### 3. Create Secrets File
|
|
|
|
```bash
|
|
sops hosts/<hostname>/secrets.yaml
|
|
```
|
|
|
|
This opens your editor. Add secrets in YAML format:
|
|
|
|
```yaml
|
|
tailscale:
|
|
auth-key: "tskey-..."
|
|
|
|
some-service:
|
|
password: "secret123"
|
|
```
|
|
|
|
## Usage in Modules
|
|
|
|
### Declaring Secrets
|
|
|
|
```nix
|
|
{ config, ... }:
|
|
{
|
|
sops.secrets.my-secret = {
|
|
# Optional: set owner/group
|
|
owner = "myservice";
|
|
group = "myservice";
|
|
};
|
|
}
|
|
```
|
|
|
|
### Using Secrets
|
|
|
|
Reference the secret path in service configuration:
|
|
|
|
```nix
|
|
{
|
|
services.myservice = {
|
|
passwordFile = config.sops.secrets.my-secret.path;
|
|
};
|
|
}
|
|
```
|
|
|
|
### Using Templates
|
|
|
|
For secrets that need to be embedded in config files:
|
|
|
|
```nix
|
|
{
|
|
sops.secrets."netdata/stream-api-key" = { };
|
|
|
|
sops.templates."netdata-stream.conf" = {
|
|
content = ''
|
|
[stream]
|
|
enabled = yes
|
|
api key = ${config.sops.placeholder."netdata/stream-api-key"}
|
|
'';
|
|
owner = "netdata";
|
|
};
|
|
|
|
services.netdata.configDir."stream.conf" =
|
|
config.sops.templates."netdata-stream.conf".path;
|
|
}
|
|
```
|
|
|
|
## Common Secrets
|
|
|
|
### cryodev-main
|
|
|
|
```yaml
|
|
mailserver:
|
|
accounts:
|
|
forgejo: "$2y$05$..." # bcrypt hash
|
|
admin: "$2y$05$..."
|
|
|
|
forgejo-runner:
|
|
token: "..."
|
|
|
|
headplane:
|
|
cookie_secret: "..." # openssl rand -hex 16
|
|
agent_pre_authkey: "..." # headscale preauthkey
|
|
|
|
tailscale:
|
|
auth-key: "tskey-..."
|
|
```
|
|
|
|
### cryodev-pi
|
|
|
|
```yaml
|
|
tailscale:
|
|
auth-key: "tskey-..."
|
|
|
|
netdata:
|
|
stream:
|
|
child-uuid: "..." # uuidgen
|
|
```
|
|
|
|
## Generating Secret Values
|
|
|
|
| Secret | Command |
|
|
|--------|---------|
|
|
| Mailserver password | `nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'` |
|
|
| Random hex token | `nix-shell -p openssl --run 'openssl rand -hex 16'` |
|
|
| UUID | `uuidgen` |
|
|
| Tailscale preauth | `sudo headscale preauthkeys create --expiration 99y --reusable --user <ID>` |
|
|
|
|
## Updating Keys
|
|
|
|
After modifying `.sops.yaml`, update existing secrets files:
|
|
|
|
```bash
|
|
sops --config .sops.yaml updatekeys hosts/<hostname>/secrets.yaml
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### "No matching keys found"
|
|
|
|
Ensure the host's age key is in `.sops.yaml` and you've run `updatekeys`.
|
|
|
|
### Secret not decrypting on host
|
|
|
|
Check that `/etc/ssh/ssh_host_ed25519_key` exists and matches the public key in `.sops.yaml`.
|