Módulo 7: Detective Vs Preventive Guardrails

1. Introducción: la baranda frente a la cámara de seguridad

Descripción

RISK-MAP.md (Módulo 1) dejó una fila abierta que ningún módulo anterior tocó: TM-03, Repudiation, ningún trail de CloudTrail existe, control: CloudTrail (hasta donde LocalStack Hobby lo permite), módulo: M7. Este es ese módulo, y es también el que le pone nombre formal a una distinción que ya usaste, sin decirlo así, desde el Módulo 4: hay controles que impiden que algo malo pase, y hay controles que se limitan a dejar constancia de que pasó. Esta lección abre el vocabulario exacto — preventivo frente a detectivo — que gobierna las siete lecciones que siguen.

Si viniste leyendo esta guía en orden, ya conoces la mitad de este vocabulario sin el nombre: la lección 2 del Módulo 4 ("Qué es Open Policy Agent y Rego") cerraba con esta frase exacta: "Vas a ver esta distinción una vez más, con nombre explícito, en el Módulo 7 de esta guía, cuando compares controles preventivos contra controles detectivos como CloudTrail". Esta es esa vez.

Conexión con el módulo

Este módulo tiene ocho lecciones, y se leen en cuatro bloques de dos. Las lecciones 2 y 3 son el lado preventivo: qué son las Service Control Policies (SCP) y los permission boundaries, y una aws_iam_policy real usada como boundary de AppServerRole. Las lecciones 4 y 5 son el lado detectivo: qué hacen CloudTrail, Config y GuardDuty, y un intento honesto de correr CloudTrail hasta donde la cobertura de LocalStack llega. La lección 6 nombra el escalón que sigue después de un permission boundary de una sola cuenta — SCPs aplicadas de verdad a una jerarquía de cuentas — sin construirlo, porque Andes Cargo, tal como existe en este ecosistema, no lo necesita todavía. La lección 7 vuelve a terreno ejecutable: una alarma real sobre un evento de IAM sensible. La lección 8 cierra el módulo con el documento que este módulo entero existe para producir: la matriz completa de qué es preventivo, qué es detectivo, y qué tan construido está cada uno.


El mapa de este módulo: las 8 lecciones

   MÓDULO 7 — GUARDRAILS DETECTIVOS FRENTE A PREVENTIVOS

   M7.1  Introducción (esta)                        la baranda frente a la cámara
   M7.2  Preventivos: SCPs y permission boundaries   conceptual, misma fuente de pago que M2.6
   M7.3  Manos a la obra: boundary para AppServerRole  aws_iam_policy, EJECUTADO (el recurso)
   M7.4  Detectivos: CloudTrail, Config, GuardDuty   NOMBRADO/REPRESENTATIVO, con su fuente
   M7.5  Manos a la obra: CloudTrail hasta donde llega  aws_cloudtrail, EJECUTADO (el intento)
   M7.6  SCPs y multi-cuenta                          NOMBRADO — faltante de VALIDACION.md
   M7.7  Manos a la obra: alarma sobre evento IAM     EJECUTADO (el recurso)
   M7.8  Proyecto: el mapa de guardrails de Andes Cargo  EJECUTADO (el documento)
#LecciónQué construye
1Introducción (esta)El mapa del módulo; la metáfora que lo gobierna: preventivo = baranda, detectivo = cámara
2Guardrails preventivos: SCPs y permission boundariesConceptual — la misma funcionalidad de pago de LocalStack citada en el M2.6
3Manos a la obra: una permission boundary para AppServerRoleEjecutado (el recurso) + representativo (el bloqueo): aws_iam_policy como permissions_boundary
4Guardrails detectivos: CloudTrail, Config y GuardDuty, nombradosRepresentativo/nombrado: qué hace cada uno, y por qué
5Manos a la obra: CloudTrail, hasta donde llega LocalStackEjecutado (el intento honesto): aws_cloudtrail declarado, terraform validate real
6SCPs y multi-cuenta, nombradosNombrado: el faltante ALTA de VALIDACION.md sin guía asignada
7Manos a la obra: una alarma sobre un evento de IAM sensibleEjecutado: aws_cloudwatch_event_rule + aws_sns_topic, terraform validate/plan reales
8Proyecto: el mapa de guardrails de Andes CargoEjecutado (el documento): matriz preventivo/detectivo × implementado/representativo/nombrado

