Skip to content

Container Security Scanning — Trivy, Clair, Docker Bench, Image Signing, and SBOM

DodaTech Updated 2026-06-22 7 min read

In this tutorial, you'll learn about Container Security Scanning. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Container security scanning detects vulnerabilities in container images by analyzing installed packages, operating system dependencies, and application libraries — enabling teams to identify and remediate risks before deployment.

What You'll Learn

Why It Matters

Container images bundle entire operating systems and application stacks. A single vulnerable libssl package in a base image exposes every container running that image to exploitation. Automated scanning catches these issues in the CI/CD pipeline before deployment, while image signing and SBOM generation provide supply chain transparency.

Real-World Use

Durga Antivirus Pro scans every container image in its pipeline using Trivy for OS and library vulnerabilities, generates SPDX SBOMs for software supply chain Compliance, and signs images with Cosign before pushing to the registry — ensuring only verified, non-vulnerable images reach production.

flowchart LR
    A[Build Image] --> B[Trivy Scan]
    B --> C{Pass?}
    C -->|Yes| D[Generate SBOM]
    C -->|No| E[Block & Notify]
    D --> F[Sign Image]
    F --> G[Push to Registry]
    G --> H[Docker Bench]
    H --> I[Runtime Security]
    style C fill:#ff9800,color:#fff
    style E fill:#f44336,color:#fff
    style G fill:#4CAF50,color:#fff
â„šī¸ Info

Prerequisites: Basic Docker and Containerization knowledge. Familiarity with CI/CD pipeline concepts.

Core Scanning Tools

Trivy — Comprehensive Vulnerability Scanner

Trivy scans container images, filesystems, and Git repositories for vulnerabilities in OS packages (Alpine, Debian, Ubuntu) and application dependencies (npm, pip, gem, cargo).

# Scan the latest nginx image
trivy image nginx:1.25-alpine

# Expected output:
# nginx:1.25-alpine (alpine 3.18)
# ================================
# Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 1, CRITICAL: 0)
#
# +---------------+---------------------+----------+-----------+
# |   Library     |   Vulnerability     | Severity | Installed |
# +---------------+---------------------+----------+-----------+
# | libcrypto3    | CVE-2024-XXXX       | HIGH     | 3.1.4     |
# | libssl3       | CVE-2024-XXXX       | MEDIUM   | 3.1.4     |
# | zlib          | CVE-2024-YYYY       | LOW      | 1.2.13    |
# +---------------+---------------------+----------+-----------+

Expected output: Trivy downloads vulnerability databases on first run, then scans every layer of the image — reporting each vulnerable package with its severity, CVE identifier, and installed version.

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

on:
  push:
    branches: [main]

jobs:
  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: sarif
          output: trivy-results.sarif
          exit-code: 1
          severity: CRITICAL,HIGH

      - name: Upload scan results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

Expected behavior: The pipeline builds the image, scans it with Trivy, and fails the build if any CRITICAL or HIGH vulnerabilities exist. Results are uploaded to GitHub's Security tab for tracking.

Clair — Static Analysis

Clair performs deep Static Analysis of container layers, identifying vulnerabilities by comparing installed package versions against CVE databases.

# Using clairctl to analyze an image
clairctl analyze myapp:latest

# Expected output:
# Analyzing myapp:latest...
# Found 7 vulnerabilities
#   - CVE-2024-XXXX (Critical) in libssl3
#   - CVE-2024-YYYY (High) in curl
#   - CVE-2024-ZZZZ (Medium) in libxml2

Expected output: Clair decompiles each image layer and cross-references every package against known CVEs, producing a prioritized list of vulnerabilities.

Docker Bench for Security

Docker Bench runs over 100 CIS benchmark checks against the Docker host configuration, identifying insecure defaults and misconfigurations.

# Run Docker Bench for Security
docker run --net host --pid host \
  --cap-add audit_control \
  -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  docker/docker-bench-security

# Expected output (excerpt):
# [PASS] 1.1.1 - Ensure the container host has been Hardened
# [WARN] 2.1   - Ensure network traffic is restricted between containers
# [NOTE] 4.1   - Ensure a user for the container has been created
# [FAIL] 5.4   - Ensure container restart policies are not set to 'always'

Expected behavior: Each check returns PASS, WARN, NOTE, or FAIL. Failing checks require immediate attention — they represent known attack vectors against Docker configurations.

Image Signing with Cosign

Image signing ensures that images pushed to a registry are authentic and haven't been tampered with since they were built.

# Generate a key pair
cosign generate-key-pair

# Expected output:
# Private key written to cosign.key
# Public key written to cosign.pub

