Hoy hablaremos sobre configurar OIDC en Github actions. Pero antes, ¿qué es lo más frecuente? Lo más común que solemos encontrar cuando buscamos configuraciones entre Github y AWS, para poder dar ciertos permisos es el uso de access keys.
- Normalmente, crearemos un usuario IAM en AWS
- Crearemos un Access Key y su secret.
- Este usuario tendrá un política asociada o formará parte de un grupo.
- Luego en Github, en el mejor de los casos, usaremos los Secrets y accederemos a ellos desde nuestro job en Github. El contenido de los secrets no se puede volver a obtener nunca más, a menos, desde la consola de Github.
¿Qué problema hay con esto?
Las access keys de larga duración. Y su rotación. Has de mantener un usuario, que siempre va a estar en tu cuenta. Ese usuario puede ser borrado, su secret puede ser filtrado, o has de mantenerlo. En resumen, es un secreto que has de custodiar para siempre. ¿Y que opción hay? Configurar OIDC con Github actions.
El problema con las credenciales estáticas en GitHub Secrets

La forma más habitual de autenticar GitHub Actions contra AWS es crear un usuario IAM, generar una access key y añadirla como secret en el repositorio. Funciona, pero tiene varios problemas:
- Las credenciales no caducan solas. Si hay un leak — del repo, de los logs, de la propia interfaz de GitHub — siguen siendo válidas hasta que alguien las rote manualmente.
- Requieren rotación periódica, que en proyectos personales o de equipo pequeño suele no hacerse.
- Das acceso permanente a operaciones que solo deberían ejecutarse en contexto de CI.
- No hay forma de limitar el acceso por rama, evento o contexto de ejecución — cualquier workflow en el repo puede usar esas credenciales.
OIDC (OpenID Connect) resuelve todo esto eliminando las credenciales estáticas del flujo.
Cómo funciona OIDC entre GitHub y AWS

En lugar de credenciales de larga duración, GitHub Actions genera un token JWT firmado para cada ejecución del workflow. AWS verifica ese token contra el OIDC provider de GitHub y, si todo está en orden, devuelve credenciales temporales del STS válidas solo para la duración del job.
El flujo completo:
- El job solicita un JWT a GitHub. El token incluye información del contexto: repo, rama, evento, etc.
- El workflow llama a AWS STS con
AssumeRoleWithWebIdentity, pasando el JWT. - AWS verifica la firma del JWT contra el OIDC provider (
token.actions.githubusercontent.com). - Si el token es válido y cumple las condiciones del trust policy del rol, AWS devuelve credenciales temporales.
- El job opera en AWS con esas credenciales. Expiran solas — normalmente en 15-60 minutos.
Lo más importante de este mecanismo son las condiciones del trust policy: puedes limitar qué repos, ramas o tipos de evento pueden asumir el rol, algo imposible con credenciales estáticas.
La implementación en Terraform
Necesitas dos recursos: el OIDC provider en IAM y el rol que asumirán los workflows.
OIDC Provider
resource "aws_iam_openid_connect_provider" "github_actions" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
# Hash del certificado raíz de token.actions.githubusercontent.com
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}El thumbprint_list es el hash del certificado raíz del OIDC provider de GitHub. Es un valor público y estable.
Rol IAM con trust policy
resource "aws_iam_role" "github_actions_oidc" {
name = "mi-proyecto-github-actions-oidc"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github_actions.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:mi-org/mi-repo:*"
}
}
}
]
})
}La condición sub es la que limita el acceso. Puedes afinarla según el contexto que necesites:
# Solo pushes a master
"repo:mi-org/mi-repo:ref:refs/heads/master"
# Solo pull requests
"repo:mi-org/mi-repo:pull_request"
# Cualquier contexto del repo
"repo:mi-org/mi-repo:*"Después añades las políticas de permisos al rol igual que harías con cualquier otro rol IAM.
El workflow actualizado
Antes, con credenciales estáticas:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
aws-region: eu-west-1Después, con OIDC:
jobs:
deploy:
permissions:
id-token: write # obligatorio para solicitar el JWT a GitHub
contents: read
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/mi-proyecto-github-actions-oidc
aws-region: eu-west-1El permiso id-token: write es obligatorio. Sin él, el job no puede solicitar el JWT y el paso de autenticación falla.
El problema del bootstrap
Hay un detalle que hay que tener en cuenta al implementar esto: no puedes crear el rol OIDC y actualizar el workflow en el mismo PR si el apply usa el propio workflow.
El rol tiene que existir en AWS antes de que el workflow intente asumirlo. El orden correcto:
- PR 1: añadir los recursos Terraform (OIDC provider + rol). El apply corre todavía con credenciales estáticas y crea los recursos en AWS.
- PR 2: actualizar el workflow para usar
role-to-assume. A partir de aquí, los secrets ya no se usan. - Eliminar
AWS_ACCESS_KEYyAWS_SECRET_KEYde GitHub Secrets.
Si los combinas en un solo PR, el apply fallará intentando asumir un rol que todavía no existe.
Por qué merece la pena
| Credenciales estáticas | OIDC | |
|---|---|---|
| Duración | Permanentes hasta rotación manual | 15-60 minutos por job |
| Impacto de un leak | Requiere rotación inmediata | Token ya expirado, sin impacto |
| Rotación | Manual, se suele olvidar | Automática en cada ejecución |
| Secrets en GitHub | AWS_ACCESS_KEY + AWS_SECRET_KEY | Ninguno |
| Restricción por contexto | No | Por repo, rama, evento |
La complejidad de configurar OIDC es puntual y se hace una sola vez en Terraform. A cambio, eliminas la superficie de ataque más obvia: una access key de larga duración con permisos de infraestructura que vive en los secrets de un repositorio.
Links
- https://aws.amazon.com/blogs/security/use-iam-roles-to-connect-github-actions-to-actions-in-aws/
- https://www.rubenortiz.es/tag/github/
¿Te ha ayudado este artículo?
☕ Invítame a un café