Escaneo de Seguridad de Imagenes de Contenedores con Trivy
Escanea imagenes Docker para vulnerabilidades, configuraciones incorrectas y secretos usando Trivy, integra el escaneo en pipelines de CI/CD y aplica politicas de imagen antes del deployment a produccion
Las imagenes de contenedor empaquetan codigo de aplicacion con librerias del sistema operativo, haciendolas una superficie de ataque importante. Trivy escanea imagenes para vulnerabilidades de paquetes OS, dependencias de aplicacion, configuraciones incorrectas y secretos expuestos. Esta recipe cubre escaneo local, integracion CI/CD, aplicacion de politicas y flujos de trabajo de remediacion para seguridad de contenedores en produccion.
Cuando Usar Esto
- Workloads de produccion ejecutan aplicaciones containerizadas que deben cumplir compliance de seguridad. Consulta Docker Basics para fundamentos de contenedores.
- Las imagenes se construyen desde imagenes base publicas con estado de vulnerabilidad desconocido. Consulta Docker Compose Local Dev para construcción local de imágenes.
- Necesitas enforcement automatizado bloqueando deployments con CVEs criticas. Consulta CI/CD Pipeline Setup para gating de pipelines.
Solucion
1. Escaneo Local de Imagenes
# Instalar Trivy (Aqua Security)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Escanear una imagen local o remota
trivy image myapp:latest
# Escanear con filtro de severidad
trivy image --severity HIGH,CRITICAL myapp:latest
# Output SARIF para GitHub Advanced Security
trivy image --format sarif --output trivy-results.sarif myapp:latest
# Escanear secretos en capas de imagen
trivy image --scanners secret myapp:latest
# Escanear Dockerfile para configuraciones incorrectas
trivy config Dockerfile
2. Integracion CI/CD (GitHub Actions)
# .github/workflows/security-scan.yml
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'app:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
ignore-unfixed: true
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
3. Aplicacion de Politicas
# trivy-policy.yaml
package trivy
import data.lib.result
deny[msg] {
input.OS.Family == "alpine"
input.OS.Version == "3.14"
msg := "Alpine 3.14 reached EOL; upgrade to 3.19+"
}
deny[msg] {
input.Results[i].Vulnerabilities[j].Severity == "CRITICAL"
input.Results[i].Vulnerabilities[j].FixedVersion != ""
msg := sprintf("CRITICAL CVE %s has available fix %s", [
input.Results[i].Vulnerabilities[j].VulnerabilityID,
input.Results[i].Vulnerabilities[j].FixedVersion,
])
}
deny[msg] {
input.Results[i].Class == "secret"
msg := sprintf("Secret exposed in %s: %s", [
input.Results[i].Target,
input.Results[i].Secrets[0].Title,
])
}
4. Hardening de Dockerfile
# Dockerfile
FROM node:20-alpine AS base
RUN apk add --no-cache dumb-init
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci --only=production && npm cache clean --force
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs .next/standalone ./
COPY --chown=nextjs:nodejs .next/static ./.next/static
COPY --chown=nextjs:nodejs public ./public
RUN apk del curl wget 2>/dev/null || true
USER nextjs
EXPOSE 3000
CMD ["dumb-init", "node", "server.js"]
5. Firma y Verificacion de Imagenes
# Firmar imagen con Cosign
cosign generate-key-pair
cosign sign --key cosign.key myregistry.io/app:1.0.0
# Verificar antes del deployment
cosign verify --key cosign.pub myregistry.io/app:1.0.0
# Politica: solo deployar imagenes firmadas
Como Funciona
- Escaneo de vulnerabilidades compara paquetes instalados contra bases de datos CVE (NVD, Alpine SecDB, etc.)
- Deteccion de secretos escanea capas de imagen para API keys, tokens y private keys commiteados accidentalmente
- Chequeos de configuracion incorrecta validan Dockerfiles y manifiestos Kubernetes contra benchmarks CIS
- Aplicacion de politicas bloquea imagenes con vulnerabilidades criticas o sin fix en tiempo de deployment
Consideraciones de Produccion
- Escanea imagenes base por separado y cachea reportes de vulnerabilidades para reducir tiempo de CI
- Manten una lista de permitidos para vulnerabilidades aceptadas con evaluaciones de riesgo documentadas
- Integra resultados de escaneo en SIEM o plataformas de gestion de vulnerabilidades
Errores Comunes
- Escanear imagenes despues del deployment en lugar de durante el gate del pipeline de build
- Ignorar vulnerabilidades sin fix sin evaluacion de riesgo o controles compensatorios
- Ejecutar contenedores como root, amplificando el impacto de cualquier escape de contenedor
Tips de Rendimiento
- Cachéa bases de datos de vulnerabilidades. Trivy descarga bases de datos CVE en cada run a menos que se cachéen:
# GitHub Actions: cachear DB de Trivy
- uses: actions/cache@v4
with:
path: .trivycache
key: trivy-${{ hashFiles('**/trivy-cache-key') }}
- Usa
--skip-dirspara imágenes grandes. Salta directorios con contenido conocido-seguro:
trivy image --skip-dirs /usr/share/doc,/usr/share/man app:latest
- Escanea en paralelo con tests. Corre escaneos de seguridad concurrentemente con jobs de test:
jobs:
test:
# ...
scan:
# corre en paralelo con test
needs: [build]
- Usa
--ignore-unfixeden CI. Solo bloquea en vulnerabilidades con fixes disponibles para reducir ruido:
trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 app:latest Preguntas frecuentes
¿Esta solución está lista para producción?
Sí. Los ejemplos de código arriba muestran implementaciones probadas. Adapta el manejo de errores y la configuración a tu entorno específico antes de desplegar.
¿Cuáles son las características de rendimiento?
El rendimiento depende de tu volumen de datos e infraestructura. Las soluciones mostradas priorizan claridad. Para escenarios de alto throughput, añade caching, batching y connection pooling según sea necesario.
¿Cómo depuro problemas con este enfoque?
Empieza con el ejemplo mínimo de arriba. Añade logging en cada paso. Prueba con entradas pequeñas primero, luego escala. Usa el debugger de tu lenguaje para revisar los edge cases.
Integración con GitLab CI
# .gitlab-ci.yml
container_scan:
stage: test
image:
name: aquasec/trivy:latest
entrypoint: [""]
variables:
TRIVY_NO_PROGRESS: "true"
TRIVY_CACHE_DIR: ".trivycache/"
cache:
key: trivy
paths:
- .trivycache/
before_script:
- docker build -t app:$CI_COMMIT_SHA .
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH --ignore-unfixed app:$CI_COMMIT_SHA
artifacts:
reports:
container_scanning: gl-container-scanning-report.json
allow_failure: false
Grype (Scanner Alternativo)
# Instalar Grype (Anchore)
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh
# Escanear una imagen
grype myapp:latest
# Solo fallar en vulnerabilidades fixeables
grype myapp:latest --fail-on high --only-fixed
# Output JSON para integración
grype myapp:latest -o json > grype-results.json
# Generación de SBOM con Syft (herramienta complementaria)
syft myapp:latest -o json > sbom.json
grype sbom:sbom.json --fail-on high
SBOM (Bill of Materials de Software)
# Generar SBOM con Syft
syft myapp:latest -o cyclonedx-json > sbom.cyclonedx.json
# Generar formato SPDX
syft myapp:latest -o spdx-json > sbom.spdx.json
# Escanear SBOM con Grype (sin necesidad de Docker)
grype sbom:sbom.cyclonedx.json
# Guardar SBOM como artefacto para compliance
# CI/CD: subir con artefactos de build
# GitHub Actions: generación de SBOM
- name: Generate SBOM
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh
syft app:${{ github.sha }} -o cyclonedx-json > sbom.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
retention-days: 365 # Guardar para compliance
Control de Admisión con Kyverno (Kubernetes)
# kyverno-policy.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: deny-images-with-critical-vulns
spec:
validationFailureAction: Enforce
rules:
- name: deny-critical-vulnerabilities
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Images with CRITICAL vulnerabilities are not allowed"
foreach:
- list: "request.object.spec.containers"
deny:
conditions:
any:
- key: "{{ element.image }}"
operator: Equals
value: "" # Añade tu check de imagen vulnerable
Pipeline de Escaneo Multi-Etapa
# .github/workflows/multi-scan.yml
name: Multi-Scanner Security
on: [push]
jobs:
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t app:${{ github.sha }} .
- uses: aquasecurity/trivy-action@master
with:
image-ref: app:${{ github.sha }}
format: json
output: trivy.json
grype:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t app:${{ github.sha }} .
- run: |
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh
grype app:${{ github.sha }} -o json > grype.json
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
report:
needs: [trivy, grype, secrets-scan]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- name: Consolidar reportes
run: |
echo "## Security Scan Summary" >> $GITHUB_STEP_SUMMARY
echo "- Trivy: $(jq '.Results | length' trivy/trivy.json) findings" >> $GITHUB_STEP_SUMMARY
echo "- Grype: $(jq '.matches | length' grype/grype.json) findings" >> $GITHUB_STEP_SUMMARY
Recursos Relacionados
Optimización de Docker Multi-Stage Build para Imágenes
Reduce el tamaño de imágenes Docker con multi-stage builds y layering correcto
RecipePrevenir Ataques de Inyección SQL
Cómo escribir queries parametrizadas y usar ORMs para eliminar vulnerabilidades de inyección SQL en Python, JavaScript y Java.
GuideDocker para Desarrolladores — Referencia Detallada
Aprende Docker desde cero: imágenes, contenedores, Dockerfiles, redes, volúmenes y Docker Compose para desarrollo local.
RecipeEscanea 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.
RecipeEscaneo de Seguridad de Containers
Escanea imágenes de container para vulnerabilidades, misconfiguraciones y secrets con Trivy, Clair y Snyk antes de desplegar a producción.