CreaRack-SL

Rotación de claves de cifrado de credenciales

Descripción

Soporte nativo para rotar la clave de cifrado de credenciales (CREDENTIAL_ENCRYPTION_KEY) sin que ninguna credencial almacenada deje de ser legible durante la transición.

Contexto: Las claves iniciales del hito E se generaron en una consola interactiva durante sesión 101, quedando registradas en transcripts y backups. Se consideran expuestas. Este cambio implementa el mecanismo para rotarlas por unas nuevas generadas de forma segura.

Mecanismo

Cadena de descifrado extendida

La clase CredentialManager pasa de una cadena binaria:

activa → SECRET_KEY (legacy) → DEV_KEY (rescue)

A una cadena que incluye la clave anterior:

activa → CREDENTIAL_ENCRYPTION_KEY_OLD → SECRET_KEY (legacy) → DEV_KEY (rescue)

Durante la rotación:

  1. Se establece CREDENTIAL_ENCRYPTION_KEY a la nueva clave (activa)
  2. Se establece CREDENTIAL_ENCRYPTION_KEY_OLD a la clave anterior
  3. Se ejecuta manage.py reencrypt_credentials — migra todo ciphertext a la clave nueva
  4. Se limpia CREDENTIAL_ENCRYPTION_KEY_OLD (vacío)
  5. A partir de ese momento, el ciphertext antiguo ya no es legible (migrado)

Implementación

Config (config/settings/base.py):

CREDENTIAL_ENCRYPTION_KEY_OLD = os.getenv("CREDENTIAL_ENCRYPTION_KEY_OLD", "")

Vacía si no hay rotación en curso.

Descifrado (core/security/credential_manager.py, método _decryption_chain):

_add(cls._active_key_material())
_add((getattr(settings, "CREDENTIAL_ENCRYPTION_KEY_OLD", "") or "").strip() or None)
_add(getattr(settings, "SECRET_KEY", None))
if include_dev_rescue:
    _add(cls.DEV_KEY)

Solo añade OLD si está definida y no vacía.

Variables de entorno propagadas en:

  • compose.prod.yml (web + worker)
  • compose.yml (dev)

Test de rotación

tests/test_credential_key_rotation.py::test_rotation_via_old_key:

  • Cifra con clave A
  • Rota a clave B (A pasa a OLD)
  • Verifica que el ciphertext antiguo se descifra vía OLD
  • Verifica que nuevos ciphertexts usan B
  • Limpia OLD y confirma que A-ciphertext ya no se lee

Impacto operativo

  • Sin downtime: Las credenciales existentes siguen siendo válidas durante toda la rotación
  • Reversible hasta reencrypt: Si algo falla, las claves se pueden revertir antes de ejecutar reencrypt_credentials
  • Aplicable a futuras rotaciones: El mecanismo es genérico; no es específico de esta ocasión

Notas de seguridad

  • CREDENTIAL_ENCRYPTION_KEY_OLD debe ser una variable de entorno secreto (en Dokploy u orquestador)
  • Solo se usa en cadena de descifrado — nunca para escribir nuevos valores
  • Debe ser limpiada inmediatamente después de reencrypt_credentials

Véase también

  • [[entity—core—config—credential-encryption-key-old]]
  • [[concept—security—credential-encryption]]
  • [[concept—security—key-rotation]]
  • [[entity—core—service—credential-manager]]
  • [[entity—core—config—credential-encryption-key]]