La metáfora que gobierna este módulo entero

Un balcón bien construido tiene dos cosas, no una. Tiene una baranda: una barrera física que hace que caerse sea, sencillamente, más difícil — nadie tiene que revisar nada después, porque el accidente que la baranda previene nunca llega a suceder. Y, en un edificio con seguridad seria, también tiene una cámara apuntando a esa misma zona: un dispositivo que no impide absolutamente nada por sí mismo — no detiene una caída, no sostiene a nadie — pero que, si algo pasa de todas formas, deja un registro exacto de qué pasó, cuándo, y quién estaba ahí. Un edificio con solo baranda y sin cámara no tiene ningún registro si la baranda falla o si alguien la sortea a propósito. Un edificio con solo cámara y sin baranda documenta perfectamente cada caída, sin haber evitado ninguna.

Esta guía usa exactamente ese vocabulario, tomado del mismo campo de la seguridad física, para dos categorías completas de controles en la nube:

  • Un guardrail preventivo no pudo pasar. Actúa antes de que el evento exista: rechaza la llamada, bloquea el apply, deniega el permiso. conftest/OPA (Módulo 4) es la versión más pura de esto que ya construiste — evalúa un plan antes de que un solo recurso se cree, modifique o destruya. Un permission boundary (este módulo, lección 3) es la misma idea aplicada a una llamada individual de IAM: incluso si la política del rol lo permitiera, el boundary puede negarlo, en el momento exacto de la llamada.
  • Un guardrail detectivo queda registro. Actúa después: no impidió nada, pero deja evidencia consultable de que algo ocurrió. CloudTrail (lecciones 4 y 5) es el ejemplo central de esta guía: cada llamada a la API de AWS, quién la hizo, cuándo, con qué resultado — un historial que no evitó ni una sola de esas llamadas, pero que responde, con evidencia, la pregunta que un guardrail preventivo nunca contesta: "¿qué pasó de verdad?".

Fíjate en algo importante antes de seguir: esta distinción no es una jerarquía de "mejor" y "peor". Un balcón con cámara y sin baranda es, objetivamente, más peligroso que uno con baranda y sin cámara — por eso esta guía, en su RISK-MAP.md (Módulo 1, lección 8), construye lo preventivo primero (M2-M4, M6) y deja lo detectivo para el final (este módulo). Pero un sistema de seguridad real necesita las dos cosas: la baranda reduce cuántas veces necesitas mirar la cámara; la cámara es lo único que te dice qué pasó las veces que la baranda, por la razón que sea, no estaba donde debía.


Ejemplo trabajado: clasificando lo que ya construiste

Antes de construir nada nuevo, vale la pena aplicar el vocabulario de esta lección a seis piezas que ya existen en andes-cargo-infra/, de módulos anteriores — la prueba de que esta distinción no es nueva, solo le faltaba el nombre:

PiezaMódulo¿Preventivo o detectivo?Por qué
no-destroy-shipments.regoM4.6PreventivoEvalúa el plan antes del apply; un intento de destruir Shipments nunca llega a ejecutarse
least-privilege-iam.regoM4.7PreventivoRechaza un plan que declara "Action": "*" antes de que esa política exista de verdad
Trivy / Checkov (trivy config)M5PreventivoEvalúa el HCL antes de cualquier apply, igual que conftest — con reglas de la comunidad, no propias
cosign verify-blobM6PreventivoEl apply.yml no continúa si la firma no verifica — bloquea antes de desplegar un artefacto alterado
OIDC + trust policy acotadaM2.4-2.5PreventivoLa condición sobre sub decide, antes de emitir credenciales, si esa rama tiene permiso de asumir el rol
THREAT-MODEL.md / RISK-MAP.mdM1Ninguno de los dosSon documentos de análisis, no controles automatizados — no actúan ni antes ni después de ningún evento

