Módulo 7: Detective Vs Preventive Guardrails

2. Guardrails preventivos: SCPs y permission boundaries

Descripción

Esta lección es puramente conceptual — la única de este bloque sin ningún HCL nuevo —, y existe para que cuando la lección 3 te ponga a declarar un aws_iam_policy real como permission boundary, no estés aprendiendo el concepto y la sintaxis al mismo tiempo. Aquí separas dos preguntas: qué es un permission boundary, y cómo se relaciona con la Service Control Policy (SCP) que la lección 6 nombra — de cómo se declara uno en HCL, exactamente, en la lección 3.

Conexión con el módulo

La lección 1 clasificó no-destroy-shipments.rego (M4) y least-privilege-iam.rego (M4) como preventivos: evalúan un plan antes de que exista la posibilidad de aplicarlo. Un permission boundary es preventivo por una razón distinta, más cercana en el tiempo: no evalúa un plan antes del apply — evalúa una llamada individual a la API de AWS, en el momento exacto en que esa llamada ocurre, sin importar si el recurso que la hizo fue creado hace un año o hace un segundo. Es la misma categoría (preventivo), un punto distinto del ciclo de vida (tiempo de ejecución, no tiempo de planificación).


Analogía: la llave maestra con un límite grabado en el metal

Vuelve a la llave maestra de la lección 7 del Módulo 2: un edificio con un sistema de llaves mal diseñado le da a cada empleado una llave que abre todas las puertas. Esa lección recortó las llaves de AppServerRole y LambdaManifestProcessorRole a exactamente la puerta que cada una necesita — una política de permisos más angosta. Pero imagina ahora un segundo mecanismo, independiente del primero: cada llave, además de sus dientes específicos, tiene un límite físico grabado en el propio metal — un tope que ninguna combinación de dientes puede sortear, sin importar qué tan permisiva sea la cerradura que la acepte. Aunque alguien, por error, le fabricara a esa llave dientes que abrieran la sala de servidores, el tope grabado en el metal seguiría impidiendo que la llave gire en esa cerradura específica.

Un permission boundary es ese tope grabado en el metal. La política de permisos de un rol (lo que la lección 7 del Módulo 2 recortó) sigue siendo los dientes de la llave — define qué puede hacer, en principio. El permission boundary es un límite independiente, adjunto al rol mismo, que ninguna política de permisos —ni siquiera una futura, escrita por error con alcance demasiado amplio— puede superar.


Qué es un permission boundary, en los términos exactos de AWS

La documentación oficial de IAM lo define así, palabra por palabra: "A permissions boundary is an advanced feature for using a managed policy to set the maximum permissions that an identity-based policy can grant to an IAM entity. An entity's permissions boundary allows it to perform only the actions that are allowed by both its identity-based policies and its permissions boundaries". Dos piezas de esa definición valen la pena separar:

  • "Set the maximum permissions" — un permission boundary nunca otorga nada por sí mismo. Es, exclusivamente, un techo. Un rol con un boundary perfecto y ninguna política de permisos adjunta no puede hacer absolutamente nada — el boundary define el máximo posible, no lo que existe hoy.
  • "Allowed by both" — esta es la palabra que importa: both. Las acciones efectivas de un rol con boundary son la intersección de dos conjuntos: lo que su política de permisos permite, y lo que su boundary permite. Si la política de permisos dice "puede leer S3 y escribir DynamoDB" y el boundary dice "puede hacer cualquier cosa en S3, nada en IAM ni en Organizations", la intersección es exactamente "puede leer S3" — la escritura en DynamoDB desaparece, porque el boundary nunca la mencionó como permitida.
   EVALUACIÓN DE EFECTIVE PERMISSIONS CON UN PERMISSION BOUNDARY

   Política de permisos del rol        Permission boundary
   (lo que la lección 7 del M2         (el techo, esta lección)
    y la lección 3 de este módulo
    recortan)

   ┌─────────────────────┐            ┌─────────────────────┐
   │ s3:GetObject          │            │ s3:*  (Allow)         │
   │ s3:ListBucket          │  ∩        │ iam:*  (Deny)          │
   │                       │            │ organizations:*(Deny)  │
   │                       │            │ sts:AssumeRole (Deny)  │
   └─────────────────────┘            └─────────────────────┘
              │                                  │
              └───────────────┬──────────────────┘
                               ▼
                  PERMISOS EFECTIVOS: s3:GetObject, s3:ListBucket
                  (intersección — ninguna de las dos políticas por
                   sí sola decide el resultado final)

