TL;DR

  • Usa la API ValidatePolicy de AWS IAM Access Analyzer en CI/CD para detectar políticas excesivamente permisivas antes del deployment—es gratis y detecta errores gramaticales más violaciones de mejores prácticas
  • Combina análisis estático (Checkov) con simulación de políticas (IAM Policy Simulator) para cobertura completa—estático detecta patrones, simulación detecta comportamiento en runtime
  • El testing IAM cross-cloud requiere herramientas diferentes: AWS Access Analyzer, Azure Policy Insights, GCP Policy Analyzer cada uno tiene capacidades únicas

Ideal para: Equipos gestionando políticas IAM en IaC que necesitan prevenir escalación de privilegios y asegurar mínimo privilegio No recomendado si: Usas servicios de identidad gestionados donde las políticas están abstraídas (ej., AWS SSO con permission sets predefinidos) Tiempo de lectura: 14 minutos

Las políticas IAM excesivamente permisivas siguen siendo una de las principales causas de brechas cloud. Un solo "Resource": "*" o condición faltante puede exponer toda tu cuenta AWS. Estudios muestran que 82% de las misconfiguraciones cloud provienen de error humano—haciendo el testing automatizado de políticas IAM esencial, no opcional.

Para contexto más amplio sobre testing de infraestructura, consulta Testing de Policy as Code y Testing de Compliance para IaC.

Enfoques Asistidos por IA

Las herramientas de IA destacan analizando políticas IAM complejas y generando suites de tests comprehensivas.

Analizando políticas IAM por permisos excesivos:

Analiza esta política IAM de AWS por problemas de seguridad:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:*"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["iam:PassRole"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": ["ec2:*"],
      "Resource": "*",
      "Condition": {
        "StringEquals": {"ec2:Region": "us-east-1"}
      }
    }
  ]
}

Identifica: Riesgos de escalación de privilegios, permisos excesivamente amplios,
condiciones faltantes, y proporciona una alternativa de mínimo privilegio para
un rol de pipeline CI/CD.

Generando casos de test de políticas IAM:

Genera casos de test pytest para validar políticas IAM de AWS usando boto3:

1. Verificar que ninguna política permite iam:* o iam:PassRole a Resource "*"
2. Verificar que políticas S3 requieren condiciones de encriptación
3. Asegurar que ninguna política permite assume-role a cuentas externas sin condiciones
4. Validar que políticas admin solo están adjuntas a roles break-glass

Incluye setup apropiado de sesión AWS, parsing de documentos de política y
assertions claras.

Creando checks personalizados de Checkov:

Escribe un check personalizado de Checkov en Python que valide:

1. Las políticas IAM no deben permitir acciones terminando en "*" (ej., s3:*, ec2:*)
2. Todas las declaraciones iam:PassRole deben tener ARNs de Resource explícitos
3. Políticas con acciones administrativas deben tener condiciones MFA
4. Políticas de trust cross-account deben especificar IDs de cuenta explícitos

Incluye la clase del check, lógica de evaluación y tipos de recursos soportados.

Cuándo Usar Diferentes Enfoques de Testing

Framework de Decisión de Estrategia de Testing

Tipo de TestHerramientaCuándo EjecutarQué Detecta
Validación gramaticalIAM Access AnalyzerPre-commit, CIErrores de sintaxis, elementos deprecados
Mejores prácticasAccess Analyzer ValidatePolicyPipeline CIAcciones excesivamente permisivas, condiciones faltantes
Análisis estáticoCheckov, cfn-nagPre-commit, CIPatrones malos conocidos (Resource: “*”)
Simulación de políticasIAM Policy SimulatorPre-deployComportamiento real de permisos
Análisis de accesoHallazgos Access AnalyzerContinuoAcceso externo, permisos no usados
Reglas personalizadasOPA/RegoPipeline CIRequisitos específicos de organización

Checks Críticos de Políticas IAM

Check IDDescripciónNivel de Riesgo
CKV_AWS_1Políticas IAM no deben permitir privilegios administrativos completos “*”Crítico
CKV_AWS_49Políticas IAM no deben permitir PassRole a “*”Crítico
CKV_AWS_40Políticas IAM no deben permitir escalación de privilegiosCrítico
CKV_AWS_62Políticas IAM no deben tener SID vacíoBajo
CKV_AWS_109Políticas IAM no deben permitir exposición de credencialesAlto
CKV_AWS_110Políticas IAM no deben permitir gestión de permisos sin restriccionesAlto

