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:
- Se establece
CREDENTIAL_ENCRYPTION_KEYa la nueva clave (activa) - Se establece
CREDENTIAL_ENCRYPTION_KEY_OLDa la clave anterior - Se ejecuta
manage.py reencrypt_credentials— migra todo ciphertext a la clave nueva - Se limpia
CREDENTIAL_ENCRYPTION_KEY_OLD(vacío) - 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_OLDdebe 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]]