Fíjate en un detalle que la propia documentación de AWS resalta con un ejemplo: si le agregaras, por error, iam:CreateUser a la política de permisos de AppServerRole, el boundary de arriba lo bloquearía de todas formas —iam:* está explícitamente denegado en el boundary—, sin que nadie tuviera que revisar ni recordar que esa política de permisos existía. Ese es, con precisión, el valor del permission boundary: es una defensa que sigue funcionando incluso cuando la primera línea de defensa (la política de permisos) falla.


Qué es una Service Control Policy (SCP), y en qué se parece — y en qué no — a un permission boundary

La documentación oficial de AWS Organizations define una SCP de forma casi idéntica en espíritu: "SCPs do not grant permissions to the IAM users and IAM roles in your organization. No permissions are granted by an SCP. An SCP defines a permission guardrail, or sets limits, on the actions that the IAM users and IAM roles in your organization can perform". Es el mismo mecanismo —un techo, nunca un otorgamiento— aplicado a una escala completamente distinta:

Permission boundaryService Control Policy (SCP)
Se adjunta aUn usuario o rol IAM específicoUna cuenta completa, una unidad organizacional, o toda una organización
RequiereNada especial — cualquier cuenta de IAM lo soportaAWS Organizations con "all features enabled"
Afecta aSolo la entidad a la que está adjuntoTodos los usuarios y roles de la cuenta afectada, incluido el usuario root (con excepciones documentadas)
Esta guíaSe construye de verdad en la lección 3 (el recurso)Se nombra, sin construirse, en la lección 6 — Andes Cargo es una sola cuenta

La propia documentación de AWS lo confirma con precisión sobre cómo interactúan cuando ambos existen a la vez: "If both a permissions boundary (an advanced IAM feature) and an SCP are present, then the boundary, the SCP, and the identity-based policy must all allow the action" — una tercera intersección, no solo dos. Andes Cargo, con una sola cuenta y sin AWS Organizations activado, nunca tiene una SCP en juego — solo la intersección de dos políticas, la de permisos y el boundary. La lección 6 de este módulo vuelve sobre esto con más detalle, incluido por qué Andes Cargo no lo necesita todavía.


Por qué el enforcement real de ambos depende de la misma pieza exacta que ya viste en el M2.6

Esta es la conexión más importante de esta lección con el resto de la guía, y vale la pena decirla sin rodeos: el motor que evaluaría de verdad un permission boundary o una SCP contra una llamada real es la misma funcionalidad de LocalStack citada en el Módulo 2, lección 6IAM Policy Enforcement. La documentación oficial de LocalStack la describe con precisión: evalúa "identity-based policies, resource-based policies, permission boundaries, and Service Control Policies" — las cuatro, en la misma funcionalidad, con la misma restricción de plan confirmada textualmente: "Included in Plans: Base, Ultimate", sin el plan gratuito Hobby/Community que usa todo este ecosistema desde aws-core-services-guide.

La consecuencia práctica, exacta, para la lección 3: vas a declarar un aws_iam_policy real, usarlo como permissions_boundary de AppServerRole, y confirmar con terraform validate/plan que el HCL es correcto y se aplicaría sin errores — pero no vas a poder demostrar, contra este laboratorio, que una llamada real que el boundary debería bloquear queda de verdad bloqueada. Es exactamente el mismo límite del M2.6, aplicado ahora a un mecanismo distinto (boundary de identidad, no trust policy de federación), con la misma fuente citada.


Profundizando: por qué un permission boundary no es lo mismo que "una segunda política de permisos"

Un error conceptual común es pensar que un rol con boundary tiene, en la práctica, dos políticas de permisos que se suman. Es lo opuesto: se intersectan. Este detalle importa porque cambia por completo cómo se escribe un boundary bien diseñado — no listas "todo lo que este rol podría necesitar alguna vez" (esa es la política de permisos), listas "la categoría más amplia de cosas que este rol jamás debería poder hacer, pase lo que pase". Un boundary bien escrito suele ser mucho más simple que la política de permisos que limita: no necesita enumerar cada acción exacta, solo trazar la línea que ninguna política de permisos futura, por error o por diseño, debería poder cruzar.

