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
This commit is contained in:
+17
-4
@@ -9,6 +9,11 @@
|
|||||||
|
|
||||||
image: atlassian/default-image:5
|
image: atlassian/default-image:5
|
||||||
|
|
||||||
|
options:
|
||||||
|
oidc:
|
||||||
|
audiences:
|
||||||
|
- sts.amazonaws.com
|
||||||
|
|
||||||
definitions:
|
definitions:
|
||||||
steps:
|
steps:
|
||||||
- step: ¬ify-start
|
- step: ¬ify-start
|
||||||
@@ -60,15 +65,15 @@ pipelines:
|
|||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 03_terraform
|
name: 03_terraform
|
||||||
|
oidc: true
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
|
- source scripts/aws-oidc-setup.sh dev
|
||||||
- cd terraform
|
- cd terraform
|
||||||
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
|
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
|
||||||
- unzip -q terraform_1.11.4_linux_amd64.zip
|
- unzip -q terraform_1.11.4_linux_amd64.zip
|
||||||
- mv terraform /usr/local/bin/terraform
|
- mv terraform /usr/local/bin/terraform
|
||||||
- terraform version
|
- terraform version
|
||||||
- export AWS_ACCESS_KEY_ID="${DEV_AWS_ACCESS_KEY_ID}"
|
|
||||||
- export AWS_SECRET_ACCESS_KEY="${DEV_AWS_SECRET_ACCESS_KEY}"
|
|
||||||
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||||
- terraform init -backend-config=backend.dev.hcl
|
- terraform init -backend-config=backend.dev.hcl
|
||||||
- terraform plan -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan
|
- terraform plan -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan
|
||||||
@@ -91,8 +96,10 @@ pipelines:
|
|||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 05_publish
|
name: 05_publish
|
||||||
|
oidc: true
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
|
- source scripts/aws-oidc-setup.sh dev
|
||||||
- aws s3 sync build/ "s3://${DEV_S3_FRONTEND_BUCKET}/" --delete
|
- aws s3 sync build/ "s3://${DEV_S3_FRONTEND_BUCKET}/" --delete
|
||||||
- aws s3 cp build/libs/*.jar "s3://${DEV_S3_ARTIFACTS_BUCKET}/develop/proyectosacc-app.jar"
|
- aws s3 cp build/libs/*.jar "s3://${DEV_S3_ARTIFACTS_BUCKET}/develop/proyectosacc-app.jar"
|
||||||
|
|
||||||
@@ -111,8 +118,10 @@ pipelines:
|
|||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 07_deploy
|
name: 07_deploy
|
||||||
|
oidc: true
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
|
- source scripts/aws-oidc-setup.sh dev
|
||||||
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
- echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
- chmod 600 ~/.ssh/sacc4_key
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
- |
|
- |
|
||||||
@@ -148,15 +157,15 @@ pipelines:
|
|||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 03_terraform
|
name: 03_terraform
|
||||||
|
oidc: true
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
|
- source scripts/aws-oidc-setup.sh prod
|
||||||
- cd terraform
|
- cd terraform
|
||||||
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
|
- wget -q "https://releases.hashicorp.com/terraform/1.11.4/terraform_1.11.4_linux_amd64.zip"
|
||||||
- unzip -q terraform_1.11.4_linux_amd64.zip
|
- unzip -q terraform_1.11.4_linux_amd64.zip
|
||||||
- mv terraform /usr/local/bin/terraform
|
- mv terraform /usr/local/bin/terraform
|
||||||
- terraform version
|
- terraform version
|
||||||
- export AWS_ACCESS_KEY_ID="${PROD_AWS_ACCESS_KEY_ID}"
|
|
||||||
- export AWS_SECRET_ACCESS_KEY="${PROD_AWS_SECRET_ACCESS_KEY}"
|
|
||||||
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
- export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}"
|
||||||
- terraform init -backend-config=backend.prod.hcl
|
- terraform init -backend-config=backend.prod.hcl
|
||||||
- terraform plan -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan
|
- terraform plan -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan
|
||||||
@@ -179,8 +188,10 @@ pipelines:
|
|||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 05_publish
|
name: 05_publish
|
||||||
|
oidc: true
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
|
- source scripts/aws-oidc-setup.sh prod
|
||||||
- aws s3 sync build/ "s3://${PROD_S3_FRONTEND_BUCKET}/" --delete
|
- aws s3 sync build/ "s3://${PROD_S3_FRONTEND_BUCKET}/" --delete
|
||||||
- aws s3 cp build/libs/*.jar "s3://${PROD_S3_ARTIFACTS_BUCKET}/main/proyectosacc-app.jar"
|
- aws s3 cp build/libs/*.jar "s3://${PROD_S3_ARTIFACTS_BUCKET}/main/proyectosacc-app.jar"
|
||||||
|
|
||||||
@@ -199,8 +210,10 @@ pipelines:
|
|||||||
|
|
||||||
- step:
|
- step:
|
||||||
name: 07_deploy
|
name: 07_deploy
|
||||||
|
oidc: true
|
||||||
script:
|
script:
|
||||||
- set -euo pipefail
|
- set -euo pipefail
|
||||||
|
- source scripts/aws-oidc-setup.sh prod
|
||||||
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
- echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key
|
||||||
- chmod 600 ~/.ssh/sacc4_key
|
- chmod 600 ~/.ssh/sacc4_key
|
||||||
- |
|
- |
|
||||||
|
|||||||
@@ -0,0 +1,352 @@
|
|||||||
|
# 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*
|
||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ===============================================================================================================
|
||||||
|
# aws-oidc-setup.sh - Configura credenciales AWS via OIDC para Bitbucket Pipelines
|
||||||
|
# Descripción:
|
||||||
|
# Exporta las variables necesarias para que AWS CLI y Terraform usen
|
||||||
|
# el token OIDC de Bitbucket y asuman el rol IAM correspondiente.
|
||||||
|
#
|
||||||
|
# Uso:
|
||||||
|
# source scripts/aws-oidc-setup.sh <dev|prod>
|
||||||
|
#
|
||||||
|
# Requiere:
|
||||||
|
# - El step de bitbucket-pipelines.yml debe tener "oidc: true"
|
||||||
|
# - AWS CLI moderno que soporte AWS_WEB_IDENTITY_TOKEN_FILE
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ENV="${1:-dev}"
|
||||||
|
|
||||||
|
if [[ -z "${BITBUCKET_STEP_OIDC_TOKEN:-}" ]]; then
|
||||||
|
echo "ERROR: BITBUCKET_STEP_OIDC_TOKEN no está definido."
|
||||||
|
echo "Asegúrate de agregar 'oidc: true' al step en bitbucket-pipelines.yml"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
export AWS_WEB_IDENTITY_TOKEN_FILE="$(pwd)/web-identity-token"
|
||||||
|
printf '%s' "${BITBUCKET_STEP_OIDC_TOKEN}" > "${AWS_WEB_IDENTITY_TOKEN_FILE}"
|
||||||
|
|
||||||
|
if [[ "$ENV" == "prod" ]]; then
|
||||||
|
export AWS_ROLE_ARN="arn:aws:iam::523761210517:role/BitbucketProyectosaccCICDRoleProd"
|
||||||
|
else
|
||||||
|
export AWS_ROLE_ARN="arn:aws:iam::668889063715:role/BitbucketProyectosaccCICDRoleDev"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export AWS_DEFAULT_REGION="mx-central-1"
|
||||||
|
|
||||||
|
echo "=== AWS OIDC Setup ==="
|
||||||
|
echo "Ambiente : $ENV"
|
||||||
|
echo "Role ARN : $AWS_ROLE_ARN"
|
||||||
|
echo "Region : $AWS_DEFAULT_REGION"
|
||||||
|
echo "Token file: $AWS_WEB_IDENTITY_TOKEN_FILE"
|
||||||
|
echo "======================"
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# ===============================================================================================================
|
||||||
|
# oidc-bitbucket.tf - Configuración OIDC entre Bitbucket Pipelines y AWS
|
||||||
|
# Descripción:
|
||||||
|
# Crea el IAM OIDC Provider para Bitbucket y el rol IAM que el pipeline
|
||||||
|
# asumirá mediante Web Identity Federation (AssumeRoleWithWebIdentity).
|
||||||
|
#
|
||||||
|
# IMPORTANTE (Bootstrap):
|
||||||
|
# Este archivo debe aplicarse MANUALMENTE la primera vez por un
|
||||||
|
# administrador con credenciales AWS válidas. Una vez creados el
|
||||||
|
# OIDC provider y el rol, el pipeline puede autenticarse solo.
|
||||||
|
# ===============================================================================================================
|
||||||
|
|
||||||
|
locals {
|
||||||
|
bitbucket_workspace = "ccsoft1"
|
||||||
|
bitbucket_oidc_url = "https://api.bitbucket.org/2.0/workspaces/${local.bitbucket_workspace}/pipelines-config/identity/oidc"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# REPO_UUID de proyectosacc
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Obtener en Bitbucket: Repository settings > OpenID Connect > Repository UUID
|
||||||
|
# Ejemplo real: "{1de489be-ce6a-42a0-a8c8-eadbf1174ac7}"
|
||||||
|
#
|
||||||
|
# NOTA DE SEGURIDAD:
|
||||||
|
# Si aún no conoces el UUID exacto, puedes dejar "*" temporalmente
|
||||||
|
# para permitir cualquier repo del workspace. Esto es MENOS SEGURO
|
||||||
|
# y solo debe usarse durante las pruebas iniciales. En producción,
|
||||||
|
# reemplázalo por el UUID exacto del repo ccsoft1/proyectosacc.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
bitbucket_repo_uuid = "{REPO_UUID_PLACEHOLDER}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Obtener el thumbprint del certificado TLS del issuer OIDC
|
||||||
|
data "tls_certificate" "bitbucket_oidc" {
|
||||||
|
url = local.bitbucket_oidc_url
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# OIDC Provider para Bitbucket Pipelines
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_iam_openid_connect_provider" "bitbucket" {
|
||||||
|
url = local.bitbucket_oidc_url
|
||||||
|
|
||||||
|
# Usamos "sts.amazonaws.com" como audience para simplificar la
|
||||||
|
# configuración y evitar depender del Workspace UUID de Bitbucket.
|
||||||
|
# Esto requiere configurar "audiences: [sts.amazonaws.com]" en
|
||||||
|
# bitbucket-pipelines.yml.
|
||||||
|
client_id_list = [
|
||||||
|
"sts.amazonaws.com"
|
||||||
|
]
|
||||||
|
|
||||||
|
thumbprint_list = [
|
||||||
|
data.tls_certificate.bitbucket_oidc.certificates[0].sha1_fingerprint
|
||||||
|
]
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "bitbucket-pipelines-oidc"
|
||||||
|
Project = var.project_name
|
||||||
|
ManagedBy = "terraform"
|
||||||
|
Environment = var.environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Rol IAM para CI/CD desde Bitbucket Pipelines
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_iam_role" "bitbucket_ci_cd" {
|
||||||
|
name = "BitbucketProyectosaccCICDRole${title(var.environment)}"
|
||||||
|
|
||||||
|
assume_role_policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [
|
||||||
|
{
|
||||||
|
Effect = "Allow"
|
||||||
|
Principal = {
|
||||||
|
Federated = aws_iam_openid_connect_provider.bitbucket.arn
|
||||||
|
}
|
||||||
|
Action = "sts:AssumeRoleWithWebIdentity"
|
||||||
|
Condition = {
|
||||||
|
StringEquals = {
|
||||||
|
"${trimprefix(local.bitbucket_oidc_url, "https://")}:aud" = "sts.amazonaws.com"
|
||||||
|
}
|
||||||
|
StringLike = {
|
||||||
|
"${trimprefix(local.bitbucket_oidc_url, "https://")}:sub" = "${local.bitbucket_repo_uuid}:*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Name = "bitbucket-ci-cd-role-${var.environment}"
|
||||||
|
Project = var.project_name
|
||||||
|
Environment = var.environment
|
||||||
|
ManagedBy = "terraform"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Política de permisos: AdministratorAccess (punto de partida)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# TODO: Reemplazar AdministratorAccess por una política custom con
|
||||||
|
# mínimo privilegio. Ver docs/iam-policy-ci-cd-proyectosacc.json como
|
||||||
|
# base para construir la política restrictiva.
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
resource "aws_iam_role_policy_attachment" "bitbucket_ci_cd_admin" {
|
||||||
|
role = aws_iam_role.bitbucket_ci_cd.name
|
||||||
|
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# Política mínima alternativa (descomentar cuando esté lista)
|
||||||
|
# -------------------------------------------------------------------------------
|
||||||
|
# resource "aws_iam_role_policy" "bitbucket_ci_cd_minimal" {
|
||||||
|
# name = "${var.project_name}-bitbucket-ci-cd-minimal-${var.environment}"
|
||||||
|
# role = aws_iam_role.bitbucket_ci_cd.id
|
||||||
|
#
|
||||||
|
# policy = file("${path.module}/../docs/iam-policy-ci-cd-proyectosacc.json")
|
||||||
|
# }
|
||||||
@@ -17,6 +17,10 @@ terraform {
|
|||||||
source = "hashicorp/aws"
|
source = "hashicorp/aws"
|
||||||
version = ">= 5.94.0"
|
version = ">= 5.94.0"
|
||||||
}
|
}
|
||||||
|
tls = {
|
||||||
|
source = "hashicorp/tls"
|
||||||
|
version = ">= 4.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user