Terraform e IaC security: misconfigs, tfsec, Checkov y drift detection
· 10 min de lectura
Terraform y la infraestructura como código permiten desplegar entornos enteros con un commit, pero un error en el HCL puede abrir buckets al mundo o exponer credenciales en el state. Aprende a escanear, corregir y monitorizar drift.
La infraestructura como código (IaC) transformó la forma de gestionar servidores, redes y servicios cloud: en lugar de hacer clic en consolas web o ejecutar comandos manuales, defines toda tu infraestructura en archivos declarativos que versionas en Git. Terraform, de HashiCorp, es la herramienta IaC más extendida: soporta AWS, Azure, GCP, Kubernetes y decenas de proveedores. Pero ese mismo poder introduce un riesgo enorme: un terraform apply con una mala configuración despliega la vulnerabilidad en segundos, y a escala.
Los errores más frecuentes — buckets S3 públicos, security groups abiertos a 0.0.0.0/0, roles IAM con permisos *, bases de datos sin cifrado — no son teóricos. Aparecen en auditorías reales constantemente, y las herramientas de escaneo estático como tfsec y Checkov existen precisamente porque la comunidad se cansó de encontrarlos en producción. Esta lección cubre todo el ciclo: desde escribir HCL seguro hasta detectar drift (la diferencia entre lo declarado y lo real) y proteger el state file, que es el archivo donde Terraform guarda todos los secretos de tu infraestructura.
Es la primera lección de la Tanda 9, dedicada a cloud nativo e infraestructura como código.
Practica solo en cuentas cloud propias o en entornos de laboratorio. Desplegar infraestructura en cuentas ajenas sin autorización es acceso no autorizado y puede generar costes reales.
Qué es la seguridad en IaC#
La seguridad en IaC consiste en aplicar controles de seguridad al código que define la infraestructura, no solo a la infraestructura ya desplegada. La idea es sencilla: si el código es la fuente de verdad de tu entorno, escanear ese código antes de aplicarlo es más barato, más rápido y más fiable que auditar la consola de AWS después del hecho.

