Configure Podman image scanning with Trivy security vulnerability detection

Intermediate 45 min Apr 22, 2026 557 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up automated container image security scanning using Trivy with Podman to detect vulnerabilities, misconfigurations, and security issues before deploying containers to production.

Prerequisites

  • Root or sudo access
  • Internet connectivity for downloading vulnerability databases
  • At least 2GB free disk space
  • Basic knowledge of containers and JSON

What this solves

Container images often contain vulnerabilities in base operating systems, libraries, and dependencies that attackers can exploit. Trivy security scanner integrates with Podman to automatically detect CVEs, secrets, misconfigurations, and license issues in your container images before deployment. This tutorial shows you how to set up automated scanning workflows that catch security problems early in your development pipeline.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you have the latest security updates.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install Podman

Install Podman container engine if not already present on your system.

sudo apt install -y podman
sudo dnf install -y podman

Install Trivy security scanner

Download and install the latest version of Trivy from the official repository.

sudo apt update
sudo apt install -y wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt update
sudo apt install -y trivy
sudo tee /etc/yum.repos.d/trivy.repo << EOF
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/\$basearch/
gpgcheck=1
enabled=1
gpgkey=https://aquasecurity.github.io/trivy-repo/rpm/public.key
EOF
sudo dnf update -y
sudo dnf install -y trivy

Configure Trivy database location

Set up Trivy database directory with proper permissions for vulnerability database updates.

sudo mkdir -p /var/lib/trivy
sudo chown $USER:$USER /var/lib/trivy
export TRIVY_CACHE_DIR=/var/lib/trivy

Update Trivy vulnerability database

Download the latest vulnerability database which contains CVE information for scanning.

trivy image --download-db-only

Configure Podman with Trivy integration

Create scanning configuration file

Set up Trivy configuration file with security scanning policies and output formats.

cache:
  dir: /var/lib/trivy
db:
  skip-update: false
  download-timeout: 10m
vulnerability:
  type:
    - os
    - library
  scanners:
    - vuln
    - secret
    - config
severity:
  - UNKNOWN
  - LOW
  - MEDIUM
  - HIGH
  - CRITICAL
format: table
output: /var/log/trivy-scan.log
ignore-unfixed: false
timeout: 5m

Create log directory with proper permissions

Set up logging directory where Trivy will store scan results and reports.

sudo mkdir -p /var/log/trivy
sudo chown $USER:$USER /var/log/trivy
sudo chmod 755 /var/log/trivy

Create scanning wrapper script

Build an automation script that integrates Trivy scanning with Podman image operations.

#!/bin/bash
# Podman + Trivy security scanning wrapper

set -e

# Configuration
TRIVY_CONFIG="/etc/trivy/trivy.yaml"
LOG_DIR="/var/log/trivy"
DATE=$(date +%Y%m%d_%H%M%S)

# Function to scan image
scan_image() {
    local image="$1"
    local output_file="${LOG_DIR}/scan_${DATE}_$(echo "$image" | tr '/:' '_').json"
    
    echo "Scanning image: $image"
    echo "Output file: $output_file"
    
    # Run Trivy scan
    trivy image \
        --config "$TRIVY_CONFIG" \
        --format json \
        --output "$output_file" \
        --severity HIGH,CRITICAL \
        "$image"
    
    # Check for critical vulnerabilities
    critical_count=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL") | .VulnerabilityID' "$output_file" 2>/dev/null | wc -l)
    high_count=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH") | .VulnerabilityID' "$output_file" 2>/dev/null | wc -l)
    
    echo "Critical vulnerabilities: $critical_count"
    echo "High vulnerabilities: $high_count"
    
    # Generate summary report
    trivy image \
        --config "$TRIVY_CONFIG" \
        --format table \
        --severity HIGH,CRITICAL \
        "$image"
    
    # Return exit code based on findings
    if [ "$critical_count" -gt 0 ]; then
        echo "ERROR: Critical vulnerabilities found in $image"
        return 1
    elif [ "$high_count" -gt 5 ]; then
        echo "WARNING: More than 5 high-severity vulnerabilities found in $image"
        return 2
    fi
    
    echo "Image $image passed security scan"
    return 0
}

# Function to pull and scan
pull_and_scan() {
    local image="$1"
    
    echo "Pulling image: $image"
    podman pull "$image"
    
    echo "Scanning pulled image..."
    scan_image "$image"
}

# Main execution
case "$1" in
    scan)
        if [ -z "$2" ]; then
            echo "Usage: $0 scan 

Make scanning script executable

Set proper permissions on the scanning wrapper script.

sudo chmod 755 /usr/local/bin/podman-scan
sudo chown root:root /usr/local/bin/podman-scan

Install jq for JSON parsing

Install jq utility for parsing Trivy JSON output in the scanning script.

sudo apt install -y jq
sudo dnf install -y jq

Implement automated vulnerability scanning workflows

Create systemd timer for database updates

Set up automatic vulnerability database updates to ensure current CVE data.