# Sign an image
cosign sign --key cosign.key myregistry.io/myapp:latest

# Expected output:
# Pushing signature to: myregistry.io/myapp:sha256-xxxx.sig

# Verify an image
cosign verify --key cosign.pub myregistry.io/myapp:latest

# Expected output:
# Verification for myregistry.io/myapp:latest --
# The following checks were performed on each of these signatures:
#   - The cosign claims were validated
#   - The signatures were verified against the specified public key

Expected behavior: Signed images carry a cryptographic signature that verification checks against the public key. Deployments can be configured to reject unsigned images entirely, preventing tampered or unauthorized images from running.

SBOM Generation

A Software Bill of Materials (SBOM) lists every component in a container image — essential for Supply Chain Security and Compliance.

# Generate an SBOM in SPDX format
trivy image --format spdx-json --output sbom.spdx.json myapp:latest

# Expected output:
# myapp:latest (alpine 3.18)
# Total: 87 packages
# SPDX-2.3 JSON: sbom.spdx.json

# View the SBOM
cat sbom.spdx.json | jq '.packages[].name'

# Expected output:
# "alpine-baselayout"
# "alpine-keys"
# "apk-tools"
# "busybox"
# "libcrypto3"
# "libssl3"
# "nodejs"
# ...

Expected output: The SBOM contains every package, its version, and its license information. This document can be submitted to vulnerability databases, shared with customers for Compliance audits, or stored alongside the image for future reference.

Runtime Container Security

Read-Only Root Filesystem

Making the container filesystem read-only prevents attackers from modifying binaries or writing malicious scripts.

# docker-compose.yml
services:
  app:
    image: myapp:latest
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

Expected behavior: The container's root filesystem is read-only. If the application needs to write temporary files, specific directories are mounted as tmpfs with explicit permissions.

Drop Capabilities

Containers run with all Linux capabilities by default. Dropping unnecessary capabilities reduces the Blast Radius of a compromised container.

# Docker run with minimal capabilities
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp:latest

Expected behavior: The container has no Linux capabilities except the ability to bind to privileged ports. Even if an attacker gains code execution, they cannot perform privileged operations like loading kernel modules or changing system time.

Common Errors

  1. Scanning only the final image layer: Vulnerabilities in intermediate layers are still present in the final image. Always scan the complete assembled image, not just the last Dockerfile instruction.

  2. Ignoring base image vulnerabilities: Using ubuntu:latest or node:latest as a base pulls in all CVEs from that base. Pin to specific versions (node:20.11-slim) and scan before the first use.

  3. Not signing images: Without image signing, anyone with registry access can push a tampered image. A compromised registry can serve malicious images to all consuming deployments.

  4. Running containers as root: Containers default to running as root inside the container. An attacker who escapes the container gains root access on the host. Always use USER in Dockerfile.

  5. Skipping runtime security: Image scanning at build time catches known CVEs but misses zero-day exploits. Combine build-time scanning with runtime monitoring tools like Falco or Tracee.

  6. Overlooking the host OS: Container security is only as strong as the host. A vulnerable host kernel or misconfigured Docker daemon compromises every container. Run Docker Bench regularly.

Practice Questions

  1. What is the difference between Trivy and Docker Bench? Answer: Trivy scans container images for vulnerable packages. Docker Bench audits the Docker host configuration against CIS benchmarks — checking daemon settings, file permissions, and network configurations.

  2. Why is image signing important for Supply Chain Security? Answer: Image signing cryptographically proves an image was built and approved by a trusted source. Without signing, a malicious image with the same name can be substituted at any point between build and deployment.

  3. What information does an SBOM contain and why is it useful? Answer: An SBOM lists every package, version, and license in an image. It enables vulnerability tracking, license Compliance audits, and quick identification of which images are affected by a newly disclosed CVE.

  4. How does a read-only root filesystem improve container security? Answer: Even if an attacker gains code execution, they cannot modify binaries, write malware, or persist changes because the filesystem rejects all write operations.

Challenge

Set up a complete container security pipeline: use Trivy to scan a Node.js application image with exit-code enforcement, generate an SPDX SBOM, sign the image with Cosign, verify the signature before deployment, run Docker Bench on the host, and configure the container with a read-only filesystem and dropped capabilities.

Mini Project

Build a secure container pipeline for a web application. Write a Dockerfile that uses a distroless base image with a non-root user. Integrate Trivy scanning into GitHub Actions that fails the build on HIGH or CRITICAL CVEs. Generate and archive an SBOM for each build. Sign the image with Cosign. Finally, run Docker Bench against the build host and output the results as a pipeline artifact.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro