Skip to content

Rotating secrets

Workflow estándar para rotar tokens, API keys y passwords usando SOPS + age. Asume que la setup base de SOPS key rotation está hecha (al menos un keypair por nodo y por usuario humano).

Cuándo rotar

  • Programado: cada 6 meses para tokens long-lived (CF API, Telegram, HA).
  • Reactivo: filtración sospechada, ex-colaborador, dispositivo perdido.
  • Tras la migración inicial (F3): los secretos pre-existentes en plaintext se consideran posiblemente expuestos en logs/history y deben rotarse.

Secretos en alcance

Inventario actual (post-migración F3 inicial):

Secreto Archivo SOPS Notas
Healthchecks UUIDs, HA token, Telegram (veraclawd_bot), Grafana, Loki bearer token secrets/observability.sops.yaml
HA long-lived, PBS API, Telegram (verahermes_bot), PocketID OIDC (grafana+karakeep), Proxmox API secrets/agents.sops.yaml
Cloudflare API + tunnel secrets/cloudflare.sops.yaml
Proxmox cluster secrets secrets/proxmox.sops.yaml
Media stack (sonarr/radarr/…) secrets/media.sops.yaml
UniFi (CloudKey Gen2+) secrets/unifi.sops.yaml
Remote-Pulse (LXC 280) secrets/remote-pulse.sops.yaml ✅ (TODO en .sops.yaml: aún falta añadir pubkey age de LXC 280 como recipient)
WP-Pulse (LXC 281) secrets/wp-pulse.sops.yaml ✅ (TODO en .sops.yaml: aún falta añadir pubkey age de LXC 281 como recipient)

