Módulo 5: Scanning Iac And Dependencies

4. Manos a la obra: escaneando `andes-cargo-infra/` con Trivy

Descripción

Esta es la lección donde Trivy deja de ser una herramienta que instalaste y se convierte en la que audita, de verdad, el proyecto completo que los Módulos 1 a 3 endurecieron. Vas a correr trivy config contra andes-cargo-infra/ — el mismo proyecto que ya tiene OIDC federado (Módulo 2), roles con mínimo privilegio (Módulo 2), y secretos gestionados fuera del repositorio (Módulo 3) — y vas a ver que, a pesar de todo ese endurecimiento, once hallazgos reales siguen ahí, esperando a que alguien los mire con la lista de verificación correcta.

Conexión con el módulo

Las lecciones 1 a 3 explicaron por qué esta herramienta importa y confirmaron que está instalada. Esta lección es el primer contacto real entre Trivy y el proyecto de Andes Cargo. Cada hallazgo que vas a ver aquí es candidato directo para la lección 6 (arreglar, suprimir o aceptar) y para el proyecto de la lección 8 (el reporte final de postura de seguridad).


Analogía retomada: el inspector camina la casa completa

La lección 1 comparó a Trivy con un inspector que trae una lista de doscientos puntos de verificación. Esta lección es, literalmente, el inspector entrando por la puerta y empezando a caminar — habitación por habitación, archivo por archivo — con esa lista en la mano. No le importa que tú ya hayas cambiado la cerradura de la puerta principal (el Módulo 2, con OIDC); su lista tiene preguntas sobre el ático, el sótano, y el sistema eléctrico que nadie en tu equipo pensó en revisar todavía.


Ejemplo trabajado: trivy config sobre el proyecto completo

andes-cargo-infra/ en este punto de la guía tiene diecisiete recursos gestionados: los tres recursos canónicos de terraform-and-iac-guide (bucket, tabla, función Lambda con su trigger), los dos roles con permisos ya acotados por prefijo (Módulo 2, lección 7), el módulo oidc-provider/ completo (Módulo 2), y los dos secretos gestionados (Módulo 3). Corre el escaneo desde la raíz del proyecto:

trivy config .

Qué esperar (literal — ejecutado para escribir esta lección, con Trivy 0.74.0):

2026-08-14T10:54:03-06:00	INFO	[misconfig] Misconfiguration scanning is enabled
2026-08-14T10:54:03-06:00	INFO	[checks-client] Using existing checks from cache	path="~/Library/Caches/trivy/policy/content"
2026-08-14T10:54:03-06:00	INFO	[terraform scanner] Scanning root module	file_path="."
2026-08-14T10:54:03-06:00	INFO	Detected config files	num=5

Report Summary

┌───────────────────────────┬───────────┬───────────────────┐
│          Target           │   Type    │ Misconfigurations │
├───────────────────────────┼───────────┼───────────────────┤
│ .                         │ terraform │         0         │
├───────────────────────────┼───────────┼───────────────────┤
│ dynamodb.tf               │ terraform │         2         │
├───────────────────────────┼───────────┼───────────────────┤
│ lambda.tf                 │ terraform │         1         │
├───────────────────────────┼───────────┼───────────────────┤
│ modules/s3-bucket/main.tf │ terraform │         7         │
├───────────────────────────┼───────────┼───────────────────┤
│ secrets.tf                │ terraform │         1         │
└───────────────────────────┴───────────┴───────────────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)

Once hallazgos, repartidos en cuatro archivos. Ni iam.tf ni oidc.tf aparecen en esta tabla — no porque Trivy no los haya mirado (num=5 en "Detected config files" cuenta los archivos con al menos una definición evaluable), sino porque, sobre esos dos archivos específicamente, no encontró nada que señalar. El mínimo privilegio del Módulo 2 y la trust policy condicionada de OIDC pasan la lista de verificación de Trivy sin ningún hallazgo — una confirmación indirecta, no buscada, de que ese trabajo anterior fue real.


Los once hallazgos, uno por uno

dynamodb.tf — dos hallazgos sobre Shipments

AWS-0024 (MEDIUM): Point-in-time recovery is not enabled.
════════════════════════════════════════
DynamoDB tables should be protected against accidentally or malicious write/delete actions by ensuring that there is adequate protection.
By enabling point-in-time-recovery you can restore to a known point in the event of loss of data.


See https://avd.aquasec.com/misconfig/aws-0024
────────────────────────────────────────
 dynamodb.tf:4-15