Testing de Políticas IAM en AWS

Validación de Políticas con IAM Access Analyzer

# Instalar el validador de políticas IAM para Terraform
pip install tf-policy-validator

# Validar template Terraform
tf-policy-validator validate \
  --template-path ./terraform \
  --region us-east-1

# Ejecutar checks específicos
tf-policy-validator validate \
  --template-path ./terraform \
  --region us-east-1 \
  --check-type VALIDATE_POLICY \
  --check-type CHECK_NO_NEW_ACCESS

Integración con GitHub Actions

name: Validación de Políticas IAM

on:
  pull_request:
    paths:

      - 'terraform/**/*.tf'
      - 'cloudformation/**/*.yaml'

jobs:
  validate-iam-policies:
    runs-on: ubuntu-latest
    steps:

      - uses: actions/checkout@v4

      - name: Configurar credenciales AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Validar Políticas IAM Terraform
        uses: aws-actions/terraform-aws-iam-policy-validator@v1
        with:
          template-path: terraform/
          region: us-east-1
          check-no-new-access: true
          check-access-not-granted: true
          check-no-public-access: true

      - name: Ejecutar Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: terraform/
          check: CKV_AWS_1,CKV_AWS_40,CKV_AWS_49,CKV_AWS_109,CKV_AWS_110
          framework: terraform

Checkov para Políticas IAM

# Instalar Checkov
pip install checkov

# Escanear por problemas específicos de IAM
checkov -d ./terraform --check CKV_AWS_1,CKV_AWS_40,CKV_AWS_49

# Output como JUnit para CI
checkov -d ./terraform --framework terraform \
  --check CKV_AWS_1,CKV_AWS_40,CKV_AWS_49,CKV_AWS_109,CKV_AWS_110 \
  --output junitxml > iam-checkov-results.xml

Check Personalizado de Checkov

# custom_checks/iam_no_passrole_star.py
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
from checkov.common.models.enums import CheckResult, CheckCategories
import json

class IAMNoPassRoleStar(BaseResourceCheck):
    def __init__(self):
        name = "Asegurar que políticas IAM no permiten PassRole a todos los recursos"
        id = "CKV_CUSTOM_IAM_1"
        supported_resources = ['aws_iam_policy', 'aws_iam_role_policy']
        categories = [CheckCategories.IAM]
        super().__init__(name=name, id=id, categories=categories,
                        supported_resources=supported_resources)

    def scan_resource_conf(self, conf):
        policy = conf.get('policy', [None])[0]
        if not policy:
            return CheckResult.UNKNOWN

        # Manejar formatos string y dict
        if isinstance(policy, str):
            try:
                policy = json.loads(policy)
            except json.JSONDecodeError:
                return CheckResult.UNKNOWN

        statements = policy.get('Statement', [])
        for statement in statements:
            if statement.get('Effect') != 'Allow':
                continue

            actions = statement.get('Action', [])
            if isinstance(actions, str):
                actions = [actions]

            resources = statement.get('Resource', [])
            if isinstance(resources, str):
                resources = [resources]

            # Verificar PassRole con recurso *
            has_passrole = any('PassRole' in action for action in actions)
            has_star_resource = '*' in resources

            if has_passrole and has_star_resource:
                return CheckResult.FAILED

        return CheckResult.PASSED

check = IAMNoPassRoleStar()

Testing de Políticas con boto3 en Python

import boto3
import json
import pytest

