Skip to content

SOPS + age key rotation

Estado actual (post F3 inicial): 3 keypairs activos — Mac de Ramón, pmx-50, pmx-51. Pendiente añadir keypairs para VM 208, LXC 101 (Hermes), LXC 200 (n8n) y LXC 251 (RAG). No hay backup offline de los age keys — riesgo: si se pierde la key de Ramón, los secretos quedan inrecuperables. Mitigación pendiente: copia cifrada de las keys en KeePassXC personal.

Custodia

Los archivos ~/.config/sops/age/keys.txt son el secreto raíz. Nunca commit. Backup manual offline (USB/papel) recomendado.

Generar un nuevo keypair age

```bash

En la máquina destino (e.g. pmx-50 o un LXC)

age-keygen -o /root/.config/sops/age/keys.txt chmod 600 /root/.config/sops/age/keys.txt cat /root/.config/sops/age/keys.txt

Apunta la línea "public key: age1xxxxxxxxxxxxxxxx"

```

Para la Mac de Ramón:

bash mkdir -p ~/.config/sops/age age-keygen -o ~/.config/sops/age/keys.txt chmod 600 ~/.config/sops/age/keys.txt

Añadir a ~/.zshrc:

bash export SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt"

Añadir un nuevo recipient a archivos existentes

1. Actualizar .sops.yaml

En la raíz del repo, edita .sops.yaml para añadir la nueva public key:

yaml creation_rules: - path_regex: secrets/.*\.sops\.yaml$ age: >- age1mac...., age1pmx50...., age1pmx51...., age1NEWKEY.... # <- añadir aquí

2. Re-encriptar archivos afectados

sops updatekeys re-aplica las reglas del .sops.yaml actual a cada archivo:

```bash cd ~/homelab-infra sops updatekeys secrets/observability.sops.yaml

repetir para cada secreto:

for f in secrets/*.sops.yaml; do sops updatekeys "$f"; done ```

3. Commit

bash git add .sops.yaml secrets/ git commit -m "sops: add <hostname> recipient to all secrets" git push

Tras 15min (Ansible-pull) los nodos verán los nuevos archivos. El nuevo recipient podrá descifrar.

Rotación de un keypair comprometido

Si una clave privada se filtra (e.g. SSH leak, disco perdido sin LUKS):

1. Generar nueva keypair en el host afectado

bash ssh pmx-50 'mv /root/.config/sops/age/keys.txt /root/.config/sops/age/keys.txt.old' ssh pmx-50 'age-keygen -o /root/.config/sops/age/keys.txt' ssh pmx-50 'cat /root/.config/sops/age/keys.txt | grep "public key"'

2. Sustituir la public key en .sops.yaml

Reemplaza la línea vieja por la nueva. Commit + sops updatekeys igual que antes.

3. Re-cifrar archivos (quitando la vieja key)

updatekeys solo añade. Para quitar la vieja:

```bash

Edición manual del header sops.age de cada archivo, o reencrypt entero:

for f in secrets/*.sops.yaml; do sops -d "$f" > /tmp/plain.yaml sops -e /tmp/plain.yaml > "$f" rm /tmp/plain.yaml done ```

4. Rotar además los secretos que la key vieja podía leer

Una vez comprometida, asumir que todo lo descifrable por esa key está comprometido. Generar tokens nuevos (CF API, Telegram bot, HA long-lived, n8n webhook secret, etc.). Ver Rotating secrets.

Verificación

```bash

Desde la Mac (con tu key)

sops -d secrets/observability.sops.yaml | head

Desde un nodo (con su key)

ssh pmx-50 'cd /var/lib/ansible/homelab-infra && sops -d secrets/observability.sops.yaml | head' ```

Si falla con Failed to get the data key: 0 successful groups required, 0 succeeded, el host no tiene una key que sea recipient del archivo.

Backup offline

Recomendado (pendiente implementar):

  1. Imprimir keys.txt de la Mac en papel + meter en caja fuerte.
  2. Copia cifrada con passphrase fuerte (age -p) en un USB + cloud personal.
  3. Documentar el procedimiento de restauración (orden de re-generar nodos, etc.) en una nota offline.