Qué esperar (literal — esta tabla es un documento, no un comando; su exactitud se verifica releyendo cada módulo citado, no ejecutando nada): las seis primeras filas de este módulo son preventivas, sin excepción. Es exactamente lo que predijo RISK-MAP.md en su sección "Decision": "preventive controls (M2-M4, M6) come first, and the guardrail that provides attribution after the fact (M7) closes the sequence before the capstone". Hasta este módulo, Andes Cargo tiene una baranda casi completa y ninguna cámara — este módulo empieza a instalar la cámara.


El compromiso de honestidad de este módulo específico

Este es, según el propio diseño de esta guía, el módulo con más peso representativo de los ocho — no porque esté peor construido que los anteriores, sino porque la distinción misma que enseña (preventivo/detectivo) es la que más depende de mecanismos que LocalStack Hobby, por diseño, no reproduce con el mismo rigor que una cuenta real. Vale la pena verlo de una vez, antes de entrar lección por lección:

  • M7.2 y M7.3 dependen de la misma funcionalidad de pago citada en el M2.6. IAM Policy Enforcement — el motor que evaluaría de verdad políticas basadas en identidad, políticas basadas en recursos, permission boundaries y Service Control Policies — está documentado como incluido únicamente en los planes Base y Ultimate de LocalStack, no en el plan gratuito Hobby/Community que usa todo este ecosistema. El recurso Terraform de un permission boundary (M7.3) se declara y se valida de verdad; que ese boundary bloquee de verdad una llamada real es exactamente lo que esa funcionalidad de pago probaría, y esta guía no puede.
  • M7.4 nombra GuardDuty y AWS Config sin construirlos, porque ninguno de los dos está confirmado en el plan gratuito de LocalStack — la misma razón, ya citada desde el Módulo 1, por la que el plan Hobby cubre "30+ servicios emulados" frente a "55+"/"110+" de los planes de pago.
  • M7.5 sí ejecuta la parte que puede ejecutar antes de declarar el límite: aws_cloudtrail se declara en HCL real, y terraform validate corre de verdad sobre ese recurso. La lectura con awslocal cloudtrail lookup-events queda representativa por la misma razón de siempre en esta guía —este entorno de escritura no tiene un LOCALSTACK_AUTH_TOKEN exportado, así que el contenedor no arranca (Could not connect to the endpoint URL)—, no por ninguna limitación de plan de pago: la propia documentación de LocalStack demuestra lookup-events como una operación funcional en su guía de introducción a CloudTrail.
  • M7.6 nombra Organizations, Control Tower y SCPs multi-cuenta sin construirlos — no por una limitación de LocalStack esta vez, sino porque Andes Cargo, tal como está definida en todo este ecosistema, es una sola cuenta AWS (000000000000). Construir una jerarquía de cuentas para un caso que no la necesita sería inventar alcance que no existe.
  • M7.7 sí corre terraform validate/plan de verdad sobre una alarma nueva; que esa alarma se dispare de verdad contra un evento real de IAM queda representativa por la misma ausencia de LOCALSTACK_AUTH_TOKEN, no por ningún límite de plan de pago — EventBridge, SNS y CloudWatch son servicios centrales cubiertos por el plan gratuito.
  • M7.8 es, de principio a fin, real. El documento que cierra este módulo —la matriz de las ocho piezas— no depende de ningún token ni de ningún plan de LocalStack: es un artefacto que tú escribes, con la misma disciplina que ya aplicaste en THREAT-MODEL.md y RISK-MAP.md.

Nota algo importante en esa lista: no todo lo que queda representativo en este módulo tiene la misma causa. Dos casos (M7.2/M7.3, M7.4 para GuardDuty/Config) son limitaciones de plan de pago de LocalStack. Dos casos (M7.5, M7.7) son limitaciones del entorno de escritura específico (sin token, el contenedor no arranca) — el mismo límite que ya viste en cada módulo anterior de esta guía, no algo nuevo de este módulo. Y uno (M7.6) no es una limitación de LocalStack en absoluto, sino una decisión honesta de alcance: Andes Cargo no necesita todavía lo que esa lección nombra. La lección 8 de este módulo exige que sepas distinguir estas tres causas sin confundirlas.


Errores comunes