Terraform usa archivos .tf escritos en HCL (HashiCorp Configuration Language). Cada recurso — una instancia EC2, un bucket S3, un security group, un rol IAM — se declara como un bloque con propiedades. Cuando ejecutas terraform plan, Terraform compara el estado deseado (tu código) con el estado actual (almacenado en el state file) y calcula los cambios. terraform apply ejecuta esos cambios contra la API del proveedor cloud.
El problema es que HCL no valida seguridad por defecto. Terraform acepta felizmente un security group que abre el puerto 22 a todo internet, un bucket S3 sin cifrado ni bloqueo de acceso público, o un rol IAM con Action: "*". Para Terraform, si la sintaxis es correcta y la API del proveedor lo acepta, el recurso es válido. La seguridad depende de ti.
Por qué ocurre: la causa raíz#
La raíz del problema es la brecha entre funcionalidad y seguridad: los valores por defecto de muchos recursos cloud priorizan que funcione sobre que sea seguro. Un desarrollador que necesita un bucket para almacenar assets copia un ejemplo de la documentación, no se fija en que falta el bloqueo de acceso público, y lo despliega.
# VULNERABLE: bucket S3 sin cifrado ni bloqueo de acceso público
resource "aws_s3_bucket" "datos" {
bucket = "mi-empresa-datos-prod"
}
# No hay aws_s3_bucket_public_access_block
# No hay aws_s3_bucket_server_side_encryption_configuration
# No hay aws_s3_bucket_versioning
Otro patrón clásico es el security group permisivo:
# VULNERABLE: SSH abierto al mundo
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Todo internet
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
Y el rol IAM con estrella:
# VULNERABLE: permisos sin restricción
resource "aws_iam_policy" "admin" {
name = "dev-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = "*"
Resource = "*"
}]
})
}
Estos tres ejemplos son los que más aparecen en informes de auditoría cloud. No son errores complejos: son omisiones que la velocidad del desarrollo normaliza.
Tipos de misconfigurations en IaC#
| Categoría | Ejemplo Terraform | Riesgo | Herramienta que lo detecta |
|---|---|---|---|
| Acceso público | S3 sin public access block | Exposición de datos | tfsec, Checkov |
| Red permisiva | Security group 0.0.0.0/0 en SSH/RDP | Acceso remoto no autorizado | tfsec, Checkov |
| IAM excesivo | Policy con Action * Resource * | Escalada de privilegios | Checkov, Parliament |
| Cifrado ausente | RDS sin storage_encrypted, EBS sin encrypt | Datos en claro en disco | tfsec |
| Logging deshabilitado | CloudTrail no activo, S3 sin access logging | Sin visibilidad de acciones | Checkov |
| State inseguro | tfstate local sin cifrar | Credenciales expuestas en disco o repo | Manual, tfsec |
| Módulos no verificados | source de registry público sin hash pin | Supply chain de IaC | Checkov |
| Secrets hardcodeados | password = "admin123" en variables | Credenciales en Git | tfsec, Gitleaks |
Dónde aparece#
Las misconfigurations de IaC aparecen en cualquier organización que use Terraform, Pulumi, CloudFormation o Ansible para gestionar infraestructura cloud. Los escenarios más comunes:
- Startups en fase de crecimiento: velocidad sobre seguridad, un solo ingeniero gestiona toda la infra.
- Migraciones cloud: equipos de on-premise que copian patrones legacy a cloud sin entender el modelo de responsabilidad compartida.
- Entornos de desarrollo: "es solo dev, no necesita seguridad" — hasta que alguien conecta dev a datos reales.
- Repositorios públicos: proyectos open source que publican módulos Terraform con defaults inseguros que otros copian.
- Adquisiciones: empresa A compra empresa B y hereda su infraestructura sin auditar.
En el framework MITRE ATT&CK, las misconfigurations de IaC facilitan técnicas como T1190 (Exploit Public-Facing Application) cuando un security group expone servicios, y T1078 (Valid Accounts) cuando credenciales hardcodeadas en el state o en variables permiten acceso legítimo al atacante.
Cómo se detecta#
Escaneo estático con tfsec#
tfsec (ahora integrado en Trivy de Aqua Security) analiza archivos .tf y reporta misconfigurations con severidad y referencia:
# Instalar tfsec
brew install tfsec
# Escanear directorio
tfsec ./infra/
# Salida en formato JUnit para CI
tfsec ./infra/ --format junit > tfsec-results.xml
tfsec produce resultados como:
Result #1 HIGH Security group rule allows ingress from public internet
─────────────────────────────────────────────────
infra/network.tf:12-18
12 │ ingress {
13 │ from_port = 22
14 │ to_port = 22
15 │ protocol = "tcp"
16 │ cidr_blocks = ["0.0.0.0/0"]
17 │ }
Escaneo con Checkov#
Checkov (de Bridgecrew/Palo Alto) soporta Terraform, CloudFormation, Kubernetes, Helm y Dockerfiles:
# Instalar Checkov
pip install checkov
# Escanear
checkov -d ./infra/ --framework terraform
# Solo fallos críticos y altos
checkov -d ./infra/ --check HIGH,CRITICAL
Comparación rápida#
| Aspecto | tfsec / Trivy | Checkov |
|---|---|---|
| Lenguaje | Go | Python |
| Frameworks | Terraform, CloudFormation | Terraform, CFN, K8s, Helm, Docker |
| Custom rules | YAML/Rego | Python, YAML |
| Integración CI | GitHub Actions, GitLab CI | GitHub Actions, GitLab CI, Jenkins |
| Licencia | Apache 2.0 | Apache 2.0 |
Drift detection#
El drift ocurre cuando alguien modifica la infraestructura directamente en la consola cloud sin actualizar el código Terraform. El resultado: tu código dice una cosa, la realidad es otra.
# Método nativo: terraform plan detecta drift
terraform plan
# Si hay drift, verás cambios que tú no hiciste
# driftctl (Snyk): escaneo de drift sin necesidad de state
driftctl scan --from tfstate://terraform.tfstate
Herramientas especializadas como Spacelift y env0 ejecutan plan periódicamente y alertan cuando detectan diferencias.
Explotación paso a paso en un laboratorio#
El laboratorio cloudship de whoami-labs simula un entorno cloud con credenciales expuestas y contenedores mal configurados. Aunque no usa Terraform directamente, los patrones de misconfiguration son idénticos a los que tfsec y Checkov detectan en código.
Paso 1: Reconocimiento de la infraestructura#
nmap -sV -p- IP_DEL_LAB
Identifica los servicios expuestos. En un entorno real con IaC, cada puerto abierto debería tener un security group correspondiente en el código Terraform.
Paso 2: Buscar credenciales en variables de entorno#
# Dentro del contenedor
env | grep -i key
env | grep -i secret
env | grep -i password
cat /proc/1/environ | tr '\0' '\n'
En Terraform, estas credenciales suelen terminar en el state file o como variables de entorno configuradas en el HCL. Un terraform show las muestra en texto plano si el state no está cifrado.
Paso 3: Verificar configuración del contenedor#
# Comprobar si Docker socket está montado
ls -la /var/run/docker.sock
# Comprobar capabilities
cat /proc/1/status | grep Cap
capsh --decode=$(cat /proc/1/status | grep CapEff | awk '{print $2}')
Un contenedor con el socket de Docker montado equivale a un security group con 0.0.0.0/0: acceso total sin restricción. En IaC, esto se previene con policies de OPA o Checkov que prohíban montar /var/run/docker.sock.
Paso 4: Escape y movimiento lateral#
# Si Docker socket está disponible
docker -H unix:///var/run/docker.sock ps
docker -H unix:///var/run/docker.sock run -v /:/host -it alpine chroot /host
Este escape es el equivalente runtime de un terraform apply con misconfiguration: el daño ya está hecho porque la infra se desplegó mal desde el código.
Qué puede lograr un atacante#
| Escenario | Impacto | Técnica MITRE |
|---|---|---|
| S3 bucket público con datos de clientes | Filtración masiva de datos, multas GDPR | T1530 Data from Cloud Storage |
| Security group SSH abierto | Acceso directo al servidor, pivote a red interna | T1190 Exploit Public-Facing Application |
| IAM policy con estrella | Control total de la cuenta cloud | T1078 Valid Accounts |
| State file con secrets en repo público | Robo de credenciales de DB, API keys, tokens | T1552 Unsecured Credentials |
| Drift no detectado | Puertas traseras manuales invisibles al código | T1098 Account Manipulation |
| Módulo Terraform malicioso | Código arbitrario en el pipeline de IaC | T1195 Supply Chain Compromise |
Cómo se corrige#