────────────────────────────────────────
   4 ┌ resource "aws_dynamodb_table" "shipments" {
   5 │   name         = var.table_name
   6 │   billing_mode = "PAY_PER_REQUEST"
   7 │   hash_key     = "shipmentId"
   8 │ 
   9 │   attribute {
  10 │     name = "shipmentId"
  11 │     type = "S"
  12 └   }
  ..   
────────────────────────────────────────


AWS-0025 (LOW): Table encryption does not use a customer-managed KMS key.
════════════════════════════════════════
Using AWS managed keys does not allow for fine grained control. DynamoDB tables are encrypted by default using AWS managed encryption keys. To increase control of the encryption and control the management of factors like key rotation, use a Customer Managed Key.


See https://avd.aquasec.com/misconfig/aws-0025

Fíjate en algo que vale la pena leer con cuidado: AWS-0024 es MEDIUM, la severidad más alta de este archivo — y toca directamente TM-06 de THREAT-MODEL.md, el mismo riesgo que el Módulo 4 resuelve del lado preventivo (no-destroy-shipments.rego bloquea el plan que destruiría la tabla). Trivy no sabe nada de esa política — solo sabe que una tabla sin recuperación a un punto en el tiempo (point-in-time recovery, PITR) no puede restaurarse si algo —una apply incorrecta, un error de aplicación, un bug— borra o corrompe datos por accidente, incluso si nunca llegó a destruirse la tabla completa. Es el mismo riesgo de fondo, visto desde un ángulo distinto: conftest evita que la tabla se destruya; PITR es la red de seguridad si algo más sutil sale mal dentro de los datos que ya existen.

lambda.tf — un hallazgo sobre observabilidad

AWS-0066 (LOW): Function does not have tracing enabled.
════════════════════════════════════════
X-Ray tracing enables end-to-end debugging and analysis of all function activity. This will allow for identifying bottlenecks, slow downs and timeouts.


See https://avd.aquasec.com/misconfig/aws-0066
────────────────────────────────────────
 lambda.tf:10-23

Este es un hallazgo de observabilidad, no de acceso ni de cifrado — categoría distinta a la mayoría de los otros diez. AWS X-Ray traza cada invocación de la función a través de todos los servicios que toca, útil para diagnosticar por qué una invocación específica fue lenta o falló. Ninguna guía anterior de este ecosistema lo mencionó; es exactamente el tipo de pregunta que solo aparece en la lista de un inspector con experiencia acumulada de miles de proyectos Lambda reales.

modules/s3-bucket/main.tf — siete hallazgos, el archivo más denso

Ya viste el adelanto de este bloque en la lección 1 de este módulo. Aquí está completo:

AWS-0086 (HIGH): No public access block so not blocking public acls
AWS-0087 (HIGH): No public access block so not blocking public policies
AWS-0089 (LOW): Bucket has logging disabled
AWS-0091 (HIGH): No public access block so not blocking public acls
AWS-0093 (HIGH): No public access block so not restricting public buckets
AWS-0094 (LOW): Bucket does not have a corresponding public access block.
AWS-0132 (HIGH): Bucket does not encrypt data with a customer managed key.

Cinco de estos siete —AWS-0086, AWS-0087, AWS-0091, AWS-0093, AWS-0094— son, en el fondo, la misma brecha vista desde cinco ángulos distintos: el recurso aws_s3_bucket_public_access_block, que modules/s3-bucket/ nunca declara, tiene cuatro banderas independientes en la API real de AWS (block_public_acls, block_public_policy, ignore_public_acls, restrict_public_buckets), y Trivy evalúa cada una por separado, más un quinto chequeo que confirma que el recurso mismo ni siquiera existe. Es exactamente TM-04 de THREAT-MODEL.md"S3 bucket has no public-access block"—, el hallazgo que tú mismo escribiste a mano en el Módulo 1, ahora confirmado, con evidencia de línea exacta, por una herramienta que nunca leyó tu documento.

AWS-0132, la severidad HIGH más notable de este bloque, es un hallazgo distinto: el bucket usa cifrado con clave administrada por AWS (el comportamiento por defecto de S3 desde hace años), no con una clave administrada por el cliente (KMS CMK). AWS-0089 (registro de acceso deshabilitado) es otro ángulo más: nadie queda registrando quién leyó o escribió en este bucket, más allá de lo que CloudTrail —representativo hasta el Módulo 7— pueda capturar.

secrets.tf — un hallazgo sobre el secreto del Módulo 3

AWS-0098 (LOW): Secret explicitly uses the default key.
════════════════════════════════════════
Secrets Manager encrypts secrets by default using a default key created by AWS. To ensure control and granularity of secret encryption, CMK's should be used explicitly.


See https://avd.aquasec.com/misconfig/aws-0098
────────────────────────────────────────
 secrets.tf:15-18

El mismo patrón de AWS-0025 y AWS-0132: aws_secretsmanager_secret.customs_api_credentials, del Módulo 3, cifra su contenido con la clave administrada por AWS por defecto, no con una clave administrada por el cliente. Es un hallazgo real, pero de severidad baja — la clave por defecto de AWS sigue siendo cifrado en reposo, solo que sin el control granular (rotación propia, políticas de acceso separadas) que una KMS CMK ofrecería.


Resumen de severidades: la tabla que vas a usar en la lección 6

SeveridadCantidadIDs
CRITICAL0
HIGH5AWS-0086, AWS-0087, AWS-0091, AWS-0093, AWS-0132
MEDIUM1AWS-0024
LOW5AWS-0025, AWS-0066, AWS-0089, AWS-0094, AWS-0098

Cero hallazgos CRITICAL — un dato que vale la pena confirmar tú mismo, porque va a ser el criterio central del proyecto de la lección 8. Los cinco HIGH concentrados en el mismo archivo son la señal más urgente de esta corrida: el bucket que recibe manifiestos de envío, sin ningún bloqueo de acceso público declarado.


Errores comunes

Correr trivy config desde un directorio que no es la raíz del proyecto (de ruta). Qué pasa: alguien corre el comando desde dentro de modules/s3-bucket/ en vez de desde la raíz de andes-cargo-infra/, esperando ver el reporte completo. Cómo detectarlo: el Report Summary muestra solo el archivo desde el que corriste el comando, sin dynamodb.tf, lambda.tf ni secrets.tf. Cómo corregirlo: trivy config . escanea el directorio actual y sus subdirectorios — corre siempre desde la raíz del proyecto (donde vive versions.tf), o pasa la ruta explícita: trivy config /ruta/a/andes-cargo-infra.

Interpretar Tests: 7 como "siete problemas distintos sin relación" (de lectura superficial del conteo). Qué pasa: alguien ve Tests: 7 (SUCCESSES: 0, FAILURES: 7) en modules/s3-bucket/main.tf y asume que hay siete arreglos independientes que hacer. Cómo detectarlo: si tu plan de corrección trata cada uno de los siete IDs como una tarea separada, sin notar que cinco de ellos desaparecen con un solo recurso HCL nuevo. Cómo corregirlo: lee la sección de esta lección sobre los cinco hallazgos AWS-0086/0087/0091/0093/0094 — todos apuntan a la misma causa raíz (aws_s3_bucket_public_access_block ausente). Declarar ese único recurso, con sus cuatro banderas en true, resuelve los cinco de un solo cambio — la lección 8 lo confirma con evidencia.

Confundir la severidad MEDIUM de AWS-0024 con "menos urgente que los HIGH" sin leer el contexto de negocio (de jerarquía ciega por número). Qué pasa: alguien prioriza arreglar primero los cinco HIGH del bucket, dejando AWS-0024 (recuperación a un punto en el tiempo de Shipments) para después, solo porque su severidad numérica es más baja. Cómo detectarlo: si tu orden de trabajo se basa únicamente en la etiqueta de severidad de Trivy, sin cruzarla contra THREAT-MODEL.md. Cómo corregirlo: la severidad que asigna un escáner genérico es un punto de partida razonable, no la última palabra — Shipments es el activo número uno de THREAT-MODEL.md, sección 3 (Módulo 1), y el hallazgo AWS-0024 toca directamente ese activo. El criterio de negocio, cuando existe, siempre complementa —y a veces reordena— la severidad genérica de la herramienta.


Ejercicios

Ejercicio 1 — Explica por qué iam.tf y oidc.tf no aparecen en el Report Summary. Sin volver a mirar la lección, explica en dos o tres frases por qué estos dos archivos no generaron ningún hallazgo, a diferencia de los otros cuatro.

Ver solución

Porque Trivy solo lista, en el Report Summary, los archivos donde encontró al menos un hallazgo — un archivo limpio simplemente no aparece en la tabla (a diferencia del "." de la raíz, que sí aparece con un 0 explícito porque el módulo raíz tiene definiciones evaluables sin problemas). iam.tf contiene el mínimo privilegio recortado por prefijo del Módulo 2, lección 7, y oidc.tf contiene la trust policy condicionada del Módulo 2, lección 5 — ambos ya endurecidos antes de que este módulo empezara. La ausencia de estos dos archivos en la tabla es una confirmación indirecta de que ese trabajo anterior resistió el escrutinio de una lista de verificación externa.

Ejercicio 2 — Calcula cuántos hallazgos quedarían si arreglaras solo el bucket. Si declararas aws_s3_bucket_public_access_block con sus cuatro banderas en true, sin tocar ningún otro archivo, ¿cuántos de los once hallazgos totales desaparecerían? ¿Cuántos quedarían?

Ver solución

Cinco desaparecerían (AWS-0086, AWS-0087, AWS-0091, AWS-0093, AWS-0094 — los cinco relacionados con el bloqueo de acceso público), y quedarían seis: AWS-0024/AWS-0025 en dynamodb.tf, AWS-0066 en lambda.tf, AWS-0089/AWS-0132 en modules/s3-bucket/main.tf (el registro de acceso y el cifrado con CMK son hallazgos independientes de la ausencia del bloqueo público — no se resuelven con el mismo recurso), y AWS-0098 en secrets.tf. La lección 8 confirma este cálculo con la corrida real.

Ejercicio 3 — Predice qué pasaría si corrieras trivy config sobre andes-cargo-infra/ del Módulo 1, antes de cualquier endurecimiento. Basándote en lo que sabes del proyecto original de terraform-and-iac-guide (sin OIDC, sin permisos acotados por prefijo, sin secretos gestionados), ¿esperarías más, menos, o los mismos once hallazgos?

Ver solución

Más — probablemente varios más, no menos. El proyecto original de terraform-and-iac-guide no tenía oidc.tf ni secrets.tf en absoluto (esos archivos son enteramente nuevos de los Módulos 2 y 3 de esta guía), así que dos de las fuentes de hallazgos de esta lección (AWS-0098 en secrets.tf, y cualquier hallazgo potencial sobre oidc.tf) ni siquiera existirían — pero iam.tf, con los permisos de bucket completo sin acotar por prefijo (antes del Módulo 2, lección 7), probablemente habría disparado hallazgos adicionales sobre alcance de permisos IAM que el HCL actual, ya recortado, no dispara. El número exacto solo se confirma corriendo el comando —pero la dirección del cambio es clara: menos endurecimiento previo generalmente significa más superficie para que un escáner de la comunidad encuentre algo.


Resumen y siguiente paso

En esta lección corriste trivy config . de verdad sobre el andes-cargo-infra/ completo — diecisiete recursos, cuatro archivos con hallazgos, once hallazgos en total: cero CRITICAL, cinco HIGH (todos concentrados en el bucket de manifiestos), uno MEDIUM (recuperación a un punto en el tiempo de Shipments), cinco LOW. Confirmaste que cinco de los siete hallazgos del bucket son, en el fondo, la misma causa raíz —el bloqueo de acceso público ausente— vista desde cinco banderas independientes de la API real de AWS. Y viste que iam.tf/oidc.tf, ya endurecidos por el Módulo 2, no generaron ningún hallazgo — la primera confirmación externa de ese trabajo.

Antes de avanzar deberías poder: correr trivy config sobre cualquier proyecto Terraform y leer su Report Summary; explicar por qué cinco IDs distintos pueden compartir la misma causa raíz; y priorizar hallazgos combinando la severidad de la herramienta con el contexto de negocio de THREAT-MODEL.md.

La lección 5 instala Checkov, el segundo escáner de este módulo, y lo corre contra el mismo proyecto — con una comparación honesta de qué encuentra cada herramienta, y por qué el solapamiento nunca es total.

Recursos

  1. trivy.dev — Terraform coverage — documentación oficial de la cobertura de Terraform de Trivy.
  2. avd.aquasec.com — AWS-0086 y las páginas equivalentes para cada ID citado en esta lección — documentación completa de cada regla, con la razón de seguridad detrás.
  3. Este curso, Módulo 1, THREAT-MODEL.md — el hallazgo TM-04, confirmado en esta lección por una herramienta independiente.
  4. Este curso, Módulo 2, lección 7 — el trabajo de mínimo privilegio que esta lección confirma indirectamente, por ausencia de hallazgos en iam.tf.