Files
proyectosacc-mirror/docs/14-oidc-bitbucket-aws.md
T
Evert Daniel Romero Garrido 744c5d1413 feat(iam): implementa autenticación OIDC entre Bitbucket Pipelines y AWS
- Agrega aws_iam_openid_connect_provider y roles IAM para DEV/PROD
- Actualiza bitbucket-pipelines.yml para usar OIDC en steps 03, 05, 07
- Crea script helper scripts/aws-oidc-setup.sh
- Agrega provider tls en terraform/provider.tf
- Documenta el flujo completo en docs/14-oidc-bitbucket-aws.md

Elimina la dependencia de AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY
estáticos en el pipeline, permitiendo autenticación sin credenciales
de larga vida via AssumeRoleWithWebIdentity.

Refs: cuenta DEV 668889063715, PROD 523761210517
2026-04-15 12:50:31 -06:00

353 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 14 - Autenticación OIDC entre Bitbucket Pipelines y AWS
> Guía paso a paso para eliminar Access Keys estáticas y usar OpenID Connect (OIDC) en el pipeline de `proyectosacc`.
---
## 1. ¿Por qué OIDC?
En muchos equipos, los pipelines de CI/CD usan **IAM Users** con **Access Keys de larga vida** (`AWS_ACCESS_KEY_ID` y `AWS_SECRET_ACCESS_KEY`). Esto tiene varios problemas:
| Problema | Explicación |
|----------|-------------|
| **Rotación manual** | Las keys deben renovarse periódicamente (cada 90 días recomendado). Si se olvida, el pipeline falla. |
| **Fuga de secretos** | Si las keys se filtran (logs, commits, etc.), un atacante puede usarlas desde cualquier lugar del mundo. |
| **Deny de IAM** | En organizaciones maduras, los administradores bloquean `iam:CreateUser` e `iam:CreateAccessKey`, haciendo imposible este camino. |
**OIDC resuelve todo esto** permitiendo que el pipeline de Bitbucket asuma un **rol IAM temporal** automáticamente, sin guardar credenciales estáticas. Cada ejecución obtiene un token JWT que AWS valida y convierte en credenciales temporales (vencen en 1 hora por defecto).
---
## 2. ¿Cómo funciona el flujo?
El proceso es como mostrar una credencial de invitado en la entrada de un edificio:
```
┌─────────────────┐ 1. Solicita token OIDC ┌─────────────────┐
│ Bitbucket │ ───────────────────────────────▶│ Bitbucket │
│ Pipelines │ OIDC Provider │
│ (runner) │◀────────────────────────────────│ │
└─────────────────┘ 2. Devuelve JWT firmado └─────────────────┘
│ 3. Presenta JWT + Role ARN
┌─────────────────┐ 4. Valida JWT contra ┌─────────────────┐
│ AWS STS │ ───────────────────────────────▶│ AWS IAM │
│ (sts:AssumeRole│ OIDC Provider │
│ WithWebIdent.)│◀────────────────────────────────│ │
└─────────────────┘ 5. Confía si aud/sub OK └─────────────────┘
│ 6. Devuelve credenciales temporales
┌─────────────────┐
│ Pipeline │ 7. Ejecuta Terraform / aws s3 / cloudfront ...
│ con acceso AWS │
└─────────────────┘
```
**Claims importantes del JWT:**
| Claim | Significado | Ejemplo |
|-------|-------------|---------|
| `aud` | Audience (a quién va dirigido el token) | `sts.amazonaws.com` |
| `sub` | Subject (quién solicita el token) | `{REPO_UUID}:{STEP_UUID}` |
| `iss` | Issuer (quién emitió el token) | `https://api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc` |
---
## 3. Pre-requisitos
Antes de empezar, asegúrate de tener:
1. **Acceso administrativo a AWS** (consola o CLI) en ambas cuentas:
- DEV: `668889063715`
- PROD: `523761210517`
2. **Permiso para crear** `iam:CreateOpenIDConnectProvider` y `iam:CreateRole`.
3. El **Repository UUID** de `ccsoft1/proyectosacc`. Lo encuentras en:
- Bitbucket > Repository settings > OpenID Connect > **Repository UUID**
- Formato: `{1de489be-ce6a-42a0-a8c8-eadbf1174ac7}` (incluye las llaves `{}`)
---
## 4. Bootstrap manual: crear OIDC Provider y Roles
> **IMPORTANTE**: El pipeline NO puede crear su propio rol OIDC porque aún no tiene credenciales. La primera vez, un administrador debe crear el `OIDC Provider` y los `IAM Roles` manualmente o ejecutando Terraform desde su laptop con credenciales de admin.
### Opción A: Aplicar con Terraform local (recomendado)
1. Clona el repo en tu máquina:
```bash
git clone https://bitbucket.org/ccsoft1/proyectosacc.git
cd proyectosacc/terraform
```
2. Edita `terraform/oidc-bitbucket.tf` y reemplaza el placeholder:
```hcl
bitbucket_repo_uuid = "{1de489be-ce6a-42a0-a8c8-eadbf1174ac7}"
```
3. Configura tus credenciales de admin para la cuenta **DEV**:
```bash
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_DEFAULT_REGION="mx-central-1"
```
4. Inicializa y aplica:
```bash
terraform init -backend-config=backend.dev.hcl
terraform plan -var-file=environments/dev.tfvars -target=aws_iam_openid_connect_provider.bitbucket -target=aws_iam_role.bitbucket_ci_cd
terraform apply -var-file=environments/dev.tfvars -target=aws_iam_openid_connect_provider.bitbucket -target=aws_iam_role.bitbucket_ci_cd
```
5. Repite para la cuenta **PROD** cambiando el backend:
```bash
terraform init -backend-config=backend.prod.hcl -reconfigure
terraform plan -var-file=environments/prod.tfvars -target=aws_iam_openid_connect_provider.bitbucket -target=aws_iam_role.bitbucket_ci_cd
terraform apply -var-file=environments/prod.tfvars -target=aws_iam_openid_connect_provider.bitbucket -target=aws_iam_role.bitbucket_ci_cd
```
### Opción B: Crear desde la Consola AWS (paso a paso)
#### Paso 1: Crear el OIDC Provider
Repite esto en **DEV** y **PROD**:
1. Ve a **IAM > Identity Providers > Add Provider**.
2. Selecciona **OpenID Connect**.
3. **Provider URL**: `https://api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc`
4. Haz clic en **Get thumbprint** (AWS lo obtendrá automáticamente).
5. **Audience**: `sts.amazonaws.com`
6. Guarda con el nombre sugerido: `bitbucket-pipelines-oidc`
#### Paso 2: Crear el Rol IAM para DEV
1. Ve a **IAM > Roles > Create Role**.
2. Tipo de entidad confiable: **Web Identity**.
3. Selecciona el provider creado en el paso anterior.
4. **Audience**: `sts.amazonaws.com`.
5. En **Conditions**, agrega una condición personalizada (avanzado) o edita el trust policy después de crearlo.
6. Nombre del rol: `BitbucketProyectosaccCICDRoleDev`
7. Adjunta la política `AdministratorAccess` (solo como punto de partida).
8. Edita el **Trust Policy** para que quede así:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::668889063715:oidc-provider/api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc:aud": "sts.amazonaws.com"
},
"StringLike": {
"api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc:sub": "{REPO_UUID}:*"
}
}
}
]
}
```
Reemplaza `{REPO_UUID}` por el UUID real del repositorio.
#### Paso 3: Crear el Rol IAM para PROD
Repite exactamente el paso 2 pero en la cuenta PROD (`523761210517`) y con el nombre:
```
BitbucketProyectosaccCICDRoleProd
```
El ARN del provider federado cambiará a:
```
arn:aws:iam::523761210517:oidc-provider/api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc
```
---
## 5. Configuración del pipeline
El archivo `bitbucket-pipelines.yml` ya fue actualizado para usar OIDC. Estos son los cambios clave:
### 5.1 Audience global
```yaml
options:
oidc:
audiences:
- sts.amazonaws.com
```
Esto le dice a Bitbucket que el token OIDC incluya `sts.amazonaws.com` en el claim `aud`.
### 5.2 Activar OIDC en los steps
Cada step que interactúa con AWS debe tener `oidc: true`:
```yaml
- step:
name: 03_terraform
oidc: true
script:
- source scripts/aws-oidc-setup.sh dev
- ...
```
En `proyectosacc`, los steps con OIDC son:
- `03_terraform`
- `05_publish`
- `07_deploy`
### 5.3 Script helper `aws-oidc-setup.sh`
Este script:
1. Lee `BITBUCKET_STEP_OIDC_TOKEN` (lo inyecta Bitbucket automáticamente).
2. Lo guarda en un archivo temporal.
3. Exporta `AWS_WEB_IDENTITY_TOKEN_FILE` y `AWS_ROLE_ARN`.
4. AWS CLI y Terraform usan estas variables para asumir el rol automáticamente.
---
## 6. ¿Cómo probar que funciona?
### Prueba 1: Verificar el token JWT
Crea un pipeline de prueba temporal o agrega este comando al inicio de un step:
```bash
echo "$BITBUCKET_STEP_OIDC_TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | python3 -m json.tool
```
Deberías ver algo como:
```json
{
"sub": "{1de489be-ce6a-42a0-a8c8-eadbf1174ac7}:{759de0c6-eaee-4eaa-b7a6-c507eec759a7}",
"aud": "sts.amazonaws.com",
"iss": "https://api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc",
...
}
```
### Prueba 2: Verificar AssumeRole desde el pipeline
Agrega temporalmente esto al step `03_terraform`:
```bash
source scripts/aws-oidc-setup.sh dev
aws sts get-caller-identity
```
Debería devolver el ARN del rol asumido:
```json
{
"UserId": "AROA...:bot/session",
"Account": "668889063715",
"Arn": "arn:aws:sts::668889063715:assumed-role/BitbucketProyectosaccCICDRoleDev/bot/session"
}
```
### Prueba 3: Pipeline completo en DEV
1. Haz push a la rama `developer`.
2. Ve a Bitbucket > Pipelines.
3. Verifica que `03_terraform`, `05_publish` y `07_deploy` terminen en verde.
4. Si falla, revisa la sección de Troubleshooting.
---
## 7. Troubleshooting común
### Error: `BITBUCKET_STEP_OIDC_TOKEN no está definido`
**Causa**: Olvidaste agregar `oidc: true` al step.
**Solución**: Verifica que los steps `03_terraform`, `05_publish` y `07_deploy` tengan:
```yaml
oidc: true
```
---
### Error: `An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithWebIdentity operation`
**Causa**: El `aud` del token no coincide con el `client_id_list` del OIDC Provider.
**Solución**:
1. Verifica que `bitbucket-pipelines.yml` tenga:
```yaml
options:
oidc:
audiences:
- sts.amazonaws.com
```
2. Verifica que el OIDC Provider en AWS tenga `sts.amazonaws.com` en `client_id_list`.
---
### Error: `AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity`
**Causa**: El trust policy del rol no coincide con el claim `sub` del token.
**Solución**:
1. Obtén el `sub` real del token (ver Prueba 1).
2. Ajusta el trust policy. Si usas `StringLike`, asegúrate de que el patrón termine en `:*`:
```json
"StringLike": {
"api.bitbucket.org/2.0/workspaces/ccsoft1/pipelines-config/identity/oidc:sub": "{REPO_UUID}:*"
}
```
3. Si aún no conoces el `REPO_UUID`, puedes poner `"*"` temporalmente **solo para pruebas** (menos seguro).
---
### Error: `Unable to locate credentials`
**Causa**: AWS CLI no encuentra el token o el rol.
**Solución**:
1. Confirma que `scripts/aws-oidc-setup.sh` se ejecutó antes de cualquier comando `aws` o `terraform`.
2. Verifica que `AWS_WEB_IDENTITY_TOKEN_FILE` y `AWS_ROLE_ARN` estén exportadas.
3. Asegúrate de usar una versión reciente de AWS CLI (`aws --version` >= 2.x).
---
### Error: `terraform init` falla por permisos al bucket de estado
**Causa**: El rol OIDC no tiene permisos para leer/escribir el bucket S3 de estado ni la tabla DynamoDB de locks.
**Solución**: Como usamos `AdministratorAccess` como punto de partida, esto no debería pasar. Si en el futuro restringes la política, asegúrate de incluir permisos sobre:
- `s3:GetObject`, `s3:PutObject`, `s3:ListBucket` en el bucket de estado
- `dynamodb:GetItem`, `dynamodb:PutItem`, `dynamodb:DeleteItem` en la tabla de locks
---
## 8. Roadmap de hardening
Aunque `AdministratorAccess` hace que todo funcione inmediatamente, es una mala práctica de seguridad dejarlo así a largo plazo.
| Prioridad | Acción | Justificación |
|-----------|--------|---------------|
| **Alta** | Reemplazar `AdministratorAccess` por una política custom basada en `docs/iam-policy-ci-cd-proyectosacc.json`. | Mínimo privilegio. |
| **Media** | Restringir el `sub` claim al `REPO_UUID` exacto. | Evita que otros repos del workspace asuman el rol. |
| **Media** | Separar roles: uno para Terraform (infraestructura) y otro para despliegue de aplicación (S3 + CloudFront). | Reduce el blast radius. |
| **Baja** | Agregar condiciones de IP en el trust policy (si el pipeline siempre corre desde rangos conocidos de Bitbucket). | Capa extra de seguridad. |
---
## Referencias
- [Deploy on AWS using Bitbucket Pipelines OpenID Connect Atlassian](https://support.atlassian.com/bitbucket-cloud/docs/deploy-on-aws-using-bitbucket-pipelines-openid-connect/)
- [AWS Docs: Creating OpenID Connect identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html)
- [docs/12-credenciales-aws-ci-cd.md](12-credenciales-aws-ci-cd.md) — Contexto anterior de credenciales estáticas.
- [docs/iam-policy-ci-cd-proyectosacc.json](iam-policy-ci-cd-proyectosacc.json) — Política mínima de referencia.
---
*Área de Tecnología y Desarrollo — CCsoft — Abril 2026*