Para AppServerRole específicamente —el rol que la lección 3 va a acotar—, esa línea es clara a partir del inventario del Módulo 1: el rol lee objetos de S3 y nada más (confirmado en awslocal iam get-role-policy, Módulo 1, lección 4). Un boundary razonable para ese rol permite operaciones de lectura sobre el bucket de Andes Cargo, y deniega explícitamente cualquier cosa relacionada con gestión de identidad (iam:*) o de la organización (organizations:*) — la categoría de acciones que, si alguna vez aparecieran por error en la política de permisos de un rol de solo lectura, representarían la escalada de privilegios más grave posible. Esa es, con precisión, la lógica que la lección 3 va a declarar en HCL.


Errores comunes

Pensar que un permission boundary otorga permisos por sí solo (el error más citado en la propia documentación de AWS). Qué pasa: alguien adjunta un boundary generoso a un rol nuevo, sin ninguna política de permisos, y espera que el rol ya pueda hacer algo. Cómo detectarlo: si tu rol tiene un boundary pero ningún aws_iam_role_policy (inline) ni ninguna política administrada adjunta, y esperas que funcione de todas formas. Cómo corregirlo: un boundary sin ninguna política de permisos dejaría al rol sin ningún permiso efectivo — la intersección de "cualquier cosa" (la política de permisos vacía) con "lo que el boundary permite" sigue siendo vacía. El boundary solo recorta lo que la política de permisos ya otorga; nunca sustituye a esa política.

Confundir un permission boundary con una SCP porque ambos usan la palabra "boundary"/"guardrail" en su documentación (de vocabulario). Qué pasa: alguien, leyendo ambas definiciones oficiales una al lado de la otra, asume que son el mismo mecanismo con dos nombres. Cómo detectarlo: si tu explicación de la diferencia entre los dos se limita a "uno es para roles y el otro para cuentas", sin mencionar el requisito de AWS Organizations. Cómo corregirlo: son mecanismos técnicamente distintos —diferentes tipos de recurso, diferente alcance de aplicación, y una SCP requiere una precondición (Organizations con todas las features activas) que un permission boundary nunca necesita—, aunque resuelvan el mismo tipo de problema (un techo que ninguna política de permisos puede superar) a escalas distintas.

Esperar que la lección 3 demuestre un bloqueo real contra LocalStack (de expectativa, ya anticipado en esta lección). Qué pasa: alguien, después de leer esta lección, espera que la siguiente incluya un comando awslocal que efectivamente falle con AccessDenied por culpa del boundary. Cómo detectarlo: si buscas, en la lección 3, un bloque "Qué esperar" que muestre una llamada real siendo rechazada. Cómo corregirlo: esta misma lección ya lo declaró con la fuente citada — IAM Policy Enforcement es una funcionalidad de los planes de pago Base y Ultimate. La lección 3 construye el recurso real, lo valida con el motor real de Terraform, y lee su forma con awslocal iam get-role (representativo) — el bloqueo mismo queda fuera del alcance de este laboratorio $0, exactamente como se documentó aquí.


Ejercicios

Ejercicio 1 — Calcula la intersección a mano. Un rol tiene esta política de permisos: s3:GetObject, s3:PutObject, dynamodb:Scan. Su permission boundary permite: s3:* (Allow), y deniega explícitamente dynamodb:* (Deny). ¿Cuáles son los permisos efectivos del rol?

Ver solución

Permisos efectivos: s3:GetObject, s3:PutObject. dynamodb:Scan desaparece de los permisos efectivos, aunque la política de permisos del rol lo incluya explícitamente — un Deny en cualquiera de las dos políticas (boundary o política de permisos) siempre gana sobre un Allow en la otra, la misma regla que la documentación oficial de AWS confirma: "An explicit deny in either of these policies overrides the allow". s3:GetObject y s3:PutObject sí quedan permitidos, porque ambas políticas —permisos y boundary— coinciden en permitirlos (el boundary con el comodín s3:*, que cubre ambas acciones específicas).