Los secretos se consolidan en esos 8 ficheros (verificado contra secrets/*.sops.yaml en el repo, 2026-08-01); no hay uno por servicio.

Plano aparte: secretos de Vera / OpenClaw (CT100, FUERA de SOPS)

No pasan por SOPS+age

Vera / OpenClaw mantiene su propio plano de secretos, separado del workflow SOPS+age de homelab-infra. Viven en texto plano en un fichero .env dentro de CT100 (clawdbot): /root/.openclaw/secrets.env. No están cifrados, no están en el repo homelab-infra, y no se rotan con sops.

Claves en /root/.openclaw/secrets.env (CT100):

Clave Qué es
TG_BOT Token del bot de Telegram de Vera
TG_CHAT Chat ID de Telegram destino
OPENCLAW_GATEWAY_PASSWORD Password del gateway OpenClaw (≈clawdbot2026)
OPENAI_API_KEY API key de OpenAI
GOG_KEYRING_PASSWORD Password del keyring (GOG)
JOBHUNTER_DB_PASSWORD Password de la DB de JobHunter (≈jobhunter2025)

Cómo rotar (plano OpenClaw)

Al ser un .env plano, la rotación es manual — no hay sops, git push ni ansible-pull que lo propague:

# 1) Entrar en CT100 (desde un nodo Proxmox que hospede el CT)
pct exec 100 -- bash

# 2) Backup antes de tocar
cp /root/.openclaw/secrets.env /root/.openclaw/secrets.env.bak.$(date +%Y%m%d)

# 3) Editar el valor a rotar (fichero KEY=VALUE plano)
vi /root/.openclaw/secrets.env

# 4) Rotar el secreto en su provider (Telegram BotFather, OpenAI dashboard,
#    DB de JobHunter, etc.) y pegar el nuevo valor en el .env

# 5) Reiniciar el servicio OpenClaw para recargar el .env
systemctl restart openclaw-gateway   # (o el unit que consuma el secrets.env)

Deuda técnica conocida

Este plano en plano-texto está fuera del modelo SOPS+age del resto del homelab. Objetivo futuro: migrar estas claves a secrets/*.sops.yaml y renderizarlas con Ansible como el resto (ver más abajo). Mientras tanto, trátalo como posiblemente expuesto y rota tras cualquier incidente.

Workflow base

ℹ️ Requiere sops + age (ambos instalados en .208 desde 2026-07-12: sops v3.9.1).

1. Editar el archivo SOPS

cd ~/docs-repo
sops secrets/agents.sops.yaml

Se abre el editor ($EDITOR) con el contenido descifrado. Edita el valor:

telegram:
  verahermes_bot_token: <NUEVO_TOKEN>

Guarda y sal. SOPS re-cifra automáticamente.

2. Verificar el diff

git diff secrets/agents.sops.yaml

Debería mostrar solo cambios en los campos cifrados (data:, mac:, lastmodified:). No debería aparecer texto plano.

3. Commit + push

git add secrets/agents.sops.yaml
git commit -m "secrets(telegram): rotate bot token"
git push

4. Esperar Ansible-pull (15min) o forzar

ssh pmx-50 'ansible-pull -U https://github.com/monxas/homelab-infra.git ansible/playbook.yml --limit pmx-50'

(Igual para pmx-51 y nodos afectados.)

5. Reiniciar el servicio que usa el secreto

# Ejemplo: n8n alert forwarder
ssh pmx-51 'pct exec 200 -- systemctl restart alert-forwarder'

6. Verificar

# Trigger un alert de prueba
ssh pmx-51 'pct exec 200 -- curl -X POST http://localhost:5678/webhook/test-alert'
# Comprobar que llega Telegram

Generar un secret nuevo

Token aleatorio (32 chars hex)

openssl rand -hex 32

Token aleatorio (URL-safe base64)

openssl rand -base64 32 | tr -d '=' | tr '+/' '-_'

Password humano-recordable (passphrase 5 palabras)

# Si tienes diceware o pwgen
pwgen -s 24 1

Bcrypt hash (para basic auth Caddy o htpasswd)

Genera en la máquina destino (no via SSH desde la Mac — el shell remoto expande $ y rompe el hash). Ver Creating OIDC client para más detalle.

ssh pmx-50 'pct exec 270 -- caddy hash-password --plaintext "<password>"'

Crear un archivo SOPS nuevo

cd ~/docs-repo
# Edita partiendo de plantilla mínima
cat > secrets/newservice.sops.yaml.plain <<'EOF'
api_key: <REPLACE>
EOF
# ⚠️ PRIMERO añade una regla per-file en `.sops.yaml` — NO hay catch-all `secrets/.*`:
#   - path_regex: secrets/newservice\.sops\.yaml$
#     key_groups: [ { age: [ *ramon, ... ] } ]
sops -e secrets/newservice.sops.yaml.plain > secrets/newservice.sops.yaml
rm secrets/newservice.sops.yaml.plain

# Edita el cifrado para meter el valor real
sops secrets/newservice.sops.yaml

Cada fichero de secrets/ tiene su propia path_regex en .sops.yaml (no hay regla global secrets/.*); sin añadirla antes, sops -e falla con no matching creation rules.

Después de rotar

  • Borrar el token viejo en el provider (CF, Telegram, etc.) — si no, sigue válido.
  • Buscar referencias al token viejo en logs (grep -r "<prefix-token>" /var/log) por si quedó en algún sitio.
  • Documentar la rotación en el commit message + Hermes log si aplica.

Lista de tokens a rotar inmediatamente post-F3

Tras la migración a SOPS, considerar los siguientes como posiblemente expuestos (estuvieron en plaintext en .env/history/backups):

  1. CF API token con permisos DNS — alta prioridad.
  2. CF Tunnel credentials (<uuid>.json) — regenerar tunnel si dudas.
  3. Telegram bot tokens (n8n alert forwarder + morning-report).
  4. HA long-lived tokens (Hermes, ha-ml, scripts externos).
  5. PBS API token.
  6. NAS SMB/NFS share passwords.
  7. PocketID admin password.
  8. Vaultwarden admin token.

Plan: rotar 1-3 por semana para evitar romper algo y poder bisectar.