Módulo 5: Securing The Ai Workload
2. Mínimo privilegio para un modelo, no para un servicio
Descripción
El Módulo 3, lección 4 construyó BedrockManifestExtractorRole: un rol IAM cuya política de permisos acota bedrock:InvokeModel al ARN exacto de un único modelo —arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-lite-v1:0—, nunca al servicio completo. En ese momento, la única verificación disponible fue leer el JSON en la salida de terraform plan con tus propios ojos. Esta lección nombra, con precisión, el antipatrón exacto que esa disciplina evita —bedrock:*—, y prepara el terreno para que la lección 3 lo convierta en una regla que se aplica sola, cada vez, sin depender de que alguien se acuerde de revisar.
Conexión con el módulo
cloud-security-and-guardrails-guide, Módulo 2, ya estableció mínimo privilegio como disciplina general de IAM —y su Módulo 4, lección 7, escribió least-privilege-iam.rego, la política que detecta cualquier Statement con Action: "*" en cualquier aws_iam_role_policy del proyecto—. Esta lección no repite esa disciplina desde cero: la aplica, por primera vez en este ecosistema, a un dominio donde el "todo" que hay que evitar no es un asterisco genérico, sino un espacio de acciones específico de un servicio (bedrock:*) que la política genérica de esa guía no está diseñada para detectar.
Analogía: la llave de una sala, retomada bajo un lente distinto
El Módulo 3, lección 4 de esta misma guía ya usó esta analogía: una llave que abre exactamente la sala donde alguien trabaja, no la llave maestra que abre todo el edificio. Vale la pena volver a ella aquí, porque esta lección le agrega algo que esa primera versión no tenía: quién revisa que la llave correcta se entregó. En un edificio real, alguien —un oficial de seguridad, un proceso de auditoría— tiene que confirmar, de forma repetible, que cada llave nueva que se corta de verdad abre solo la sala asignada, y no "por error" también abre el cuarto de servidores tres pisos más abajo. Sin esa confirmación, la disciplina de "solo la llave de tu sala" depende enteramente de que la persona que corta la llave nunca se equivoque — una apuesta razonable la primera vez, una apuesta arriesgada la vigésima.
bedrock-least-privilege.rego, que la lección 3 construye, es ese oficial de seguridad, convertido en código. No corta la llave —eso lo sigue haciendo el HCL de bedrock.tf—, pero confirma, cada vez que alguien propone un cambio, que la llave que se está a punto de cortar de verdad abre solo lo que debería.
El antipatrón exacto: bedrock:*, y por qué es peor de lo que suena
Es fácil escuchar "no uses comodines" como una regla de estilo, casi cosmética. No lo es. Vale la pena ser preciso sobre qué autoriza realmente bedrock:* en una política IAM, comparado con lo que Andes Cargo necesita:
LO QUE extract-shipment-manifest-fields NECESITA LO QUE "bedrock:*" AUTORIZA
bedrock:InvokeModel bedrock:InvokeModel
sobre UN modelo bedrock:InvokeModelWithResponseStream
(amazon.nova-lite-v1:0) bedrock:CreateGuardrail
bedrock:DeleteGuardrail
bedrock:UpdateGuardrail
bedrock:CreateModelInvocationJob
bedrock:PutModelInvocationLoggingConfiguration
bedrock:CreateProvisionedModelThroughput
bedrock:DeleteProvisionedModelThroughput
... (docenas de acciones más)
La lista de la derecha no es hipotética ni exagerada: son acciones reales del espacio de nombres bedrock: en el catálogo de acciones IAM de AWS. Un rol con bedrock:* puede, entre muchas otras cosas, borrar el guardrail que el Módulo 4 de esta guía construyó (bedrock:DeleteGuardrail), crear infraestructura de facturación nueva (bedrock:CreateProvisionedModelThroughput, que empieza a cobrar por hora, no por token), o modificar la configuración de registro de invocaciones (bedrock:PutModelInvocationLoggingConfiguration) — nada de eso tiene que ver con lo que extract-shipment-manifest-fields necesita para hacer su trabajo: leer texto de un manifiesto y pedirle a un modelo que extraiga campos.
Esto es lo que separa "mínimo privilegio como buena práctica general" de "mínimo privilegio aplicado con precisión a un servicio específico": la pregunta correcta nunca es "¿evité el asterisco?" en abstracto — es "¿el conjunto exacto de acciones que este rol puede hacer coincide, uno a uno, con lo que este código necesita hacer, ni una acción de más?". BedrockManifestExtractorRole, tal como el Módulo 3 lo dejó, contesta esa pregunta correctamente: una sola acción (bedrock:InvokeModel), un solo recurso (el ARN de un modelo).
Por qué el ARN también importa, no solo la acción
Hay una segunda dimensión del mismo problema, y es tan real como la primera: incluso si un rol acota correctamente la acción a bedrock:InvokeModel —nunca bedrock:*—, todavía podría dejar el recurso abierto:
{
"Action": "bedrock:InvokeModel",
"Effect": "Allow",
"Resource": "*"
}
Esta política es, en la superficie, más restrictiva que bedrock:* — solo permite invocar modelos, nada más. Pero Resource: "*" significa que extract-shipment-manifest-fields podría invocar cualquier modelo disponible en la cuenta, no solo Amazon Nova Lite. En un escenario real, eso importa: distintos modelos de Bedrock tienen distintos precios por token —Nova Premier cuesta varias veces más por millón de tokens que Nova Lite (Módulo 2, lección 2 de esta guía)—, así que un rol con Resource: "*" no solo amplía la superficie de seguridad, amplía la superficie de costo: un error de código, o una credencial comprometida, podría empezar a facturar contra un modelo mucho más caro sin que ningún límite de IAM lo impida. BedrockManifestExtractorRole cierra las dos dimensiones a la vez: una acción, un recurso.
Los dos vectores de ataque que la lección 3 va a bloquear con código
VECTOR 1 -- Acción demasiado amplia VECTOR 2 -- Recurso demasiado amplio
"Action": "bedrock:*" "Action": "bedrock:InvokeModel"
"Resource": "*"
Autoriza crear/borrar guardrails, Autoriza invocar CUALQUIER modelo
provisionar throughput, modificar de la cuenta, no solo Nova Lite --
logging -- superficie de seguridad superficie de COSTO, no solo de
completa del servicio seguridad
bedrock-least-privilege.rego, regla 1 bedrock-least-privilege.rego, regla 2
Los dos vectores comparten una misma raíz —"more than the code actually needs"—, pero merecen dos reglas deny independientes, no una sola regla combinada: son preguntas distintas ("¿la acción es demasiado amplia?" y "¿el recurso es demasiado amplio, dado que la acción sí es la correcta?"), y combinarlas en una sola regla produciría el mismo problema que cloud-security-and-guardrails-guide, Módulo 4, lección 7 ya evitó con no-public-buckets.rego: un mensaje de error que no distingue con precisión cuál de los dos problemas reales se encontró.
Errores comunes
Pensar que acotar la acción (bedrock:InvokeModel, no bedrock:*) ya es suficiente, sin revisar el recurso (de resolver mitad del problema). Qué pasa: alguien escribe "Action": "bedrock:InvokeModel" con cuidado, se siente satisfecho de haber evitado el comodín obvio, y no revisa que "Resource" siga siendo "*". Cómo detectarlo: si tu política tiene una acción específica pero un Resource genérico, sin ningún ARN citado. Cómo corregirlo: mínimo privilegio en Bedrock es una decisión de dos ejes, acción y recurso — la lección de arriba lo desarrolla con precisión. BedrockManifestExtractorRole acota ambos; una política que solo acota uno de los dos sigue siendo, en la práctica, más amplia de lo necesario.
Argumentar que bedrock:* "es más simple de mantener" porque nunca hay que actualizar la política cuando se agrega un modelo nuevo (de conveniencia sobre disciplina). Qué pasa: alguien, anticipando que Andes Cargo podría querer probar un modelo distinto en el futuro, prefiere dejar el acceso abierto "para no tener que tocar el HCL después". Cómo detectarlo: si la justificación de una política amplia es sobre comodidad futura, no sobre una necesidad actual. Cómo corregirlo: la misma disciplina que terraform-and-iac-guide y cloud-security-and-guardrails-guide ya establecieron para otros recursos aplica aquí sin excepción — declara lo que el código necesita hoy; si Andes Cargo decide invocar un segundo modelo mañana, ese cambio es una línea nueva en resources = [...], revisada por el mismo policy-check que este módulo construye, no una razón para dejar la puerta abierta desde ahora.
Confundir "least privilege" a nivel de acción de IAM con "least privilege" a nivel de contenido del prompt (de mezclar dos capas de seguridad distintas). Qué pasa: alguien, después de leer esta lección, empieza a hablar de "mínimo privilegio del prompt" —qué información se le pasa al modelo— como si fuera el mismo concepto que el mínimo privilegio de IAM de esta lección. Cómo detectarlo: si tu explicación de esta lección empieza a mencionar qué texto se le envía a Bedrock, en vez de qué acciones puede invocar el rol. Cómo corregirlo: son capas completamente distintas, con dueños distintos. Qué información llega al modelo es responsabilidad del guardrail (Módulo 4) y del prompt mismo (frontera con AI Engineering, Módulo 1, lección 4); qué acciones de AWS puede invocar el código que llama al modelo es responsabilidad de IAM (esta lección, y el Módulo 3, lección 4). Ninguna de las dos reemplaza a la otra.
Ejercicios
Ejercicio 1 — Sin mirar bedrock.tf, escribe de memoria la política IAM completa (acción y recurso) que BedrockManifestExtractorRole debería tener, en formato JSON. Después, compárala contra el Módulo 3, lección 4 de esta guía.
Ver solución
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "InvokeManifestExtractionModelOnly",
"Effect": "Allow",
"Action": "bedrock:InvokeModel",
"Resource": "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-lite-v1:0"
}
]
}
Una sola acción, un solo recurso, con un Sid que describe exactamente qué autoriza este Statement — el mismo JSON, literal, que el Módulo 3, lección 4 mostró en su terraform plan.
Ejercicio 2 — Explica por qué bedrock:DeleteGuardrail dentro del alcance de bedrock:* es, en los hechos, más peligroso para Andes Cargo que una fuga de PII individual. Piensa en qué protege el guardrail del Módulo 4, y qué pasaría si esa protección desapareciera para todos los manifiestos futuros, no solo uno.
Ver solución
Una fuga de PII individual —un correo electrónico que se filtra en la respuesta de una sola invocación— es un incidente acotado, doloroso pero limitado a ese caso específico. bedrock:DeleteGuardrail, en cambio, si se ejecutara por error o por una credencial comprometida, borraría el guardrail gestionado —las seis políticas del Módulo 4— para todas las invocaciones futuras, hasta que alguien lo notara y lo volviera a declarar con terraform apply. Es la diferencia entre un incidente de datos puntual y la remoción silenciosa de una capa entera de defensa, sin que nada en el sistema lo señale de inmediato — la razón por la que bedrock:* es categóricamente distinto de "un poco más de acceso del necesario": abre la puerta a que el propio mecanismo de protección se desactive.
Ejercicio 3 — Diseña, en prosa, un tercer vector de ataque que bedrock-least-privilege.rego (lección 3) NO cubre, relacionado con IAM pero fuera del alcance de "acción" y "recurso" de un solo Statement. Pista: piensa en quién puede asumir el rol, no en qué puede hacer una vez asumido.
Ver solución
Una respuesta razonable: la política de confianza (assume_role_policy) de BedrockManifestExtractorRole podría, en teoría, declarar un principal demasiado amplio —por ejemplo, cualquier servicio de AWS ("Service": "*" en vez de "lambda.amazonaws.com" específicamente), o incluso una cuenta externa completa—, lo cual permitiría que un recurso completamente distinto al Lambda extract-shipment-manifest-fields asumiera este rol y heredara su permiso de invocar el modelo. bedrock-least-privilege.rego, tal como se diseña en la lección 3, evalúa la política de permisos (inline_policy_json/permissions_policy_json), no la de confianza — un vector real, pero fuera del alcance específico de esta política. Cerrarlo requeriría una regla adicional, dedicada a assume_role_policy, siguiendo el mismo patrón que least-privilege-iam.rego ya usa para otros roles del proyecto.
Resumen y siguiente paso
Esta lección nombró, con precisión y sin exagerar, el antipatrón exacto que este módulo existe para bloquear: bedrock:* como acción (autoriza crear, borrar y reconfigurar recursos de Bedrock enteros, no solo invocar un modelo) y Resource: "*" como recurso (autoriza invocar cualquier modelo de la cuenta, con implicaciones de costo además de seguridad). Viste los dos vectores como preguntas independientes, cada una con su propia regla deny en la política que la lección 3 construye, y confirmaste que BedrockManifestExtractorRole, tal como el Módulo 3 lo dejó, ya contesta ambas preguntas correctamente.
Antes de avanzar deberías poder: explicar por qué bedrock:* es cualitativamente distinto de "un poco de acceso extra"; nombrar los dos ejes (acción, recurso) que mínimo privilegio de Bedrock necesita cubrir; y anticipar por qué esta lección necesitaba dos reglas deny, no una.
La lección 3 convierte esta tesis en código real: policy/bedrock-least-privilege.rego, corrido de verdad contra el plan del Módulo 3, con un PASS sobre el rol correcto y un FAIL real sobre un intento con bedrock:*.
Recursos
- AWS Docs — Actions, resources, and condition keys for Amazon Bedrock — catálogo oficial completo de las acciones del espacio de nombres
bedrock:, la fuente de la lista citada en esta lección. - Módulo 3, lección 4 de esta guía (
04-hands-on-bedrockmanifestextractorrole-least-privilege.md) — la construcción original del rol que esta lección revisita. cloud-security-and-guardrails-guide, Módulo 4, lección 7 (least-privilege-iam.rego) — la disciplina general de mínimo privilegio de IAM que esta lección aplica a un dominio específico.- Módulo 2, lección 2 de esta guía — los precios por token de distintos modelos de Bedrock, la fuente de la preocupación de costo detrás de acotar
Resource.