1. Introducción
Esta práctica integra Trivy con Wazuh para analizar vulnerabilidades en imágenes Docker. El endpoint Ubuntu ejecuta un script mediante el módulo command, genera una salida estructurada con el prefijo Trivy: y envía el resultado al manager para su decodificación y clasificación por severidad.
wazuh-logtest y con eventos reales.2. Instalar Docker Engine y descargar imágenes
Los pasos siguientes instalan Docker Engine y despliegan un conjunto de imágenes base en el endpoint Ubuntu.
root para ejecutar los comandos de esta sección.2.1 Actualizar paquetes e instalar cURL
apt update && apt install curl -y
2.2 Descargar y ejecutar el instalador de Docker
curl -sSL https://get.docker.com/ | sh
2.3 Crear el directorio del entorno de contenedores
mkdir -p /docker && cd /docker
2.4 Crear el archivo docker-compose.yml
nano /docker/docker-compose.yml
services:
api:
image: python:3.9-alpine
db:
image: postgres:alpine
redis:
image: redis:alpinepython:3.9-alpine, postgres:alpine y redis:alpine.2.5 Descargar las imágenes con Docker Compose
cd /docker docker compose pull
2.6 Verificar que las imágenes están presentes
docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE postgres alpine 2bf60600670f 8 days ago 278MB redis alpine ee33180a8437 5 weeks ago 41.4MB python 3.9-alpine 8831b2c8c07c 2 months ago 49.1MB
3. Instalar Trivy
Los pasos siguientes instalan Trivy en el endpoint Ubuntu.
3.1 Instalar dependencias requeridas
apt-get install -y wget apt-transport-https gnupg lsb-release
3.2 Importar la clave del repositorio de Trivy
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | tee /usr/share/keyrings/trivy.gpg > /dev/null
3.3 Añadir el repositorio APT de Trivy
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | tee -a /etc/apt/sources.list.d/trivy.list
3.4 Actualizar índices de paquetes
apt-get update
3.5 Instalar Trivy
apt-get install trivy
3.6 Verificar la instalación
trivy --version
Version: 0.59.1
4. Crear el script personalizado y configurar el módulo Command
El script detecta las imágenes existentes en el endpoint y ejecuta un análisis de vulnerabilidades con Trivy sobre cada una. El módulo command ejecuta el script periódicamente y Wazuh trata su salida como contenido de log para su análisis posterior.
4.1 Crear el directorio y editar el script
mkdir -p /var/ossec/custom-script/ && nano /var/ossec/custom-script/trivy_scan.sh
4.2 Contenido de /var/ossec/custom-script/trivy_scan.sh
#!/bin/bash
# Copyright (C) 2015-2025, Wazuh Inc.
# Directory to save the custom output template
TEMPLATE_DIR="/tmp"
TEMPLATE_FILE="$TEMPLATE_DIR/trivy-custom.tmpl"
# Create the custom output template
cat <<'EOL' > "$TEMPLATE_FILE"
"Package","Version Installed","Vulnerability ID","Severity"
{{- range $ri, $r := . }}
{{- range $vi, $v := .Vulnerabilities }}
"{{ $v.PkgName }}","{{$v.InstalledVersion }}","{{ $v.VulnerabilityID }}","{{$v.Severity }}","{{$v.Title }}"
{{- end}}
{{- end }}
EOL
# Retrieve list of container images (including both repository and tag)
images=$(docker images --format "{{.Repository}}:{{.Tag}}")
if [ -z "$images" ]; then
echo "No images found. Exiting..."
exit 1
fi
# Loop through each container image and run Trivy scan
for image in $images; do
# Run Trivy scan on the current image using the custom output template
trivy_output=$(trivy --scanners vuln i -q --format template --template "@/tmp/trivy-custom.tmpl" "$image")
# Check if the scan was successful
if [ $? -ne 0 ]; then
echo "Error running Trivy scan on image $image. Skipping..."
continue
fi
# Process Trivy output for the current image
while IFS= read -r line; do
# Prepend image name with "Trivy:", followed by image name and a comma
formatted_line="Trivy:\"$image\",$line"
# Print the formatted line with quoted image name
echo "$formatted_line"
done <<< "$trivy_output"
done
# Clean up the custom output template
rm -f "$TEMPLATE_FILE"Trivy: al inicio de cada línea y elimina la plantilla temporal al finalizar.4.3 Asignar permisos de ejecución
chmod 750 /var/ossec/custom-script/trivy_scan.sh
4.4 Ajustar propietario del script y su directorio
chown root:wazuh /var/ossec/custom-script/ -R
4.5 Probar el script manualmente en el agente
/var/ossec/custom-script/trivy_scan.sh | head -n 20
Trivy:"imagen","paquete","version","CVE","severity","titulo".4.6 Editar la configuración del agente
nano /var/ossec/etc/ossec.conf
4.7 Añadir el bloque del módulo Command
<!-- Trivy container vulnerability scanner script --> <wodle name="command"> <disabled>no</disabled> <command>/var/ossec/custom-script/trivy_scan.sh</command> <interval>3d</interval> <ignore_output>no</ignore_output> <run_on_start>yes</run_on_start> <timeout>0</timeout> </wodle>
4.8 Significado de los parámetros del módulo Command
| Parámetro | Función |
|---|---|
disabled | Indica si la configuración del módulo está activa. El valor no mantiene habilitada la ejecución. |
command | Define la ruta del script trivy_scan.sh que se ejecutará en el endpoint. |
interval | Establece el intervalo de ejecución del script. En esta configuración se ejecuta cada 3 días. |
ignore_output | Determina si la salida del comando se ignora o se procesa. El valor no permite que Wazuh la procese y la registre. |
run_on_start | Ejecuta la configuración del módulo inmediatamente después del arranque del servicio del agente. |
timeout | Define el tiempo máximo de ejecución. El valor 0 permite la ejecución sin restricciones de tiempo impuestas por el módulo. |
5. Reiniciar el agente
El reinicio del agente aplica la configuración del módulo command y permite la ejecución inmediata del script si run_on_start está habilitado.
systemctl restart wazuh-agent
6. Crear decoders y reglas en el manager
root.6.1 Crear el archivo de decoders
nano /var/ossec/etc/decoders/trivy_decoders.xml
6.2 Añadir los decoders personalizados
<decoder name="trivy-decoder"> <prematch>^Trivy:</prematch> </decoder> <decoder name="trivy-decoder-fields"> <parent>trivy-decoder</parent> <regex offset="after_parent">"(\.+)","(\.+)","(\.+)","(\.+)","(\.+)","(\.+)"</regex> <order>image, package, version, vulnerability_id, severity, description</order> </decoder>
6.3 Crear el archivo de reglas
nano /var/ossec/etc/rules/trivy_rules.xml
6.4 Añadir las reglas personalizadas
<group name="trivy,">
<rule id="100250" level="0">
<decoded_as>trivy-decoder</decoded_as>
<description>Trivy alert detected.</description>
</rule>
<rule id="100251" level="14">
<if_sid>100250</if_sid>
<field name="severity">CRITICAL</field>
<description>Trivy alert [CRITICAL]: Vulnerabilty '$(vulnerability_id)' detected in package '$(package)' version '$(version)' on container image '$(image)'.</description>
</rule>
<rule id="100252" level="12">
<if_sid>100250</if_sid>
<field name="severity">HIGH</field>
<description>Trivy alert [HIGH]: Vulnerabilty '$(vulnerability_id)' detected in package '$(package)' version '$(version)' on container image '$(image)'.</description>
</rule>
<rule id="100253" level="7">
<if_sid>100250</if_sid>
<field name="severity">MEDIUM</field>
<description>Trivy alert [MEDIUM]: Vulnerabilty '$(vulnerability_id)' detected in package '$(package)' version '$(version)' on container image '$(image)'.</description>
</rule>
<rule id="100254" level="4">
<if_sid>100250</if_sid>
<field name="severity">LOW</field>
<description>Trivy alert [LOW]: Vulnerabilty '$(vulnerability_id)' detected in package '$(package)' version '$(version)' on container image '$(image)'.</description>
</rule>
<rule id="100255" level="7">
<if_sid>100250</if_sid>
<field name="severity">UNKNOWN</field>
<description>Trivy alert [UNKNOWN]: Vulnerabilty '$(vulnerability_id)' detected in package '$(package)' version '$(version)' on container image '$(image)'.</description>
</rule>
</group>6.5 Significado de los Rule ID utilizados
| Rule ID | Función |
|---|---|
100250 | Detecta la salida del escaneo de Trivy. Esta regla no genera alertas visibles en el dashboard al tener nivel 0. |
100251 | Detecta vulnerabilidades con severidad CRITICAL. |
100252 | Detecta vulnerabilidades con severidad HIGH. |
100253 | Detecta vulnerabilidades con severidad MEDIUM. |
100254 | Detecta vulnerabilidades con severidad LOW. |
100255 | Detecta vulnerabilidades con severidad UNKNOWN. |
6.6 Ajustar propietario y permisos
chown wazuh:wazuh /var/ossec/etc/rules/trivy_rules.xml /var/ossec/etc/decoders/trivy_decoders.xml chmod 660 /var/ossec/etc/rules/trivy_rules.xml /var/ossec/etc/decoders/trivy_decoders.xml
7. Reiniciar el manager
El reinicio del manager aplica los nuevos decoders y reglas.
systemctl restart wazuh-manager
8. Validación con wazuh-logtest
La validación con wazuh-logtest permite comprobar que el decoder extrae los campos esperados y que la regla correspondiente se activará según la severidad del evento.
/var/ossec/bin/wazuh-logtest
Trivy:"python:3.9-alpine","jaraco.context","5.3.0","CVE-2026-23949","HIGH","jaraco.context: jaraco.context: Path traversal via malicious tar archives"
Phase 2: name: 'trivy-decoder' image: 'python:3.9-alpine' package: 'jaraco.context' version: '5.3.0' vulnerability_id: 'CVE-2026-23949' severity: 'HIGH' description: 'jaraco.context: jaraco.context: Path traversal via malicious tar archives' Phase 3: id: '100252' description: 'Trivy alert [HIGH]: Vulnerabilty ...' groups: '['trivy']' Alert to be generated.
9. Visualización en Wazuh dashboard
Tras una ejecución real del agente, las alertas pueden revisarse en los archivos del manager y en Discover o en los paneles del dashboard.
grep 'Trivy alert' /var/ossec/logs/alerts/alerts.json | tail -n 20 grep '"id":"10025' /var/ossec/logs/alerts/alerts.json | tail -n 20
grep 'Trivy:' /var/ossec/logs/archives/archives.json | tail -n 20
rule.groups:trivy, rule.id:100252 o el nombre de la imagen analizada.10. Validación con eventos reales
10.1 Reiniciar el agente para lanzar el wodle
systemctl restart wazuh-agent
10.2 Confirmar la presencia de eventos y alertas
grep 'Trivy:' /var/ossec/logs/archives/archives.json | tail -n 10 grep 'Trivy alert' /var/ossec/logs/alerts/alerts.json | tail -n 10
10.3 Reiniciar Filebeat si las alertas no se visualizan
systemctl restart filebeat systemctl status filebeat tail -f /var/log/filebeat/filebeat
11. Errores comunes
| Problema | Causa probable | Solución |
|---|---|---|
| El script no devuelve vulnerabilidades | Trivy no puede descargar la base de datos o no existen imágenes locales | Comprobar conectividad, validar la descarga de la base de datos y revisar docker image ls. |
| El evento aparece en archives.json pero no en alerts.json | Las reglas no casan o el ruleset no está cargado | Validar con wazuh-logtest y reiniciar wazuh-manager. |
| El dashboard no muestra alertas | Las alertas no se han indexado todavía o los filtros no coinciden | Comprobar primero alerts.json, reiniciar filebeat y revisar los filtros del dashboard. |
| El agente no puede ejecutar el script | Permisos insuficientes sobre el script o sobre Docker | Revisar permisos, propietario y acceso al motor Docker. |