[Unit]
Description=Update Trivy vulnerability database
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/trivy image --download-db-only
Environment=TRIVY_CACHE_DIR=/var/lib/trivy

[Install]
WantedBy=multi-user.target

Create systemd timer configuration

Schedule daily vulnerability database updates during low-traffic hours.

[Unit]
Description=Update Trivy database daily
Requires=trivy-db-update.service

[Timer]
OnCalendar=daily
RandomizedDelaySec=3600
Persistent=true

[Install]
WantedBy=timers.target

Enable and start the update timer

Activate the systemd timer for automatic vulnerability database updates.

sudo systemctl daemon-reload
sudo systemctl enable trivy-db-update.timer
sudo systemctl start trivy-db-update.timer
sudo systemctl status trivy-db-update.timer

Create CI/CD integration script

Build a script for integrating security scanning into continuous integration pipelines.

#!/bin/bash
# CI/CD security scanning integration

set -e

# Configuration
FAIL_ON_CRITICAL=${FAIL_ON_CRITICAL:-true}
FAIL_ON_HIGH=${FAIL_ON_HIGH:-false}
MAX_HIGH_VULNS=${MAX_HIGH_VULNS:-10}
SCAN_TIMEOUT=${SCAN_TIMEOUT:-300}
OUTPUT_FORMAT=${OUTPUT_FORMAT:-json}

# Function to scan and evaluate
ci_scan() {
    local image="$1"
    local build_id="${2:-$(date +%s)}"
    local output_file="/tmp/trivy-ci-${build_id}.json"
    
    echo "=== Security Scan Report ==="
    echo "Image: $image"
    echo "Build ID: $build_id"
    echo "Timestamp: $(date)"
    echo "=============================="
    
    # Run security scan
    timeout "$SCAN_TIMEOUT" trivy image \
        --format json \
        --output "$output_file" \
        --severity HIGH,CRITICAL \
        --no-progress \
        "$image" || {
        echo "ERROR: Scan failed or timed out"
        exit 1
    }
    
    # Parse results
    if [ -f "$output_file" ]; then
        critical_vulns=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL") | .VulnerabilityID' "$output_file" 2>/dev/null | wc -l)
        high_vulns=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH") | .VulnerabilityID' "$output_file" 2>/dev/null | wc -l)
        
        echo "Critical vulnerabilities: $critical_vulns"
        echo "High vulnerabilities: $high_vulns"
        
        # Display summary table
        trivy image \
            --format table \
            --severity HIGH,CRITICAL \
            --no-progress \
            "$image"
        
        # Apply CI/CD policies
        exit_code=0
        
        if [ "$FAIL_ON_CRITICAL" = "true" ] && [ "$critical_vulns" -gt 0 ]; then
            echo "❌ FAIL: Critical vulnerabilities found (Policy: FAIL_ON_CRITICAL=true)"
            exit_code=1
        fi
        
        if [ "$FAIL_ON_HIGH" = "true" ] && [ "$high_vulns" -gt "$MAX_HIGH_VULNS" ]; then
            echo "❌ FAIL: Too many high-severity vulnerabilities ($high_vulns > $MAX_HIGH_VULNS)"
            exit_code=1
        fi
        
        if [ $exit_code -eq 0 ]; then
            echo "✅ PASS: Security scan completed successfully"
        fi
        
        # Copy results to permanent location if specified
        if [ -n "$SCAN_RESULTS_DIR" ]; then
            mkdir -p "$SCAN_RESULTS_DIR"
            cp "$output_file" "$SCAN_RESULTS_DIR/scan-${build_id}.json"
        fi
        
        # Cleanup
        rm -f "$output_file"
        
        return $exit_code
    else
        echo "ERROR: Scan output file not found"
        return 1
    fi
}

