Skip to content
SP StackPractices
intermediate Por StackPractices

Escanea Imágenes Docker en busca de CVEs con Trivy y Grype

Escanea imágenes Docker en busca de vulnerabilidades antes del despliegue usando Trivy y Grype. Cubre integración CI, filtrado por severidad, SBOM y remediación.

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Visión General

Las imágenes de contenedores suelen contener paquetes con vulnerabilidades conocidas (CVEs). Escanear imágenes antes del despliegue detecta estos problemas temprano. Trivy y Grype son dos scanners open-source populares que detectan vulnerabilidades en paquetes del OS y dependencias de aplicaciones. Esta recipe cubre escaneo de imágenes, filtrado por severidad, integración con CI, generación de SBOMs y remediación de vulnerabilidades.

Cuándo Usar

  • Quieres escanear imágenes antes de pushear a un registry
  • Necesitas bloquear despliegues con vulnerabilidades críticas
  • Generas un SBOM (Software Bill of Materials) para compliance
  • Quieres auditar imágenes en un registry en busca de CVEs conocidos

Solución

Instalar Trivy

# macOS
brew install trivy

# Linux (Debian/Ubuntu)
sudo apt-get install trivy

# Docker
docker run --rm aquasec/trivy:latest --version

Instalar Grype

# macOS
brew tap anchore/grype && brew install grype

# Linux
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh

# Docker
docker run --rm anchore/grype:latest --version

Escaneo básico de imagen con Trivy

# Escanear una imagen local
trivy image myapp:latest

# Escanear una imagen remota
trivy image nginx:1.25

# Output como JSON
trivy image --format json --output trivy-report.json myapp:latest

# Output como tabla (default)
trivy image --format table myapp:latest

Escaneo básico de imagen con Grype

# Escanear una imagen local
grype myapp:latest

# Escanear una imagen remota
grype nginx:1.25

# Output como JSON
grype myapp:latest -o json > grype-report.json

# Output como tabla
grype myapp:latest -o table

Filtrar por severidad

# Trivy — solo mostrar HIGH y CRITICAL
trivy image --severity HIGH,CRITICAL myapp:latest

# Trivy — ignorar LOW y UNKNOWN
trivy image --severity HIGH,CRITICAL,MEDIUM myapp:latest

# Grype — solo mostrar critical
grype myapp:latest --only-cve-matches

# Grype — fallar solo en critical
grype myapp:latest --fail-on critical

Ignorar vulnerabilidades específicas

# Trivy — crear un archivo .trivyignore
echo "CVE-2023-1234" >> .trivyignore
echo "CVE-2023-5678" >> .trivyignore

# Trivy — usar ignore file
trivy image --ignorefile .trivyignore myapp:latest

# Grype — usar ignore file
grype myapp:latest --ignore-policy .grype-ignore.yaml

Escanear en CI con GitHub Actions

# .github/workflows/scan.yml
name: Container Security Scan

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

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

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Run Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: table
          severity: HIGH,CRITICAL
          exit-code: 1  # Fallar el job si se encuentran vulnerabilidades

      - name: Generate SBOM
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: json
          output: sbom.json
          scan-type: fs
          scan-ref: .

      - name: Upload SBOM artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.json

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

      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .

      - name: Run Grype scan
        uses: anchore/scan-action@v3
        with:
          image: myapp:${{ github.sha }}
          fail-build: true
          severity-cutoff: critical

Generar SBOM (Software Bill of Materials)

# Trivy — generar SBOM en formato CycloneDX
trivy image --format cyclonedx --output sbom.json myapp:latest

# Trivy — generar SBOM en formato SPDX
trivy image --format spdx-json --output sbom.spdx.json myapp:latest

# Syft — herramienta dedicada de SBOM (compañera de Grype)
syft myapp:latest -o cyclonedx-json > sbom.json
syft myapp:latest -o spdx-json > sbom.spdx.json

Escanear Dockerfile en busca de misconfiguraciones

# Trivy — escanear Dockerfile en busca de best practices
trivy config Dockerfile

# Trivy — escanear archivos IaC (Terraform, K8s manifests)
trivy config ./k8s/
trivy config ./terraform/

Escanear un contenedor en ejecución

# Trivy — escanear desde dentro de un contenedor en ejecución
docker exec myapp trivy fs --severity HIGH,CRITICAL /

# Grype — escanear el filesystem de un contenedor en ejecución
docker exec myapp grype /

Estrategias de remediación

# 1. Actualizar la imagen base a una versión más nueva
# Antes: FROM node:18
# Después:  FROM node:18.20.4-slim

# 2. Usar una imagen base minimal para reducir attack surface
# Antes: FROM ubuntu:22.04
# Después:  FROM alpine:3.20

# 3. Actualizar paquetes en el Dockerfile
# Añadir después de FROM:
RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*

# 4. Usar imágenes distroless (sin shell, sin package manager)
FROM gcr.io/distroless/nodejs18-debian12

