Saltar a contenido

Gestión de Secretos

Parte de la guía de implementación

Diagrama — tres vías de secretos

flowchart LR
  subgraph git [En_git]
    SS[Sealed_Secrets]
    SOPS[SOPS_age]
  end
  subgraph station [Estacion_operador]
    OP[1Password_SDK_DesktopAuth]
  end
  subgraph cluster [En_cluster]
    SEC[k8s_Secret]
  end
  SS --> SEC
  SOPS --> git
  OP --> SEC

Sealed Secrets (default en clúster)

Sealed Secrets — un controller (~20–30 MB RAM), sin cuenta externa.

kubeseal --format yaml < secret-plano.yaml > secret-sellado.yaml
kubectl apply -f secret-sellado.yaml
rm secret-plano.yaml

La llave de cifrado vive en 1Password

Por defecto el controller genera su par de llaves dentro del clúster (y lo renueva cada 30 días): si reinstalas K3s, se pierde y los SealedSecret de git ya no se pueden descifrar. Aquí el par se genera una vez, se guarda en 1Password (item sealed-secrets) y playbook-platform-secrets.yml lo lleva al clúster; la renovación automática está desactivada (keyrenewperiod: "0"). Cómo generarlo: Plataforma symintel — Paso 2.

Como el certificado (la llave pública) también está en 1Password, puedes sellar sin tener acceso al clúster: kubeseal --cert tls.crt --format yaml < secret-plano.yaml > secret-sellado.yaml.

SOPS + age (complemento, cero pods)

SOPS — config pre-bootstrap (tokens Incus, etc.).

age-keygen -o key.txt
sops --encrypt --age <public-key> secrets.yaml > secrets.enc.yaml

OAuth Dex (GitHub + vCluster + Incus UI) — 1Password SDK local

Credenciales de GitHub OAuth App y clientes Dex vCluster Platform e Incus UI (interfaz de usuario) no van en git.

Origen Campos
GitHub (OAuth App de la org symintel) GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET
Ansible (playbook-dex-oauth-secrets.yml) VCLUSTER_CLIENT_SECRET, INCUS_CLIENT_SECRET (genera en 1Password si faltan; aplica en Dex, Incus y/o Helm)

INCUS_CLIENT_SECRET no se crea en GitHub. Ansible lo genera en 1Password si falta y lo aplica en Dex e Incus.

Se leen con el 1Password Python SDK y DesktopAuth en la estación donde la app 1Password está desbloqueada.

Por defecto: cuenta personal (ONEPASSWORD_ACCOUNT_NAME=Personal), bóveda HomeLab, un ítem con campos GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, VCLUSTER_CLIENT_SECRET, INCUS_CLIENT_SECRET. Probar: ansible/scripts/test-1password.py.

Homepage https://argocd.homelab.local (informativa); Redirect URIs https://argocd.homelab.local/api/dex/callback (crítico).

Detalle: Fase 4 — 4.7 · 1password.md

Rotación de password de root (1Password SDK local)

playbook-rotate-root-passwords.yml genera una password nueva por nodo (no idempotente a propósito — cada corrida rota), la aplica en el root local de cada host (become + ansible.builtin.user, hasheada con password_hash('sha512')) y la guarda en la bóveda HomeLab, un ítem por host (root@invincible, root@oliver, root@deborah, campo password). No toca SSH (Secure Shell) — el login de root por SSH ya está deshabilitado por host_hardening (PermitRootLogin no); esto es defensa en profundidad para consola/su -.

cd ansible
ansible-playbook -i inventory.ini playbook-rotate-root-passwords.yml
ansible-playbook -i inventory.ini playbook-rotate-root-passwords.yml \
  --limit deborah
Análisis de trade-offs
Sealed Secrets
Cifrado en git; controller ligero (~30 MB)
Solo en clúster; no sirve pre-bootstrap
SOPS + age
Cero pods; archivos cifrados en repo
Clave age en la estación; flujo manual
1Password SDK
OAuth fuera de git; DesktopAuth
App desbloqueada; no para SealedSecret en CAPN
Vault / ESO descartado
Enterprise-grade
RAM y complejidad excesivas en HomeLab

Descartados

  • HashiCorp Vault — demasiado pesado para el M700.
  • 1Password + ESO (External Secrets Operator) en clúster — requiere plan Business; usamos SDK (Software Development Kit) Python local (DesktopAuth) desde la estación.
  • RustyVault — proyecto joven, sin ARM64 (arquitectura ARM de 64 bits) confirmado.

Stack completo: Stack tecnológico.