Bucket S3 seguro#
# SEGURO: bucket con cifrado, bloqueo público y versionado
resource "aws_s3_bucket" "datos" {
bucket = "mi-empresa-datos-prod"
}
resource "aws_s3_bucket_public_access_block" "datos" {
bucket = aws_s3_bucket.datos.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "datos" {
bucket = aws_s3_bucket.datos.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
Security group restrictivo#
# SEGURO: SSH solo desde VPN corporativa
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"] # Solo red interna
}
}
State file remoto cifrado#
# SEGURO: backend remoto con cifrado y locking
terraform {
backend "s3" {
bucket = "mi-empresa-tfstate"
key = "prod/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
Pipeline CI con escaneo obligatorio#
# GitHub Actions: scan antes de apply
name: Terraform
on: [pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: tfsec
uses: aquasecurity/[email protected]
with:
soft_fail: false
- name: Checkov
uses: bridgecrewio/[email protected]
with:
directory: ./infra
soft_fail: false
Errores comunes al defenderse#
- Poner tfsec en el pipeline pero con
soft_fail: true, de modo que los fallos no bloquean el merge. - Cifrar el state en S3 pero dejar el bucket del state sin bloqueo de acceso público.
- Usar módulos del Terraform Registry sin fijar versión exacta ni verificar el código fuente.
- Ignorar el drift: escanear el código pero nunca comparar con la infraestructura real.
- Hardcodear secrets como valores por defecto en
variables.tfen lugar de usar Vault o SSM Parameter Store. - Dar permisos de
terraform applya todos los desarrolladores en lugar de restringirlo a pipelines CI con approval.
Preguntas frecuentes#
tfsec o Checkov, cuál elegir#
Ambos son gratuitos y de calidad. tfsec (ahora dentro de Trivy) es más rápido y se centra en Terraform. Checkov cubre más frameworks (CloudFormation, Kubernetes, Dockerfile) y tiene un ecosistema de policies más amplio. Lo ideal es usar ambos: se complementan porque sus conjuntos de reglas no son idénticos.
Terraform Cloud o backend S3 para el state#
Terraform Cloud (o Terraform Enterprise) ofrece gestión del state, locking, run history y policy as code (Sentinel) integrado. Un backend S3 + DynamoDB es más barato y flexible pero requiere que tú gestiones el cifrado, los permisos y el locking. Para equipos pequeños, S3 es suficiente. Para organizaciones con más de diez desarrolladores usando Terraform, Terraform Cloud simplifica la operación.
Cómo evitar que un dev haga apply manual saltándose el pipeline#
Restringe los credentials de cloud: solo el service account del pipeline tiene permisos de escritura en la cuenta de producción. Los developers pueden ejecutar terraform plan localmente con credenciales read-only, pero apply solo corre en CI tras aprobación. Esto se combina con OIDC federation para evitar keys estáticas.
Qué hago si encuentro secrets en el state file#
Primero, rota las credenciales comprometidas inmediatamente. Después, migra el state a un backend remoto cifrado. Si el state estuvo en un repositorio Git, esa información está en el historial: limpia el historial con git filter-repo o considera los secrets comprometidos para siempre y rota todo.
Los módulos del Terraform Registry son seguros#
No por defecto. Cualquiera puede publicar un módulo. Antes de usar uno, revisa: quién lo mantiene, cuántas descargas tiene, cuándo fue la última actualización, si el código fuente es público y legible. Fija la versión exacta (version = "3.5.0") y revisa los cambios antes de actualizar. Para infraestructura crítica, considera alojar módulos en un registry privado.
Resumen#
- Terraform despliega infraestructura en segundos, incluyendo misconfigurations: un error en HCL se convierte en vulnerabilidad real con un solo
apply. - Los fallos más comunes son buckets públicos, security groups abiertos, IAM con permisos excesivos y bases de datos sin cifrado.
- tfsec (Trivy) y Checkov escanean archivos
.tfantes del deploy y detectan patrones inseguros. - El state file contiene secretos en texto plano: debe estar en un backend remoto, cifrado, con locking y acceso restringido.
- El drift (diferencia entre código y realidad) se detecta con
terraform plan, driftctl o plataformas como Spacelift. - La defensa se completa con pipeline CI que bloquee misconfigurations, OIDC para evitar keys estáticas, y módulos de registry verificados y versionados.