# 5. Pinear versiones específicas de paquetes
RUN apt-get install -y --no-install-recommends \
    curl=7.88.1-10+deb12u6 \
    && rm -rf /var/lib/apt/lists/*

Multi-stage build para reducir vulnerabilidades

# Build stage — tiene build tools (mayor attack surface)
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Runtime stage — imagen minimal
FROM node:18-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER node
EXPOSE 3000
CMD ["node", "dist/index.js"]

Escaneo programado de registry

# .github/workflows/registry-scan.yml
name: Weekly Registry Scan

on:
  schedule:
    - cron: "0 2 * * 1"  # Todos los lunes a las 2 AM

jobs:
  scan-registry:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        image:
          - myapp:latest
          - myapp:stable
    steps:
      - name: Run Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ matrix.image }}
          format: sarif
          output: trivy-results.sarif

      - name: Upload to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

Explicación

Trivy y Grype escanean imágenes de contenedores comparando paquetes instalados contra bases de datos de vulnerabilidades:

  • Trivy: Escanea paquetes del OS (apt, apk, yum), dependencias de lenguaje (npm, pip, gem) y archivos de config. Usa NVD y advisories específicos del OS. Rápido, single binary.
  • Grype: Escanea paquetes del OS y dependencias de lenguaje. Usa la base de datos de vulnerabilidades de Anchore. Se complementa con Syft para generación de SBOM.
  • SBOM: Software Bill of Materials. Una lista de todos los componentes y dependencias en una imagen. CycloneDX y SPDX son formatos estándar. Requerido para compliance (EO 14028).
  • Severity: LOW, MEDIUM, HIGH, CRITICAL. Filtrar para enfocarse en vulnerabilidades accionables. Bloquear solo CRITICAL en CI para evitar ruido.
  • CVE: Common Vulnerabilities and Exposures. Un identificador único para una vulnerabilidad conocida. Cada CVE tiene un score de severidad (CVSS).
  • CVSS: Common Vulnerability Scoring System. Un score de 0 a 10 que indica severidad. 7-9 es HIGH, 9-10 es CRITICAL.

Variantes

HerramientaAlcanceSBOMIntegración CIUsar Cuando
TrivyOS + lang deps + configGitHub ActionsScanner all-in-one
GrypeOS + lang depsVía SyftGitHub ActionsPareado con Syft SBOM
SnykOS + lang depsSnyk CIComercial, DB más profunda
ClairPaquetes OSNoQuay registryScanning a nivel registry
Docker ScoutOS + lang depsDocker HubSolución Docker-native

Pautas

  • Escanear imágenes en CI antes de pushear a un registry. Bloquear en vulnerabilidades CRITICAL.
  • Usar --severity HIGH,CRITICAL para enfocarse en issues accionables. LOW y MEDIUM crean ruido.
  • Actualizar imágenes base regularmente. Nuevos CVEs se descubren constantemente.
  • Usar imágenes base minimales (alpine, distroless, slim) para reducir attack surface.
  • Usar multi-stage builds. Build tools en el build stage, imagen runtime minimal.
  • Generar SBOMs para compliance. Almacenar como build artifacts o en un registry.
  • No ignorar vulnerabilidades sin documentación. Trackear CVEs ignorados con fechas de expiración.
  • Escanear registries en un schedule. Nuevos CVEs afectan imágenes ya pusheadas.
  • Pinear versiones de imágenes base. Evitar tags latest — pueden introducir cambios inesperados.
  • Usar output SARIF para integración con GitHub Security tab. Visualiza vulnerabilidades en PRs.

Errores Comunes

  • Escanear solo una vez al build. Nuevos CVEs se descubren diariamente. Programar escaneos regulares de registry.
  • Ignorar todas las vulnerabilidades para que CI pase. Esto derrota el propósito de escanear.
  • Usar tags latest de imagen base. Una nueva versión puede introducir breaking changes o nuevas vulnerabilidades.
  • No actualizar imágenes base. Imágenes base viejas acumulan CVEs con el tiempo.
  • Escanear solo paquetes del OS. Las dependencias de lenguaje (npm, pip) también tienen vulnerabilidades.
  • No generar SBOMs. Los SBOMs son requeridos para compliance e incident response.
  • Ejecutar como root en el contenedor. Incluso sin vulnerabilidades, root aumenta el blast radius.
  • No fallar CI en vulnerabilidades críticas. Un scan que no bloquea es solo un reporte.

Preguntas Frecuentes

¿Debo usar Trivy o Grype?

Ambos son excelentes. Trivy es una herramienta all-in-one que también escanea config files e IaC. Grype se complementa bien con Syft para generación de SBOM. Probar ambos y elegir el que se ajuste a tu workflow.

¿Cómo arreglo una vulnerabilidad en una imagen base?

Actualizar a una versión más nueva de la imagen base. Si la vulnerabilidad está en un paquete, actualizar ese paquete con apt-get upgrade o apk upgrade. Si no hay fix disponible, considerar cambiar a una imagen base diferente (alpine, distroless).

¿Qué es un SBOM y por qué lo necesito?

Un SBOM (Software Bill of Materials) es una lista formal de todos los componentes en un artifact de software. Es requerido para compliance (US Executive Order 14028) y ayuda con incident response — cuando se anuncia un nuevo CVE, revisas tu SBOM para ver si estás afectado.

¿Cómo escaneo imágenes en un registry privado?

Proporcionar credenciales al scanner:

trivy image --username "$REGISTRY_USER" --password "$REGISTRY_PASS" registry.example.com/myapp:latest