Escaneo de Vulnerabilidades en Contenedores con Trivy

Trivy es un escáner de vulnerabilidades de código abierto desarrollado por Aqua Security que analiza imágenes de contenedor, sistemas de archivos y repositorios de código para detectar CVEs conocidos, secretos expuestos y configuraciones incorrectas. Su velocidad, facilidad de uso y amplia base de datos de vulnerabilidades lo han convertido en la herramienta estándar para seguridad de contenedores en pipelines CI/CD. Esta guía cubre la instalación de Trivy, integración en CI/CD y generación de SBOMs.

Requisitos Previos

  • Servidor Linux (Ubuntu 22.04/Debian 12 o CentOS 9/Rocky 9)
  • Docker instalado (para escanear imágenes)
  • Mínimo 1 GB de RAM y 1 GB de disco para la base de datos de vulnerabilidades
  • Acceso a Internet para descargar la base de datos de CVEs

Instalación de Trivy

# Ubuntu/Debian: usar el repositorio oficial de Aqua Security
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | \
  gpg --dearmor -o /usr/share/keyrings/trivy-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/trivy-archive-keyring.gpg] \
  https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | \
  tee /etc/apt/sources.list.d/trivy.list

apt-get update && apt-get install -y trivy

# CentOS/Rocky Linux
cat > /etc/yum.repos.d/trivy.repo << 'EOF'
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://aquasecurity.github.io/trivy-repo/rpm/public.key
EOF
dnf install -y trivy

# Verificar la instalación
trivy --version

# Actualizar la base de datos de vulnerabilidades
trivy image --download-db-only

Escaneo de Imágenes de Contenedor

# Escanear una imagen pública del registro de Docker
trivy image nginx:latest

# Escanear una imagen con salida compacta (tabla)
trivy image --format table nginx:latest

# Escanear una imagen de un registro privado
trivy image --username usuario --password token \
  registry.empresa.com/app:latest

# Escanear una imagen local (no pull)
docker build -t mi-app:latest .
trivy image --input mi-app.tar mi-app:latest

# Exportar imagen y escanear sin Docker daemon
docker save mi-app:latest > /tmp/mi-app.tar
trivy image --input /tmp/mi-app.tar

# Incluir vulnerabilidades no corregidas (unfixed)
trivy image --include-unfixed nginx:latest

# Escanear solo paquetes del SO (ignorar dependencias de aplicación)
trivy image --security-checks vuln --vuln-type os nginx:latest

# Escanear solo dependencias de aplicación (npm, pip, etc.)
trivy image --security-checks vuln --vuln-type library nginx:latest

Filtrado por Severidad

# Mostrar solo vulnerabilidades CRÍTICAS y ALTAS
trivy image --severity CRITICAL,HIGH nginx:latest

# Fallar el escaneo si hay vulnerabilidades críticas (útil en CI/CD)
trivy image --severity CRITICAL --exit-code 1 nginx:latest
echo "Código de salida: $?"

# Ignorar vulnerabilidades específicas (conocidas o aceptadas)
cat > /tmp/.trivyignore << 'EOF'
# Ignorar CVE específico porque ya está mitigado en nuestra configuración
CVE-2023-1234
# Ignorar otro CVE con justificación
CVE-2023-5678
EOF
trivy image --ignorefile /tmp/.trivyignore --severity CRITICAL,HIGH nginx:latest

# Ignorar vulnerabilidades sin parche disponible
trivy image --ignore-unfixed --severity CRITICAL,HIGH nginx:latest

# Definir umbral máximo de días para vulnerabilidades publicadas
trivy image --severity HIGH --ignore-policy /tmp/trivy-policy.rego nginx:latest

Escaneo de Sistema de Archivos

Trivy puede escanear el sistema de archivos local, ideal para repositorios de código:

# Escanear el directorio actual (busca dependencias y Dockerfiles)
trivy fs .

# Escanear un repositorio de código en busca de secretos y vulns
trivy fs --security-checks vuln,secret,config .

# Escanear solo secretos en el código (tokens, contraseñas, claves)
trivy fs --security-checks secret /ruta/al/proyecto

# Escanear un Dockerfile en busca de malas configuraciones
trivy config ./Dockerfile

# Escanear archivos de configuración de Kubernetes
trivy config /ruta/a/manifiestos-kubernetes/

# Escanear Helm charts
trivy config ./helm-chart/

# Escanear Terraform
trivy config ./terraform/

Generación de SBOM

Un SBOM (Software Bill of Materials) lista todos los componentes de software de una imagen:

# Generar SBOM en formato CycloneDX
trivy image --format cyclonedx --output sbom.json nginx:latest

# Generar SBOM en formato SPDX
trivy image --format spdx-json --output sbom.spdx.json nginx:latest

# Generar SBOM en formato SPDX de texto
trivy image --format spdx --output sbom.spdx nginx:latest

