Sacar las claves de nube de larga vida de tu CI
La clave estática de tu pipeline es la credencial con más probabilidades de acabar en un informe de brecha. La federación OIDC la elimina en una tarde, y la política de confianza es donde se falla.
Mira en los ajustes de secretos de cualquier CI con tres años de vida y encontrarás una clave de acceso creada por alguien que ya no está, con permisos que nadie ha revisado, que nunca se ha rotado. La puede leer cualquier job del repositorio, funciona desde cualquier punto de internet y no caduca.
Hoy todos los sistemas de CI relevantes pueden intercambiar un token OIDC de vida corta por credenciales de nube. El trabajo son un par de horas y el resultado elimina una categoría entera de incidente.
Cómo funciona el intercambio
El sistema de CI firma un JWT que describe el job: qué repositorio, qué rama o entorno, qué workflow. El proveedor de nube está configurado para confiar en ese emisor, comprueba las reclamaciones contra una política que escribes tú y devuelve credenciales válidas una hora o menos.
No se almacena nada. No hay clave que filtrar, rotar ni revocar, y las credenciales están ligadas al job concreto que las pidió.
AWS: registra el proveedor una vez y luego un rol cuya política de confianza nombre el sujeto exacto:
{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:acme/orders:environment:production"
}
}
}
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: arn:aws:iam::123456789012:role/ci-orders-deploy
aws-region: eu-west-1
Azure usa credenciales federadas sobre un registro de aplicación con la misma reclamación de sujeto. GCP usa Workload Identity Federation con una condición de atributo y una cuenta de servicio. Vault tiene un método de autenticación JWT que mapea reclamaciones a una política. GitLab CI, Jenkins (con el plugin de proveedor OIDC) y Argo Workflows emiten tokens compatibles.
La política de confianza es todo el control de seguridad
Aquí es donde la mayoría de implantaciones quedan más débiles que las claves que sustituyen.
Usa StringEquals, no StringLike, siempre que puedas. Un sujeto repo:acme/* con StringLike significa que cualquier repositorio de la organización puede asumir el rol. Alguien crea un repositorio nuevo, sube un workflow y tiene tus credenciales de producción.
Acota por entorno, no por rama, para todo lo que toque producción. repo:acme/orders:ref:refs/heads/main parece estrecho, pero cualquiera que pueda empujar una rama llamada main en un flujo de fork y PR, o renombrar una rama, entra. repo:acme/orders:environment:production junto a un entorno con revisores obligatorios significa que la credencial solo puede emitirse tras la aprobación de una persona.
Ancla siempre la audiencia. Omitir la comprobación de aud permite reutilizar un token emitido para otra parte confiante.
Un rol por repositorio y entorno. Un rol ci-deploy compartido que asume todo tiene el mismo radio de impacto que la clave compartida, solo que con mejor caducidad. La gracia es precisamente que la credencial describa quién la usa.
Qué hacer con los secretos que quedan
OIDC cubre las APIs de nube. No cubre el token de npm, la contraseña de Docker Hub, la clave de una API de terceros ni la clave de firma. Para eso:
- Guárdalos en un gestor de secretos de verdad y recupéralos en tiempo de ejecución con las credenciales que acabas de federar, en vez de pegarlos en variables de CI. Un solo sitio donde rotar, un solo registro de auditoría.
- Acótalos por entorno y márcalos como protegidos, para que un job en una rama cualquiera no pueda leer los de producción.
- Rota en un calendario que puedas verificar de verdad. Una política de rotación que nadie comprueba es un documento, no un control.
- Ejecuta escaneo de secretos sobre el repositorio y sobre los logs de CI. El enmascarado en logs es de mejor esfuerzo, y cualquier cosa que pase por
base64o por un JSON se le escapa.
Retirar las claves viejas
No añadas la federación dejando las claves donde están: habrías añadido un mecanismo, no quitado un riesgo. La secuencia:
- Crea el rol OIDC con los mismos permisos que la clave.
- Cambia el pipeline y confirma que funciona.
- Mira la fecha de último uso de la clave en IAM. Espera a que deje de moverse.
- Desactiva la clave (no la borres todavía). Espera una semana por lo que se te olvidó.
- Bórrala.
Después pon una barandilla a nivel de organización para que no aparezca la siguiente: una SCP que deniegue iam:CreateAccessKey fuera de una vía de emergencia, una Azure Policy que bloquee los secretos de cliente en registros de aplicación, o la política de organización de GCP que desactiva la creación de claves de cuenta de servicio. Sin eso, repetirás este proyecto dentro de dos años.
Los permisos del rol federado merecen el mismo escrutinio que cualquier otro: concede al rol de despliegue exactamente lo que el despliegue necesita y comprueba con Access Analyzer qué usó de verdad al cabo de un mes.