Ejercicio 2 — Explica, sin usar la palabra "boundary", qué le pasaría a AppServerRole si alguien, por error, le agregara iam:AttachRolePolicy a su política de permisos. Usando el boundary que esta lección describe para AppServerRole (deniega iam:* explícitamente), explica el resultado de ese error, contra una cuenta AWS real con IAM Policy Enforcement activo.

Ver solución

Una respuesta completa suena, más o menos, así: "Aunque la política de permisos del rol ahora incluyera esa acción, el techo independiente adjunto al rol la bloquearía de todas formas — ese techo deniega explícitamente cualquier acción relacionada con la gestión de identidad, sin excepción, sin importar qué diga la política de permisos. El error de configuración seguiría siendo un error que alguien debería corregir, pero no se traduciría en un permiso real: la llamada fallaría." El punto central es que el boundary actúa como una segunda capa de defensa, independiente de la primera — exactamente el motivo por el que la lección 3 lo construye para AppServerRole, el rol de solo lectura de Andes Cargo.

Ejercicio 3 — Decide si Andes Cargo necesita una SCP hoy, y justifica con el criterio de esta lección. Usando la tabla comparativa de esta lección (permission boundary vs. SCP), explica por qué Andes Cargo, con una sola cuenta AWS y sin AWS Organizations activado, no puede tener una SCP en juego hoy, sin importar qué tan bien diseñada estuviera.

Ver solución

Porque una SCP requiere una precondición estructural que Andes Cargo no tiene: "SCPs are available only in an organization that has all features enabled" — sin una organización de AWS Organizations, con todas las features activas, el tipo de recurso que representa una SCP simplemente no existe para aplicarse a nada. Esto no es una limitación de LocalStack ni de esta guía específicamente: es un requisito de la propia arquitectura de AWS Organizations, independiente de qué plan de LocalStack (o de qué cuenta AWS real) esté en juego. La lección 6 de este módulo desarrolla esto con más profundidad, incluido qué tendría que crecer en Andes Cargo antes de que una SCP tuviera sentido.


Resumen y siguiente paso

En esta lección aprendiste qué es un permission boundary —un techo independiente de la política de permisos de un rol, evaluado como intersección, nunca como suma— y en qué se parece y se diferencia de una Service Control Policy, la misma idea aplicada a una cuenta completa en vez de a un rol. Confirmaste, con la fuente exacta ya citada en el M2.6, por qué el enforcement real de ambos mecanismos depende de la misma funcionalidad de pago de LocalStack (IAM Policy Enforcement), y por qué eso no invalida el HCL que vas a construir — solo determina qué parte de la lección siguiente se puede probar contra este laboratorio y qué parte no.

Antes de avanzar deberías poder: explicar por qué un permission boundary es una intersección, no una suma, con un ejemplo numérico como el del Ejercicio 1; distinguir un permission boundary de una SCP en al menos dos dimensiones (alcance, precondición); y anticipar, sin que la lección 3 te lo diga, qué parte de esa lección va a ser ejecutable de verdad y qué parte va a quedar representativa.

La lección 3 construye el boundary real para AppServerRole: un aws_iam_policy completo, usado como permissions_boundary del módulo iam-role ya existente, validado con el motor real de Terraform.

Recursos

  1. AWS Docs — Permissions boundaries for IAM entities — la fuente oficial completa citada en esta lección, incluido el ejemplo de evaluación de permisos efectivos.
  2. AWS Docs — Service control policies (SCPs) — la fuente oficial completa de SCPs, incluida la precondición de "all features enabled" citada en el Ejercicio 3.
  3. LocalStack Docs — IAM Policy Enforcement — la misma fuente ya citada en el Módulo 2, lección 6: "Included in Plans: Base, Ultimate", evaluando políticas de identidad, de recursos, permission boundaries y SCPs.
  4. Este curso, Módulo 2, lección 6 — el experimento original que citó por primera vez esta funcionalidad de pago, aplicado entonces a una trust policy de federación OIDC.
  5. Este curso, Módulo 2, lección 7 — el recorte de AppServerRole a mínimo privilegio, la política de permisos que el boundary de la lección 3 va a complementar, no reemplazar.