Seguridad en IaC (Checkov y TFLint)

Guía práctica para escanear Infraestructura como Código (Terraform, Ansible, Kubernetes, Dockerfile) con Checkov y TFLint, detectar misconfigurations e integrar seguridad shift-left en pre-commit y CI/CD.

Resumen

Escribir infraestructura como código (IaC) es cómodo, repetible y versionable. Pero aquí es donde reside el peligro: un error de configuración también se vuelve repetible. Un bucket S3 público, un Security Group abierto a 0.0.0.0/0 o un contenedor corriendo como root se desplegarán tantas veces como ejecutes tu pipeline.

En esta guía vamos a ver cómo puedes escanear tu IaC con Checkov y TFLint para detectar esas misconfigurations antes de que lleguen a producción. El objetivo es claro: aplicar el principio shift-left del enfoque DevSecOps para que la seguridad no sea algo que ocurre al final, sino parte del desarrollo.

Prerrequisitos

Antes de empezar, asegúrate de tener lo siguiente:

  • Terraform y/o Ansible instalados y un repositorio con código IaC.
  • Python 3.8+ (para Checkov) y pip.
  • Familiaridad con pipelines CI/CD (GitHub Actions).

¿Qué es el escaneo de IaC y por qué importa?

El escaneo de IaC analiza estáticamente los ficheros de definición (HCL de Terraform, playbooks de Ansible, manifiestos de Kubernetes, Dockerfiles) buscando patrones inseguros: cifrado desactivado, logging ausente, permisos excesivos, secretos en claro, etc.

A diferencia del escaneo de vulnerabilidades que analiza imágenes y dependencias ya construidas, aquí trabajamos sobre el plano de definición, mucho antes de que el primer recurso sea siquiera creado en la nube.

Shift-left en una frase

Cuanto más tarde encuentras un fallo de seguridad, más caro es corregirlo. Detectar un Security Group abierto en el editor o en el git commit cuesta segundos; detectarlo tras un incidente en producción cuesta mucho más.

Diagrama Mermaid

Checkov

Checkov (de Prisma Cloud / Bridgecrew) es un escáner de análisis estático que lo cubre casi todo: Terraform, Ansible, Kubernetes, Dockerfile, CloudFormation, Helm, ARM y Serverless. Viene con más de mil policies predefinidas mapeadas a estándares como CIS Benchmarks, PCI-DSS o HIPAA.

Instalación

Puedes instalarlo fácilmente mediante pip o brew:

# Con pip (recomendado)
pip install checkov

# Con Homebrew (macOS)
brew install checkov

# Verificar
checkov --version

Uso básico

Una vez instalado, puedes empezar a escanear con estos comandos:

# Escanear todo el directorio actual (autodetecta el tipo de IaC)
checkov -d .

# Escanear un único fichero
checkov -f main.tf

# Limitar a un framework concreto
checkov -d . --framework terraform
checkov -d . --framework ansible
checkov -d . --framework kubernetes
checkov -d . --framework dockerfile

# Salida en formato máquina para CI
checkov -d . -o json --output-file-path resultados/

# Fallar solo con severidad alta o superior (requiere API key para severidades,
# alternativamente filtra por checks concretos)
checkov -d . --compact --quiet

Silenciar falsos positivos

Si tienes un caso de uso legítimo donde quieres saltarte un check, no necesitas desactivarlo globalmente. Puedes añadir un comentario inline en el recurso Terraform:

resource "aws_s3_bucket" "logs" {
  # checkov:skip=CKV_AWS_18:El logging se gestiona en la cuenta central
  bucket = "mi-bucket-logs"
}

Ejemplo real: hallazgo en Terraform y su corrección

Imagina que tienes este código inseguro:

resource "aws_s3_bucket" "data" {
  bucket = "frikiteam-data"
}

Checkov no se quedará de brazos cruzados y te reportará algo como esto:

Check: CKV_AWS_18: "Ensure the S3 bucket has access logging enabled"
        FAILED for resource: aws_s3_bucket.data
Check: CKV_AWS_21: "Ensure all data stored in the S3 bucket have versioning enabled"
        FAILED for resource: aws_s3_bucket.data
Check: CKV2_AWS_6: "Ensure that S3 bucket has a Public Access block"
        FAILED for resource: aws_s3_bucket.data

Para solucionarlo, la versión corregida debería verse así:

resource "aws_s3_bucket" "data" {
  bucket = "frikiteam-data"
}

resource "aws_s3_bucket_versioning" "data" {
  bucket = aws_s3_bucket.data.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_public_access_block" "data" {
  bucket                  = aws_s3_bucket.data.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_logging" "data" {
  bucket        = aws_s3_bucket.data.id
  target_bucket = aws_s3_bucket.logs.id
  target_prefix = "log/"
}

Ejemplo en Ansible

Si trabajas con Ansible, un playbook inseguro como este:

- name: Descargar binario
  ansible.builtin.get_url:
    url: http://example.com/app.tar.gz   # HTTP sin cifrar
    dest: /opt/app.tar.gz
    validate_certs: no                    # Verificación TLS desactivada

Checkov marcará CKV2_ANSIBLE_1 (por el uso de HTTP en lugar de HTTPS) y la desactivación de la verificación de certificados. La solución es simple: usa https:// y establece validate_certs: yes.

TFLint

