Boosting Container Image Security Using Wazuh and Trivy

This article draws inspiration from the Wazuh blog post on enhancing container image security with Wazuh and Trivy.

Containerization has revolutionized software development and deployment, offering scalability and efficiency.

However, this agility can introduce security risks if container images aren’t properly secured.

Vulnerabilities within these images can expose your entire system to threats. This is where the combined power of Wazuh and Trivy comes in.

These open-source tools provide a comprehensive solution for boosting your container image security, ensuring your applications are protected from the ground up.

Why Container Image Security is Crucial

Container images are the building blocks of your containerized applications.

A single vulnerability within an image can compromise your entire system, leading to:

Data Breaches: Sensitive data can be exposed to unauthorized access.

Service Disruptions: Attacks can cause downtime and disrupt business operations.

Reputational Damage: Security incidents can erode trust and damage your brand.

Proactive security measures, like integrating Wazuh and Trivy, are essential for mitigating these risks.

Introducing Trivy: Your Vulnerability Scanner

Trivy is a fast and comprehensive open-source vulnerability scanner that seamlessly integrates into your CI/CD pipeline. It scans your container images for:

Operating System Vulnerabilities: Detects outdated packages and known weaknesses in the base OS.

Application Dependencies: Identifies vulnerabilities in libraries and frameworks your application uses.

Misconfigurations: Uncovers security misconfigurations that can widen your attack surface.

Pre-requirements

Follow the same Trivy installation steps as in the original article.

Additionally, you need to install the Docker library for Python:

pip install docker

Integrating Trivy with Wazuh

Just like in the original article, you’ll need to craft an integration script to connect Trivy and Wazuh.

However, unlike the original, I prefer using Python for this task to enhance flexibility and control.

The script should be created on the agent you intend to monitor. That said, it can also be executed from the server - just keep in mind that it must run on the system where you plan to perform the scanning.

Let’s start by setting up a directory for the script.

mkdir /var/ossec/custom-script

This directory will serve as the home for our script. Now, it’s time to build the scanning script itself.

Create the script using the following command:

vi /var/ossec/custom-script/trivy_scanner.py

Here’s how the script will look once we set it up.

#!/usr/bin/env python
import json
import subprocess
import sys
import docker


def get_all_images():
    client = docker.from_env()
    image_names = list({image.tags[0] for image in client.images.list() if image.tags})
    return image_names


def scan_result(images: list):
    all_results = []
    for image in images:
        if not images or images == [""]:
            print("No images found. Exiting...")
            sys.exit(1)
        try:
            result = subprocess.run(['trivy', 'image', image, '--format', 'json', '--scanners', 'vuln'],
                                    capture_output=True, text=True,
                                    check=True)
            trivy_json = json.loads(result.stdout)
            for result in trivy_json.get("Results", []):
                for vuln in result.get("Vulnerabilities", []):
                    pkg_name = vuln.get("PkgName", "N/A")
                    installed_version = vuln.get("InstalledVersion", "N/A")
                    vuln_id = vuln.get("VulnerabilityID", "N/A")
                    severity = vuln.get("Severity", "N/A")
                    title = vuln.get("Title", "N/A")
                    trivy_result = {
                        'image': image,
                        'package': pkg_name,
                        'version': installed_version,
                        'vulnerability_id': vuln_id,
                        'severity': severity,
                        'title': title
                    }
                    print(json.dumps(trivy_result))
        except (FileNotFoundError, subprocess.CalledProcessError, json.JSONDecodeError) as error:
            all_results.append({"error": str(error)})  # Log errors


if __name__ == '__main__':
    scan_result(get_all_images())

Save the script and assign the appropriate access permissions to ensure it runs securely.

Execute these commands to set the permissions:

chmod 750 /var/ossec/custom-script/trivy_scanner.py
chown root:wazuh -R /var/ossec/custom-script/

Next, it’s time to craft rules for detecting vulnerabilities. Create the file /var/ossec/etc/rules/trivy_rules.xml with the following content.


<group name="trivy,">
  <!-- Parent Rule for Trivy alerts -->
  <rule id="100201" level="0">
    <decoded_as>json</decoded_as>
    <description>Trivy alert detected.</description>
  </rule>

  <!-- This rule detects a critical severity vulnerability in a container image -->
  <rule id="100202" level="14">
    <if_sid>100201</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>

  <!-- This rule detects a high severity vulnerability in a container image -->
  <rule id="100203" level="12">
    <if_sid>100201</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>

  <!-- This rule detects a medium severity vulnerability in a container image -->
  <rule id="100204" level="7">
    <if_sid>100201</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>

  <!-- This rule detects a low severity vulnerability in a container image -->
  <rule id="100205" level="4">
    <if_sid>100201</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>

  <!-- This rule detects an unknown severity vulnerability in a container image -->
  <rule id="100206" level="7">
    <if_sid>100201</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>

Next, configure Wazuh to execute our script by editing the ossec.conf file. Open it with:

vi /var/ossec/etc/ossec.conf

Add the script execution details as follows:

<wodle name="command">
  <disabled>no</disabled>
  <command>/var/ossec/custom-script/trivy_scanner.py</command>
  <interval>6d</interval>
  <ignore_output>no</ignore_output>
  <run_on_start>yes</run_on_start>
  <timeout>0</timeout>
</wodle>

You can adjust the script’s run interval to suit your needs; in my example, it’s set to every 6 days.

Save the changes and restart the services:

systemctl restart wazuh-manager.service

Then, restart the agent:

systemctl restart wazuh-agent

Once completed, you’ll start receiving scan results. Here’s an example event:

2025 Mar 28 14:24:07 compute-vm-14-28-100-ssd-1740727139038->command_
Rule: 100202 (level 14) -> 'Trivy alert [CRITICAL]: Vulnerabilty 'CVE-2023-45853' detected in package 'zlib1g' version '1:1.2.13.dfsg-1' on container image 'nginx:stable'.'
{"image": "nginx:stable", "package": "zlib1g", "version": "1:1.2.13.dfsg-1", "vulnerability_id": "CVE-2023-45853", "severity": "CRITICAL", "title": "zlib: integer overflow and resultant heap-based buffer overflow in zipOpenNewFileInZip4_6"}
image: nginx:stable
package: zlib1g
version: 1:1.2.13.dfsg-1
vulnerability_id: CVE-2023-45853
severity: CRITICAL
title: zlib: integer overflow and resultant heap-based buffer overflow in zipOpenNewFileInZip4_6

See also