class TestIAMPolicies:
    @pytest.fixture
    def iam_client(self):
        return boto3.client('iam')

    @pytest.fixture
    def access_analyzer_client(self):
        return boto3.client('accessanalyzer')

    def test_no_admin_star_policies(self, iam_client):
        """Verificar que ninguna política otorga acceso admin completo."""
        paginator = iam_client.get_paginator('list_policies')

        for page in paginator.paginate(Scope='Local'):
            for policy in page['Policies']:
                version = iam_client.get_policy_version(
                    PolicyArn=policy['Arn'],
                    VersionId=policy['DefaultVersionId']
                )

                document = version['PolicyVersion']['Document']
                statements = document.get('Statement', [])

                for stmt in statements:
                    if stmt.get('Effect') != 'Allow':
                        continue

                    actions = stmt.get('Action', [])
                    if isinstance(actions, str):
                        actions = [actions]

                    resources = stmt.get('Resource', [])
                    if isinstance(resources, str):
                        resources = [resources]

                    # Fallar si Action: "*" y Resource: "*"
                    assert not (
                        '*' in actions and '*' in resources
                    ), f"Política {policy['PolicyName']} tiene privilegios admin"

    def test_validate_policy_with_access_analyzer(self, access_analyzer_client):
        """Usar Access Analyzer para validar política."""
        policy_document = {
            "Version": "2012-10-17",
            "Statement": [{
                "Effect": "Allow",
                "Action": ["s3:GetObject"],
                "Resource": "arn:aws:s3:::my-bucket/*"
            }]
        }

        response = access_analyzer_client.validate_policy(
            policyDocument=json.dumps(policy_document),
            policyType='IDENTITY_POLICY'
        )

        # Verificar errores y advertencias de seguridad
        findings = response.get('findings', [])
        errors = [f for f in findings if f['findingType'] == 'ERROR']
        security_warnings = [f for f in findings
                           if f['findingType'] == 'SECURITY_WARNING']

        assert len(errors) == 0, f"Errores de validación: {errors}"
        assert len(security_warnings) == 0, f"Advertencias de seguridad: {security_warnings}"

Testing con IAM Policy Simulator

def test_policy_denies_unauthorized_actions(iam_client):
    """Simular política para verificar acciones denegadas."""
    # Probar que rol de desarrollador no puede acceder a producción
    response = iam_client.simulate_principal_policy(
        PolicySourceArn='arn:aws:iam::123456789012:role/DeveloperRole',
        ActionNames=[
            'ec2:TerminateInstances',
            's3:DeleteBucket',
            'iam:CreateUser'
        ],
        ResourceArns=[
            'arn:aws:ec2:us-east-1:123456789012:instance/*',
            'arn:aws:s3:::production-*',
            'arn:aws:iam::123456789012:user/*'
        ]
    )

    for result in response['EvaluationResults']:
        assert result['EvalDecision'] == 'implicitDeny', \
            f"Rol desarrollador no debería tener permiso {result['EvalActionName']}"

def test_policy_allows_required_actions(iam_client):
    """Verificar que rol tiene permisos requeridos."""
    response = iam_client.simulate_principal_policy(
        PolicySourceArn='arn:aws:iam::123456789012:role/CICDRole',
        ActionNames=[
            's3:PutObject',
            'ecr:PushImage',
            'ecs:UpdateService'
        ],
        ResourceArns=[
            'arn:aws:s3:::deployment-artifacts/*',
            'arn:aws:ecr:us-east-1:123456789012:repository/my-app',
            'arn:aws:ecs:us-east-1:123456789012:service/my-cluster/my-service'
        ]
    )

    for result in response['EvaluationResults']:
        assert result['EvalDecision'] == 'allowed', \
            f"Rol CICD sin permiso requerido: {result['EvalActionName']}"

Testing IAM en Azure

Azure Policy para Validación de RBAC

# Listar definiciones de roles personalizados
az role definition list --custom-role-only true --output table

# Verificar asignaciones de roles
az role assignment list --scope /subscriptions/<sub-id> --output table

# Validar con Azure Policy
az policy assignment list --query "[?policyDefinitionId contains 'rbac']"

InSpec para Azure RBAC

control 'azure-no-owner-custom-roles' do
  impact 1.0
  title 'Roles personalizados no deben tener permisos equivalentes a Owner'

  azure_role_definitions.where(role_type: 'CustomRole').ids.each do |role_id|
    describe azure_role_definition(name: role_id) do
      its('permissions.first.actions') { should_not include '*' }
      its('permissions.first.not_actions') { should_not be_empty }
    end
  end
end

control 'azure-no-subscription-owner-assignments' do
  impact 1.0
  title 'Rol Owner no debe asignarse a nivel de suscripción'

  describe azure_role_assignments.where(
    scope: "/subscriptions/#{input('subscription_id')}",
    role_name: 'Owner'
  ) do
    its('count') { should be <= 2 }  # Solo permitir cuentas break-glass
  end
end