Pensar que "preventivo" significa "mejor" y "detectivo" significa "peor" (de jerarquía falsa). Qué pasa: alguien, después de leer la analogía de la baranda y la cámara, concluye que un guardrail detectivo es una versión de segunda categoría de uno preventivo. Cómo detectarlo: si tu resumen mental de este módulo es "CloudTrail es el guardrail barato que se usa cuando no se pudo hacer algo mejor". Cómo corregirlo: son categorías complementarias, no un ranking — ni siquiera el sistema de seguridad más preventivo del mundo elimina la necesidad de una cámara, porque ningún control preventivo es perfecto (un permission boundary mal escrito, una regla Rego con un bug, una condición de trust policy demasiado amplia). El valor de un guardrail detectivo no es ser el plan B de uno preventivo — es responder una pregunta que ningún control preventivo, por bien construido que esté, puede responder: qué pasó de verdad, con evidencia.

Asumir que este módulo construye SCPs y Organizations de verdad, porque los nombra dos veces (lecciones 2 y 6) (de expectativa). Qué pasa: alguien, viendo service control policies mencionado tanto en la lección 2 (conceptual) como en la lección 6 (multi-cuenta), espera un aws_organizations_policy real en algún punto del módulo. Cómo detectarlo: si buscas, en cualquier archivo .tf de este módulo, un recurso con organizations en el nombre. Cómo corregirlo: las SCPs se nombran en las dos lecciones, con propósitos distintos — la lección 2 las presenta como el primo conceptual de un permission boundary (mismo mecanismo de "límite máximo", alcance distinto: una cuenta completa, no un solo rol); la lección 6 explica por qué Andes Cargo no las necesita todavía. Ningún recurso de Organizations se declara ni se aplica en ningún lugar de esta guía — es, explícitamente, un faltante de nivel ecosistema, documentado como decisión de diseño, no escondido.

Leer "el módulo con más peso representativo" como "el módulo menos riguroso" (de lectura del propio diseño). Qué pasa: alguien, después de leer la sección de honestidad de esta lección, concluye que este módulo tiene menos rigor técnico que los anteriores. Cómo detectarlo: si tu impresión después de esta introducción es que "M7 es donde la guía se relaja". Cómo corregirlo: es exactamente lo opuesto — este es el módulo que hace el trabajo más difícil de honestidad técnica, distinguiendo con precisión tres causas distintas de por qué algo queda representativo (plan de pago de LocalStack, ausencia de token en este entorno específico, o decisión deliberada de alcance), en vez de agrupar todo bajo un genérico "esto no se pudo probar". Esa precisión es, en sí misma, la habilidad de seguridad que este módulo enseña: saber exactamente dónde termina lo que verificaste y empieza lo que asumiste.


Ejercicios

Ejercicio 1 — Clasifica seis controles nuevos, sin mirar la tabla de esta lección. Sin volver a mirar el "Ejemplo trabajado" de esta lección, clasifica cada uno de estos seis controles como preventivo o detectivo: (a) un firewall que bloquea tráfico en un puerto; (b) los logs de acceso de un servidor web; (c) un code review obligatorio antes de hacer merge; (d) una alarma que notifica cuando el uso de CPU supera el 90 %; (e) la validación de un formulario web que rechaza un email mal formado; (f) el historial de versiones de un documento en Google Docs.

Ver solución

(a) Preventivo — bloquea el tráfico antes de que llegue a su destino, el evento indeseado nunca ocurre. (b) Detectivo — no impide ningún acceso, solo deja constancia de que ocurrió. (c) Preventivo — el código problemático nunca llega a la rama principal si el review lo detiene antes del merge. (d) Detectivo — la alarma no reduce el uso de CPU por sí sola; notifica después de que la condición ya se cumplió (aunque dispare, después, una acción correctiva humana o automatizada, la alarma en sí misma solo detecta y notifica). (e) Preventivo — el dato mal formado nunca se guarda; se rechaza en el momento del envío. (f) Detectivo — el historial de versiones no impide que alguien edite el documento; permite, después del hecho, ver qué cambió y revertirlo. Si clasificaste los seis correctamente, tienes internalizado el criterio central de este módulo: la pregunta no es "¿qué tan grave es el problema?", es "¿este control actúa antes o después de que el evento exista?".