TFLint es un linter específico para Terraform. Ojo, no es exactamente lo mismo que Checkov: mientras Checkov busca seguridad, TFLint se encarga de detectar errores de Terraform (sintaxis obsoleta, código muerto, convenciones) y, gracias a sus plugins de proveedor, valida argumentos y tipos de instancia inválidos que Terraform no pillaría hasta que intentes hacer el apply.

Instalación

# Script oficial
curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

# Homebrew (macOS)
brew install tflint

# Verificar
tflint --version

Configuración con reglas de proveedor

Para sacarle partido, necesitas un fichero .tflint.hcl en la raíz de tu proyecto:

plugin "aws" {
  enabled = true
  version = "0.34.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

plugin "azurerm" {
  enabled = true
  version = "0.27.0"
  source  = "github.com/terraform-linters/tflint-ruleset-azurerm"
}

config {
  call_module_type = "local"
}

rule "terraform_naming_convention" {
  enabled = true
}

Uso básico

# Descargar los plugins declarados en .tflint.hcl
tflint --init

# Ejecutar el linting sobre el directorio actual
tflint

# Recorrer módulos recursivamente
tflint --recursive

# Salida para CI
tflint --format compact

Un ejemplo de un error típico que TFLint te salvaría de un despliegue fallido:

Error: "t9.micro" is an invalid value as instance_type (aws_instance_invalid_type)

  on main.tf line 12:
  12:   instance_type = "t9.micro"

Terraform no detectaría que esa instancia no existe hasta llegar al apply; TFLint te lo dice en segundos.

Integración en pre-commit hooks

Para que esto sea parte de tu flujo diario, instala pre-commit (pip install pre-commit) y crea un archivo .pre-commit-config.yaml:

repos:
  - repo: https://github.com/bridgecrewio/checkov
    rev: 3.2.0
    hooks:
      - id: checkov
        args: ["--framework", "terraform", "--framework", "ansible", "--quiet"]

  - repo: https://github.com/terraform-linters/tflint
    rev: v0.53.0
    hooks:
      - id: tflint

Luego, actívalo con:

pre-commit install       # activa el hook en git commit
pre-commit run --all-files

El hook no sustituye al pipeline

Recuerda que un desarrollador puede saltarse los hooks con git commit --no-verify. Los hooks son tu primera capa de comodidad y rapidez, pero la barrera bloqueante real debe vivir siempre en tu CI/CD.

Integración en CI/CD (GitHub Actions)

Aquí tienes un ejemplo de cómo automatizar este escaneo en .github/workflows/iac-security.yml:

name: IaC Security Scan

on:
  pull_request:
    paths:
      - "**/*.tf"
      - "**/*.yml"
      - "**/*.yaml"

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: .
          framework: terraform,ansible,dockerfile
          output_format: cli,sarif
          output_file_path: console,results.sarif
          soft_fail: false        # falla el job ante hallazgos

      - name: Subir SARIF a Code Scanning
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: results.sarif

      - name: TFLint
        uses: terraform-linters/setup-tflint@v4
        with:
          tflint_version: v0.53.0
      - run: |
          tflint --init
          tflint --recursive --format compact

SARIF y la pestaña Security

Si exportas en formato SARIF, los hallazgos aparecerán directamente en la pestaña Security → Code scanning de GitHub, con anotaciones integradas en el Pull Request.

Comparativa de herramientas

Si tienes dudas de qué herramienta elegir, aquí tienes un resumen comparativo:

Característica Checkov TFLint tfsec Terrascan
Enfoque principal Seguridad/compliance Linting + errores TF Seguridad Seguridad/compliance
Terraform Sí (nativo)
Ansible No No No
Kubernetes / Helm No Sí (limitado)
Dockerfile No No
Reglas de proveedor (AWS/Azure) Vía policies Sí (plugins) Vía reglas Vía policies
Política personalizada Python / YAML Reglas TFLint Rego (custom) Rego (OPA)
Estado del proyecto Activo Activo Fusionado en Trivy Activo
Lenguaje policies YAML / Python HCL JSON / Rego Rego

¿Cuál elijo?

No son excluyentes. De hecho, la combinación ganadora suele ser Checkov + TFLint: Checkov para la seguridad multi-framework y TFLint para el linting profundo de Terraform. Un detalle: tfsec ahora forma parte de Trivy (trivy config), así que si ya usas Trivy para escaneo de vulnerabilidades, puedes aprovecharlo también para IaC.

Buenas prácticas

Para mantener tu infraestructura realmente segura, te recomiendo:

  • Ejecuta siempre el escaneo en pre-commit (por rapidez) y de forma bloqueante en CI (como garantía).
  • Versiona tus archivos de configuración (.tflint.hcl y .pre-commit-config.yaml) junto al código.
  • Si usas un checkov:skip, documenta siempre el porqué; los skips sin motivo son deuda de seguridad pura.
  • Aprovecha el formato SARIF para centralizar todo en tu plataforma de CI.
  • Mantente al día: revisa las nuevas policies cada vez que actualices las versiones de las herramientas.

Enlaces relacionados

  • Introducción a DevSecOps
  • Escaneo de Vulnerabilidades
  • Terraform Base
  • Ansible Base

Suscríbete al blog por correo electrónico

Introduce tu correo electrónico para suscribirte a este blog y recibir avisos de nuevas entradas.

Deja un comentario