Testing IAM en GCP

GCP Policy Analyzer

# Analizar política IAM de un proyecto
gcloud policy-intelligence analyze-iam-policy \
  --project=my-project \
  --full-resource-name="//cloudresourcemanager.googleapis.com/projects/my-project"

# Verificar bindings excesivamente permisivos
gcloud projects get-iam-policy my-project --format=json | \
  jq '.bindings[] | select(.members[] | contains("allUsers") or contains("allAuthenticatedUsers"))'

Validación Terraform para GCP IAM

# variables.tf - Definir roles permitidos
variable "allowed_project_roles" {
  type = list(string)
  default = [
    "roles/viewer",
    "roles/editor",
    # Listar explícitamente roles permitidos
  ]
}

# Validación en recurso
resource "google_project_iam_binding" "binding" {
  project = var.project_id
  role    = var.role
  members = var.members

  lifecycle {
    precondition {
      condition     = contains(var.allowed_project_roles, var.role)
      error_message = "Rol ${var.role} no está en la lista de roles permitidos."
    }

    precondition {
      condition     = !contains(var.members, "allUsers") && !contains(var.members, "allAuthenticatedUsers")
      error_message = "Bindings IAM públicos (allUsers/allAuthenticatedUsers) no están permitidos."
    }
  }
}

Integración en Pipeline CI/CD

Workflow Completo de GitHub Actions

name: Seguridad de Políticas IAM

on:
  pull_request:
    paths:

      - 'terraform/iam/**'
      - 'policies/**'

jobs:
  static-analysis:
    runs-on: ubuntu-latest
    steps:

      - uses: actions/checkout@v4

      - name: Ejecutar checks IAM de Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: terraform/iam/
          check: CKV_AWS_1,CKV_AWS_40,CKV_AWS_49,CKV_AWS_109,CKV_AWS_110
          output_format: sarif
          output_file_path: checkov-iam.sarif

      - name: Subir SARIF
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: checkov-iam.sarif

  access-analyzer:
    runs-on: ubuntu-latest
    needs: static-analysis
    steps:

      - uses: actions/checkout@v4

      - name: Configurar credenciales AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: us-east-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        run: terraform init
        working-directory: terraform/iam/

      - name: Validar con Access Analyzer
        uses: aws-actions/terraform-aws-iam-policy-validator@v1
        with:
          template-path: terraform/iam/
          region: us-east-1
          check-no-new-access: true

  policy-simulation:
    runs-on: ubuntu-latest
    needs: access-analyzer
    if: github.event.pull_request.draft == false
    steps:

      - uses: actions/checkout@v4

      - name: Configurar credenciales AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: us-east-1

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Instalar dependencias
        run: pip install boto3 pytest

      - name: Ejecutar tests de simulación de políticas
        run: pytest tests/iam/ -v --tb=short

Midiendo el Éxito

MétricaAntes del TestingDespués del TestingCómo Rastrear
Políticas excesivamente permisivasEncontradas en auditorías0 en producciónReportes Checkov
Paths de escalación de privilegiosDesconocidos0 detectadosHallazgos Access Analyzer
Tiempo de validación de políticasRevisión manual (horas)Automatizado (minutos)Métricas CI/CD
Permisos no usadosAcumulados con el tiempoLimpieza trimestralAccess Analyzer

Señales de que tu testing IAM no funciona:

  • Checkov pasa pero hallazgos de Access Analyzer en producción
  • Cambios de política de “emergencia” evadiendo CI/CD
  • Roles acumulando permisos con el tiempo
  • Cuentas de servicio con privilegios admin

Conclusión

El testing efectivo de políticas IAM requiere múltiples capas de validación:

  1. Análisis estático (Checkov) detecta patrones malos conocidos en código
  2. Validación gramatical (Access Analyzer ValidatePolicy) asegura sintaxis y estructura de políticas
  3. Simulación de políticas (IAM Policy Simulator) verifica comportamiento real de permisos
  4. Análisis continuo (hallazgos de Access Analyzer) detecta drift y permisos no usados

La idea clave: las políticas IAM son demasiado críticas para revisión manual solamente. Testing automatizado en pipelines CI/CD detecta problemas antes de llegar a producción, mientras el monitoreo continuo detecta drift de políticas con el tiempo.

Ver También

Recursos Oficiales