Ejercicio 2 — Explica, con tus propias palabras, por qué RISK-MAP.md deja lo detectivo para el final. Un compañero, revisando el orden de RISK-MAP.md (Módulo 1, lección 8), pregunta por qué CloudTrail —que hubiera podido construirse desde el principio, sin depender de ningún otro módulo— queda para el último módulo que cierra una fila. ¿Es una elección arbitraria?

Ver solución

No es arbitraria — la sección "Decision" de RISK-MAP.md lo dice explícitamente: "a guardrail that stops a bad change is worth more than one that only records it happened, so preventive controls (M2-M4, M6) come first". Construir CloudTrail antes que los controles preventivos significaría auditar un sistema que todavía tiene sus riesgos más concretos y respaldados por evidencia (como TM-06, el riesgo de que un apply destructivo no encuentre ningún obstáculo, resuelto recién en el M4) completamente abiertos — tendrías un registro perfecto de eventos que un control preventivo, si hubiera existido antes, habría evitado. El orden no es sobre facilidad técnica de construcción; es sobre qué reduce más riesgo real primero.

Ejercicio 3 — Predice las tres causas de "representativo" que vas a encontrar en este módulo, antes de leerlas en detalle. Basándote únicamente en el nombre de las ocho lecciones de este módulo (la tabla de esta lección), predice: ¿cuál lección es representativa por una limitación de plan de pago de LocalStack, cuál por la ausencia de LOCALSTACK_AUTH_TOKEN en este entorno específico, y cuál no es una limitación de LocalStack en absoluto?

Ver solución

Plan de pago de LocalStack: la lección 3 (permission boundary) y la mitad de la lección 4 (GuardDuty/Config) — ambas dependen de IAM Policy Enforcement o de servicios no confirmados en el plan gratuito. Ausencia de token en este entorno específico: la lección 5 (CloudTrail) y la lección 7 (alarma) — ambas declaran HCL real y corren terraform validate real; lo que queda representativo es la ejecución contra LocalStack, exactamente el mismo límite que ya viste desde el Módulo 1. No es una limitación de LocalStack: la lección 6 (SCPs multi-cuenta) — es una decisión de alcance, porque Andes Cargo es una sola cuenta y no necesita todavía lo que esa lección nombra. Si distinguiste las tres causas sin mezclarlas, ya tienes el criterio que la lección 8 de este módulo te va a pedir aplicar con precisión sobre las ocho piezas completas.


Resumen y siguiente paso

En esta lección aprendiste el vocabulario que gobierna todo este módulo — preventivo (no pudo pasar) frente a detectivo (quedó registro) —, con la analogía de la baranda del balcón y la cámara de seguridad, y confirmaste, clasificando seis controles que ya construiste en módulos anteriores, que esta distinción no es nueva: solo le faltaba el nombre. Viste el mapa completo de las ocho lecciones que siguen, y las tres causas distintas por las que algunas de ellas van a quedar representativas o nombradas — una funcionalidad de pago de LocalStack, la ausencia de un token en este entorno de escritura específico, o una decisión honesta de que Andes Cargo todavía no necesita cierto alcance.

Antes de avanzar deberías poder: explicar la diferencia entre un guardrail preventivo y uno detectivo sin usar la palabra "mejor" o "peor"; nombrar las ocho lecciones de este módulo en orden; y distinguir las tres causas de "representativo" que vas a encontrar en las lecciones que siguen.

La lección 2 abre el lado preventivo de este módulo: qué son las Service Control Policies y los permission boundaries, conceptualmente, y por qué el enforcement real de ambos depende de exactamente la misma pieza de LocalStack que ya conociste en el M2.6.

Recursos

  1. Este curso, Módulo 1, lección 8 (RISK-MAP.md) — la fila TM-03 que este módulo resuelve, y la sección Decision que explica por qué lo preventivo va antes que lo detectivo.
  2. Este curso, Módulo 4, lección 2 — la lección que anticipó explícitamente esta distinción, con la misma analogía del inspector que revisa el plano antes del primer ladrillo.
  3. LocalStack — Pricing — la fuente de la cobertura por plan (Hobby/Base/Ultimate) citada en la sección de honestidad de esta lección.
  4. AWS Docs — AWS CloudTrail User Guide — referencia oficial de CloudTrail, el guardrail detectivo central de este módulo, desarrollado a fondo en las lecciones 4 y 5.