# Main execution
if [ $# -lt 1 ]; then
    echo "Usage: $0 

Make CI script executable

Set proper permissions on the CI/CD integration script.

sudo chmod 755 /usr/local/bin/ci-security-scan
sudo chown root:root /usr/local/bin/ci-security-scan

Set up security policies and reporting

Create security policy configuration

Define organizational security policies for container image scanning and compliance.

# Container Security Policy Configuration
policy:
  # Vulnerability severity thresholds
  severity_thresholds:
    critical: 0          # Block deployment if any critical vulns
    high: 5             # Block if more than 5 high vulns
    medium: 20          # Warn if more than 20 medium vulns
    low: 50             # Info if more than 50 low vulns
  
  # Scanner types to enable
  scanners:
    - vuln              # CVE vulnerabilities
    - secret            # Exposed secrets/keys
    - config            # Configuration issues
  
  # Base image policies
  base_images:
    approved:
      - "docker.io/library/ubuntu:22.04"
      - "docker.io/library/alpine:3.18"
      - "registry.redhat.io/ubi8/ubi:latest"
    blocked:
      - "docker.io/library/ubuntu:16.04"  # EOL
      - "docker.io/library/centos:7"       # EOL
  
  # File scanning policies
  file_patterns:
    secrets:
      - "**/.env"
      - "**/config/*.key"
      - "**/secrets/**"
    configs:
      - "**/nginx.conf"
      - "**/apache2.conf"
      - "**/ssh_config"
  
  # Compliance requirements
  compliance:
    require_signed_images: true
    block_unpatched_critical: true
    max_image_age_days: 90
    require_minimal_base: false

Create vulnerability report generator

Build a comprehensive reporting script for security scan results and trends.

#!/bin/bash
# Security vulnerability reporting tool

set -e

# Configuration
REPORT_DIR="/var/log/trivy/reports"
DATE=$(date +%Y-%m-%d)
REPORT_FILE="${REPORT_DIR}/security-report-${DATE}.html"
JSON_FILE="${REPORT_DIR}/security-report-${DATE}.json"

# Create report directory
mkdir -p "$REPORT_DIR"

# Function to scan multiple images
scan_images() {
    local images=("$@")
    local results="[]"
    
    for image in "${images[@]}"; do
        echo "Scanning $image..."
        
        # Scan image
        scan_file="/tmp/scan-$(echo "$image" | tr '/:' '_').json"
        trivy image --format json --output "$scan_file" "$image" || {
            echo "Failed to scan $image"
            continue
        }
        
        # Extract summary
        if [ -f "$scan_file" ]; then
            critical=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL") | .VulnerabilityID' "$scan_file" 2>/dev/null | wc -l)
            high=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH") | .VulnerabilityID' "$scan_file" 2>/dev/null | wc -l)
            medium=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "MEDIUM") | .VulnerabilityID' "$scan_file" 2>/dev/null | wc -l)
            low=$(jq -r '.Results[]?.Vulnerabilities[]? | select(.Severity == "LOW") | .VulnerabilityID' "$scan_file" 2>/dev/null | wc -l)
            
            # Build JSON result
            result=$(jq -n \
                --arg image "$image" \
                --arg critical "$critical" \
                --arg high "$high" \
                --arg medium "$medium" \
                --arg low "$low" \
                --arg timestamp "$(date -Iseconds)" \
                '{
                    image: $image,
                    timestamp: $timestamp,
                    vulnerabilities: {
                        critical: ($critical | tonumber),
                        high: ($high | tonumber),
                        medium: ($medium | tonumber),
                        low: ($low | tonumber)
                    }
                }')
            
            results=$(echo "$results" | jq --argjson new "$result" '. + [$new]')
            
            rm -f "$scan_file"
        fi
    done
    
    echo "$results"
}

# Function to generate HTML report
generate_html_report() {
    local json_data="$1"
    
    cat > "$REPORT_FILE" << EOF



    
    


    <h1>Container Security Report</h1>
    <p>Generated: $(date)</p>
    
    <div class="summary">
        <h2>Summary</h2>
EOF
    
    # Calculate totals
    total_images=$(echo "$json_data" | jq '. | length')
    total_critical=$(echo "$json_data" | jq '[.[].vulnerabilities.critical] | add')
    total_high=$(echo "$json_data" | jq '[.[].vulnerabilities.high] | add')
    total_medium=$(echo "$json_data" | jq '[.[].vulnerabilities.medium] | add')
    total_low=$(echo "$json_data" | jq '[.[].vulnerabilities.low] | add')
    
    cat >> "$REPORT_FILE" << EOF
        <p>Total Images Scanned: $total_images</p>
        <p>Total Critical Vulnerabilities: $total_critical</p>
        <p>Total High Vulnerabilities: $total_high</p>
        <p>Total Medium Vulnerabilities: $total_medium</p>
        <p>Total Low Vulnerabilities: $total_low</p>
    </div>
    
    <h2>Detailed Results</h2>
    
EOF
    
    # Add table rows
    echo "$json_data" | jq -r '.[] | ""' >> "$REPORT_FILE"
    
    cat >> "$REPORT_FILE" << EOF
    <table>
        <tbody><tr>
            <th>Image</th>
            <th>Critical</th>
            <th>High</th>
            <th>Medium</th>
            <th>Low</th>
            <th>Scan Time</th>
        </tr><tr><td>\(.image)</td><td>\(.vulnerabilities.critical)</td><td>\(.vulnerabilities.high)</td><td>\(.vulnerabilities.medium)</td><td>\(.vulnerabilities.low)</td><td>\(.timestamp)</td></tr></tbody></table>


EOF
}

# Main execution
if [ $# -eq 0 ]; then
    echo "Usage: $0 

Make report generator executable

Set proper permissions on the security report generation script.

Automated install script

Run this to automate the entire setup

Nie chcesz zarządzać tym samodzielnie?

Zarządzamy infrastrukturą firm, które zależą od dostępności. W pełni zarządzana, z jednym stałym kontaktem, który zna Twoje środowisko.

Macie jednego stałego opiekuna, który zna Waszą konfigurację

Rotterdam 13:02 · dostępny w wiadomości, bez formularza zgłoszeń