# Escanear un SBOM ya generado (sin necesitar la imagen)
trivy sbom sbom.json

# Generar informe de vulnerabilidades a partir de SBOM
trivy sbom --format table sbom.json

# Guardar el SBOM junto con el informe de vulnerabilidades
trivy image \
  --format cyclonedx \
  --output sbom-with-vulns.json \
  --report summary \
  nginx:latest

Integración en CI/CD

Integración con GitHub Actions:

# .github/workflows/security-scan.yml
name: Escaneo de Seguridad con Trivy

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  scan:
    name: Escanear imagen de contenedor
    runs-on: ubuntu-latest
    steps:
      - name: Checkout del código
        uses: actions/checkout@v4

      - name: Construir imagen Docker
        run: docker build -t mi-app:${{ github.sha }} .

      - name: Escanear con Trivy (solo vulnerabilidades críticas)
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: mi-app:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH
          exit-code: 1
          ignore-unfixed: true

      - name: Subir resultados a GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-results.sarif

Integración en pipelines GitLab CI:

# .gitlab-ci.yml (sección de escaneo de seguridad)
# Añadir como job de seguridad en el pipeline

# trivy-scan.sh - Script para CI/CD genérico
cat > /usr/local/bin/trivy-scan-ci.sh << 'SCRIPT'
#!/bin/bash
IMAGE="${1:?Especificar imagen a escanear}"
SEVERITY="${SEVERITY:-CRITICAL,HIGH}"
EXIT_ON_VULN="${EXIT_ON_VULN:-1}"

echo "=== Escaneando imagen: $IMAGE ==="
echo "=== Severidad mínima: $SEVERITY ==="

# Actualizar base de datos de Trivy
trivy image --download-db-only --quiet

# Ejecutar escaneo
trivy image \
  --severity "$SEVERITY" \
  --exit-code "$EXIT_ON_VULN" \
  --ignore-unfixed \
  --format table \
  "$IMAGE"

EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
    echo "✓ Escaneo completado: sin vulnerabilidades $SEVERITY"
else
    echo "✗ Escaneo fallido: se encontraron vulnerabilidades $SEVERITY"
fi

exit $EXIT_CODE
SCRIPT
chmod +x /usr/local/bin/trivy-scan-ci.sh

Aplicación de Políticas

Trivy soporta políticas en Rego (Open Policy Agent) para reglas personalizadas:

# Crear una política OPA/Rego para Trivy
mkdir -p /opt/trivy/policies
cat > /opt/trivy/policies/deny-high-crit.rego << 'EOF'
package main

import future.keywords

# Denegar imágenes con vulnerabilidades críticas en paquetes específicos
deny[msg] {
    vuln := input.Results[_].Vulnerabilities[_]
    vuln.Severity == "CRITICAL"
    vuln.PkgName == "openssl"
    msg := sprintf("Vulnerabilidad crítica en OpenSSL: %s", [vuln.VulnerabilityID])
}

# Denegar imágenes corriendo como root
deny[msg] {
    input.Metadata.ImageConfig.Config.User == ""
    msg := "La imagen ejecuta procesos como root (sin USER definido en Dockerfile)"
}
EOF

# Usar la política personalizada en el escaneo
trivy image \
  --security-checks vuln,config \
  --policy /opt/trivy/policies/ \
  --exit-code 1 \
  nginx:latest

Solución de Problemas

# Actualizar la base de datos de vulnerabilidades manualmente
trivy image --download-db-only

# Ver la versión de la base de datos actualmente instalada
trivy --version | grep DB

# Limpiar la caché de Trivy (si hay problemas con la base de datos)
trivy image --clear-cache
# o eliminar el directorio de caché
rm -rf ~/.cache/trivy

# Trivy timeout al descargar la base de datos
# Usar un timeout mayor
TRIVY_TIMEOUT=10m trivy image nginx:latest

# Usar una base de datos mirror (para entornos sin acceso a GitHub)
trivy image --db-repository ghcr.io/aquasecurity/trivy-db nginx:latest

# Modo sin conexión (con base de datos pre-descargada)
trivy image --offline-scan --skip-db-update nginx:latest

# Ver qué paquetes tiene una imagen (sin escanear vulnerabilidades)
trivy image --list-all-pkgs nginx:latest

# Debug del escaneo
trivy image --debug nginx:latest 2>&1 | head -50

Conclusión

Trivy es la herramienta más completa y accesible para la seguridad de contenedores, combinando escaneo de vulnerabilidades CVE, detección de secretos, análisis de configuraciones incorrectas y generación de SBOM en una sola herramienta sin configuración compleja. Su integración nativa en pipelines CI/CD con fallo de build automático ante vulnerabilidades críticas convierte el escaneo de seguridad en una parte natural del ciclo de desarrollo, desplazando la responsabilidad de seguridad hacia el principio del pipeline (shift-left security).