courses

Lab — Week 4: Container Security II — Scanning, Supply Chain, and Monitoring
Due: 31 July 2026
Submission: GitLab repo secdevops-s26-<CECS>

Overview

Look inside the containers — scanning for known CVEs in OS packages and dependencies — and bring the Wazuh agent online so everything from here on is observed.

Prerequisites


Part 1: Image Scanning and Supply Chain Security

Every container image inherits vulnerabilities from every layer below it. This part scans your hardened images for known CVEs, makes evidence-based remediation decisions, generates an SBOM, and produces a scanning script suitable for use in a CI pipeline.

Initial Scan

  1. Scan each image for HIGH and CRITICAL vulnerabilities:
    trivy image --severity HIGH,CRITICAL dvwa:hardened
    trivy image --severity HIGH,CRITICAL nginx-proxy:hardened
    trivy image --severity HIGH,CRITICAL mariadb:11
    
  2. Save the reports in JSON format:
    trivy image --format json --output lab07/trivy-dvwa-before.json dvwa:hardened
    trivy image --format json --output lab07/trivy-nginx-before.json nginx-proxy:hardened
    

Triage and Remediation

  1. For every HIGH and CRITICAL finding across all images, complete a triage entry in lab07/triage.md using this format:

    CVE Severity Package Image Classification Rationale Action
    CVE-XXXX-XXXX CRITICAL libssl3 dvwa True Positive — fix Remotely exploitable in network service Rebuild with updated base

    Classification must be one of:

    • True Positive — fix: exploitable in your environment; rebuild
    • True Positive — accept: exploitable but mitigated elsewhere; document rationale
    • False Positive: scanner error or not applicable; document reasoning
  2. For every True Positive — fix finding: update the relevant Dockerfile to use a newer base image or explicitly upgrade the affected package, rebuild the image, and re-scan to confirm the CVE no longer appears.

  3. Save post-remediation scans:

    trivy image --format json --output lab07/trivy-dvwa-after.json dvwa:hardened
    trivy image --format json --output lab07/trivy-nginx-after.json nginx-proxy:hardened
    

SBOM Generation

  1. Generate a CycloneDX SBOM for the hardened DVWA image:
    trivy sbom --format cyclonedx --output lab07/sbom-dvwa.json dvwa:hardened
    
  2. Answer in lab07/lab07.md: if CVE-2021-44228 (Log4Shell) were disclosed today, how would you use this SBOM to determine whether your DVWA image is affected? What command would you run?

CI Gate Script

  1. Write scripts/scan-images.sh that:
    • Scans dvwa:hardened and nginx-proxy:hardened
    • Exits non-zero if any CRITICAL CVE is found in either image
    • Prints a summary line for each image (e.g., dvwa:hardened — 0 CRITICAL, 2 HIGH)
  2. Run the script and confirm it exits 0 with your remediated images.

Deliverables

Create lab07/ in your repo containing:


Part 2: Container Networking, Secrets Integration, and Wazuh Agent

This part connects three threads: secrets from OpenBao reach containers without appearing in environment variables, network segmentation is verified by attempting forbidden connections, and the Wazuh agent is registered so that everything you do from this point forward is observed by the SIEM.

Part 1 and Week 2 Part 2 must be complete — hardened stack running and OpenBao running and unsealed; the Wazuh manager must be running on 10.10.10.30.

Secrets Integration

  1. Update docker-compose.yml to pass the DVWA database password from OpenBao rather than hardcoding it. Use Docker Compose secrets or an OpenBao Agent sidecar — your choice. Document your approach.

    Option A — Docker Compose secrets:

    secrets:
      db_password:
        file: ./secrets/db_password.txt   # populated from OpenBao at deploy time
    services:
      dvwa:
        secrets: [db_password]
        # reads /run/secrets/db_password at runtime
    

    Option B — OpenBao Agent sidecar: configure an openbao-agent container that writes the secret to a shared volume mounted into the dvwa container.

  2. Verify the password is not visible in docker inspect:

    docker inspect $(docker compose ps -q dvwa) \
      | jq '.[0].Config.Env[] | select(test("password|secret"; "i"))'
    # Must return nothing
    

Network Isolation Verification

  1. Confirm that all expected paths work:
    # nginx → dvwa: must succeed
    docker compose exec nginx curl -s -o /dev/null -w "%{http_code}" http://dvwa/
    # dvwa → mariadb: must succeed
    docker compose exec dvwa mariadb-admin ping -h mariadb -u dvwauser
    
  2. Confirm that forbidden paths fail:
    # nginx → mariadb: must fail
    docker compose exec nginx mariadb-admin ping -h mariadb 2>&1
    

    Document each result in lab08/lab08.md with an explanation of which network policy enforces it.

Wazuh Agent Registration

  1. Configure the Wazuh agent on ubuntu-server to point to the manager:
    sudo WAZUH_MANAGER='10.10.10.30' \
         WAZUH_AGENT_NAME='ubuntu-server' \
         dpkg-reconfigure wazuh-agent
    sudo systemctl enable --now wazuh-agent
    
  2. On the Wazuh manager (10.10.10.30), confirm the agent appears:
    sudo /var/ossec/bin/agent_control -l
    
  3. In the Wazuh dashboard (https://10.10.10.30), navigate to Agents and take a screenshot showing ubuntu-server with status Active.

  4. Enable the Docker Wodle in /var/ossec/etc/ossec.conf on ubuntu-server so Wazuh monitors container lifecycle events:
    <wodle name="docker-listener">
      <interval>10m</interval>
      <attempts>5</attempts>
      <run_on_start>yes</run_on_start>
      <disabled>no</disabled>
    </wodle>
    

    Restart the agent: sudo systemctl restart wazuh-agent

  5. From the Wazuh dashboard, confirm at least one Docker event appears in Security Events after restarting or creating a container.

Threat Model Update

  1. In lab02/threat-model.md, mark threats mitigated by secrets integration and Wazuh monitoring. Add new threats surfaced by having an agent in place (e.g., what if the agent itself is compromised?).

Deliverables

Create lab08/ in your repo containing:


Submission

Commit all files for both parts — lab07/ and lab08/ — and push to your GitLab repo. The lab is considered submitted when the commit appears in GitLab before the due date.