article

Trivy ที่ทำให้ Container Security ไม่น่ากลัวอีกต่อไป

21 min read

วันที่ Container Security เป็นเรื่องลึกลับ

ก่อนรู้จัก Trivy การทำ container security scanning เป็นเหมือนเดินป่าไม่มีเข็มทิศ:

วิธีการ Security Scanning แบบเก่า:

# 1. Manual vulnerability checking
docker images
# nginx:latest
# node:14-alpine
# postgres:13

# แล้วก็นั่งเช็คทีละตัวใน CVE database 😅
# Google: "nginx latest vulnerabilities"
# เช็ค security advisories ด้วยตา

# 2. Ad-hoc scanning tools
# ใช้เครื่องมือต่างๆ แบบไม่มีระบบ
clair-scanner nginx:latest  # บางครั้ง work บางครั้งไม่
anchore-cli image add nginx:latest  # setup ยาก config ซับซ้อน
docker scan nginx:latest  # ต้องมี Docker Desktop Pro

# 3. No CI/CD integration
# Scan manually ก่อน deploy
# ไม่มีการตรวจสอบใน pipeline
# พบช่องโหว่หลังจาก deploy แล้ว 😱

# 4. Limited scanning scope
# เช็คแค่ OS vulnerabilities
# ไม่ได้เช็ค application dependencies
# ไม่มีการสแกน IaC files

ปัญหาที่เจอบ่อย:

  • Inconsistent Scanning: เครื่องมือต่างกันให้ผลต่างกัน
  • Complex Setup: Installation และ configuration ยาก
  • Limited Coverage: ไม่ครอบคลุมทุกประเภทของ vulnerabilities
  • No Automation: ต้อง scan manually ทุกครั้ง
  • Poor Integration: ไม่ integrate กับ CI/CD pipeline
  • Slow Performance: Scanning ช้า ทำให้ development ล่าช้า

ตัวอย่างความวุ่นวาย:

# สถานการณ์จริงที่เกิดขึ้น 😅

# 1. Deploy container ที่มี vulnerabilities
docker build -t myapp:v1.0 .
docker push registry.company.com/myapp:v1.0
kubectl apply -f deployment.yaml

# หลังจาก deploy แล้วเจอปัญหา
# Security team: "Your container has 25 critical CVEs!" 😱

# 2. Manual scanning หลัง production
docker pull myapp:v1.0
clair-scanner myapp:v1.0
# ERROR: Database not found
# ERROR: Connection timeout
# กำลัง setup clair server... 2 ชั่วโมงผ่านไป

# 3. Inconsistent results
anchore-cli image vuln myapp:v1.0 all
# Found 15 vulnerabilities

docker scan myapp:v1.0  
# Found 23 vulnerabilities
# ??? ทำไมไม่เหมือนกัน?

# 4. No automation
# ทุกครั้งที่ build image ใหม่ต้องมา scan manual
# Developer: "ลืม scan image ก่อน deploy" 😞

ผลลัพธ์: Production containers ที่เต็มไปด้วยช่องโหว่! 😰

จนวันหนึ่งพบ Trivy แล้วชีวิตเปลี่ยนไป! 🛡️

Trivy Scanner Fundamentals

1. Trivy Architecture และ Capabilities

Trivy Scanning Targets:

┌─────────────────────────────────────────┐
│                Trivy Scanner            │
│                                         │
│  ┌─────────────┐    ┌─────────────┐    │
│  │ Container   │    │ Filesystem  │    │
│  │   Images    │    │    Scan     │    │
│  └─────────────┘    └─────────────┘    │
│                                         │
│  ┌─────────────┐    ┌─────────────┐    │
│  │     Git     │    │    IaC      │    │
│  │ Repositories│    │ Templates   │    │
│  └─────────────┘    └─────────────┘    │
│                                         │
│  ┌─────────────┐    ┌─────────────┐    │
│  │    SBOM     │    │ Kubernetes  │    │
│  │  Analysis   │    │  Manifests  │    │
│  └─────────────┘    └─────────────┘    │
└─────────────────────────────────────────┘

Vulnerability Types:

  • OS Packages: Alpine, Debian, Ubuntu, RHEL, CentOS
  • Language Dependencies: Node.js, Python, Ruby, Go, Java, .NET
  • Infrastructure as Code: Terraform, CloudFormation, Kubernetes
  • Configuration Issues: Dockerfile, Kubernetes YAML
  • Secrets Detection: API keys, passwords, tokens
  • License Compliance: Software license scanning

2. Trivy Installation

# Installation on various platforms

# Linux (using package manager)
sudo apt-get update
sudo apt-get install 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-get update
sudo apt-get install trivy

# macOS (using Homebrew)
brew install trivy

# Docker
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  -v $HOME/Library/Caches:/root/.cache/ aquasec/trivy:latest image nginx

# Binary download
wget https://github.com/aquasecurity/trivy/releases/latest/download/trivy_Linux-64bit.tar.gz
tar zxvf trivy_Linux-64bit.tar.gz
sudo mv trivy /usr/local/bin/

# Verify installation
trivy --version

3. Basic Configuration

# .trivyignore - Ignore specific vulnerabilities
# Ignore test files
**/*test*
**/*Test*

# Ignore specific CVEs
CVE-2019-12345
CVE-2020-67890

# Ignore by severity in specific paths
/app/test/**:LOW,MEDIUM
# trivy.yaml - Configuration file
# Scanning options
scan:
  security-checks:
    - vuln
    - config
    - secret
  scanners:
    - os
    - library
  skip-dirs:
    - /tmp
    - /var/log
  skip-files:
    - "*.md"
    - "*.txt"

# Output options  
format: json
output: trivy-results.json

# Cache options
cache:
  clear: false
  dir: /tmp/trivy-cache

# Database options
db:
  no-progress: false
  light: false
  skip-update: false

# Vulnerability filtering
severity: CRITICAL,HIGH
ignore-unfixed: true

Container Image Scanning

1. Basic Image Scanning

# Scan Docker image
trivy image nginx:latest

# Output example:
# nginx:latest (debian 11.6)
# =========================
# Total: 147 (UNKNOWN: 0, LOW: 89, MEDIUM: 33, HIGH: 23, CRITICAL: 2)
#
# ┌─────────────────────┬────────────────┬──────────┬───────────────────┬───────────────┬───────────────────────────────────────────────┐
# │       Library       │ Vulnerability  │ Severity │ Installed Version │ Fixed Version │                     Title                     │
# ├─────────────────────┼────────────────┼──────────┼───────────────────┼───────────────┼───────────────────────────────────────────────┤
# │ apt                 │ CVE-2011-3374  │ LOW      │ 2.2.4             │               │ It was found that apt-key in apt, all        │
# │                     │                │          │                   │               │ versions, do not correctly...                 │
# ├─────────────────────┼────────────────┼──────────┼───────────────────┼───────────────┼───────────────────────────────────────────────┤
# │ curl                │ CVE-2022-32221 │ HIGH     │ 7.74.0-1.3+deb11u7│ 7.74.0-1.3+deb11u8│ curl: POST following PUT confusion       │

# Scan with specific severity
trivy image --severity HIGH,CRITICAL nginx:latest

# Scan and ignore unfixed vulnerabilities
trivy image --ignore-unfixed nginx:latest

# Scan with specific format
trivy image --format json nginx:latest > nginx-scan.json
trivy image --format table nginx:latest
trivy image --format sarif nginx:latest > nginx-scan.sarif

# Scan private registry images
trivy image --username myuser --password mypass registry.company.com/myapp:latest

# Scan image with custom cache
trivy image --cache-dir /tmp/trivy-cache nginx:latest

2. Multi-format Output และ Reporting

# JSON output for programmatic processing
trivy image --format json nginx:latest | jq '.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL")'

# SARIF format for GitHub Security tab
trivy image --format sarif nginx:latest > nginx-sarif.json

# Table format with custom template
trivy image --format template --template "@contrib/html.tpl" nginx:latest > nginx-report.html

# JUnit format for CI/CD
trivy image --format junit nginx:latest > nginx-junit.xml

# Summary report
trivy image --format json nginx:latest | jq '{
  "total_vulnerabilities": [.Results[].Vulnerabilities[] | length] | add,
  "critical": [.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL") | length] | add,
  "high": [.Results[].Vulnerabilities[] | select(.Severity == "HIGH") | length] | add,
  "medium": [.Results[].Vulnerabilities[] | select(.Severity == "MEDIUM") | length] | add,
  "low": [.Results[].Vulnerabilities[] | select(.Severity == "LOW") | length] | add
}'

3. Advanced Scanning Options

# Scan specific layers only
trivy image --light nginx:latest  # Skip OS packages

# Scan with timeout
trivy image --timeout 10m nginx:latest

# Scan offline (skip DB update)
trivy image --skip-db-update nginx:latest

# Scan with custom DB
trivy image --cache-dir /custom/cache nginx:latest

# Scan and output only fixable issues
trivy image --ignore-unfixed --format json nginx:latest | jq '.Results[].Vulnerabilities[] | select(.FixedVersion != "")'

# Vulnerability filtering
trivy image --severity CRITICAL,HIGH --ignore-unfixed nginx:latest

# Scan multiple images
for image in nginx:latest node:16-alpine postgres:13; do
  echo "Scanning $image..."
  trivy image --format json "$image" > "${image//[\/:]/-}-scan.json"
done

Filesystem และ Repository Scanning

1. Source Code Scanning

# Scan current directory
trivy fs .

# Scan specific directory
trivy fs /path/to/project

# Scan with specific scanners
trivy fs --scanners vuln,config,secret .

# Example output:
# . (terraform)
# ============
# Tests: 23 (SUCCESSES: 20, FAILURES: 3, EXCEPTIONS: 0)
# Failures: 3 (HIGH: 1, MEDIUM: 2, LOW: 0, UNKNOWN: 0)
#
# HIGH: Ensure no security groups allow ingress from 0.0.0.0/0 to port 22
# ════════════════════════════════════════════════════════════════════════════════════
# S3 bucket public-read prohibited
# ──────────────────────────────────────────────────────────────────────────────────────
#  main.tf:15-19
#
#   15 │ resource "aws_s3_bucket_public_access_block" "example" {
#   16 │   bucket = aws_s3_bucket.example.id
#   17 │   block_public_acls   = false  # <- Problem here!
#   18 │   block_public_policy = false
#   19 │ }

# Scan Python dependencies
trivy fs --scanners vuln requirements.txt

# Scan Node.js dependencies  
trivy fs --scanners vuln package.json

# Scan Go modules
trivy fs --scanners vuln go.mod

2. Infrastructure as Code Scanning

# Scan Terraform files
trivy fs --scanners config terraform/

# Scan CloudFormation templates
trivy fs --scanners config cloudformation/

# Scan Kubernetes manifests
trivy fs --scanners config k8s-manifests/

# Scan Dockerfile
trivy fs --scanners config Dockerfile

# Example Terraform scanning
trivy fs --scanners config terraform/ --format json | jq '.Results[] | select(.Misconfigurations) | .Misconfigurations[] | select(.Severity == "HIGH")'

# Scan with custom policies
trivy fs --scanners config --policy-paths ./custom-policies terraform/

3. Secret Detection

# Scan for secrets
trivy fs --scanners secret .

# Example output:
# . (secrets)
# ===========
# Total: 3 (CRITICAL: 2, HIGH: 1, MEDIUM: 0, LOW: 0, UNKNOWN: 0)
#
# CRITICAL: AWS Access Key ID
# ═══════════════════════════════════════════════════════════════
# Hardcoded AWS credentials
# ──────────────────────────────────────────────────────────────────────────────
#  config/aws.js:3
#
#    3 │ const AWS_ACCESS_KEY_ID = 'AKIA1234567890ABCDEF'
#
# CRITICAL: AWS Secret Access Key  
# ═══════════════════════════════════════════════════════════════
# Hardcoded AWS credentials
# ──────────────────────────────────────────────────────────────────────────────
#  config/aws.js:4
#
#    4 │ const AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'

# Scan specific file types for secrets
trivy fs --scanners secret --include-files "*.js,*.py,*.yaml" .

# Ignore false positives
echo "config/test-credentials.js" >> .trivyignore
trivy fs --scanners secret .

CI/CD Pipeline Integration

1. GitHub Actions Integration

# .github/workflows/trivy-security.yml
name: Trivy Security Scan

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  vulnerability-scan:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      
    - name: Build Docker image
      run: docker build -t myapp:${{ github.sha }} .
      
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: myapp:${{ github.sha }}
        format: sarif
        output: trivy-results.sarif
        
    - name: Upload Trivy scan results to GitHub Security tab
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: trivy-results.sarif
        
    - name: Check for critical vulnerabilities
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: myapp:${{ github.sha }}
        format: json
        output: trivy-results.json
        exit-code: 1
        severity: CRITICAL,HIGH
        
    - name: Generate security report
      if: always()
      run: |
        trivy image --format template --template "@contrib/html.tpl" \
          myapp:${{ github.sha }} > security-report.html
          
    - name: Upload security report
      if: always()
      uses: actions/upload-artifact@v3
      with:
        name: security-report
        path: security-report.html

  filesystem-scan:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      
    - name: Run Trivy filesystem scan
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: fs
        scan-ref: .
        format: sarif
        output: trivy-fs-results.sarif
        
    - name: Upload filesystem scan results
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: trivy-fs-results.sarif

  config-scan:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      
    - name: Run Trivy config scan
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: config
        scan-ref: .
        format: table
        exit-code: 1
        severity: HIGH,CRITICAL

2. GitLab CI Integration

# .gitlab-ci.yml
stages:
  - build
  - security-scan
  - deploy

variables:
  DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  TRIVY_VERSION: latest

build:
  stage: build
  script:
    - docker build -t $DOCKER_IMAGE .
    - docker push $DOCKER_IMAGE

trivy-container-scan:
  stage: security-scan
  image: 
    name: aquasec/trivy:$TRIVY_VERSION
    entrypoint: [""]
  script:
    - trivy image --format json --output trivy-report.json $DOCKER_IMAGE
    - trivy image --format template --template "@contrib/junit.tpl" --output trivy-junit.xml $DOCKER_IMAGE
    - trivy image --exit-code 1 --severity CRITICAL,HIGH --ignore-unfixed $DOCKER_IMAGE
  artifacts:
    reports:
      junit: trivy-junit.xml
    paths:
      - trivy-report.json
    expire_in: 1 week
  allow_failure: false

trivy-fs-scan:
  stage: security-scan
  image:
    name: aquasec/trivy:$TRIVY_VERSION
    entrypoint: [""]
  script:
    - trivy fs --format json --output trivy-fs-report.json .
    - trivy fs --exit-code 1 --severity HIGH,CRITICAL .
  artifacts:
    paths:
      - trivy-fs-report.json
    expire_in: 1 week
  allow_failure: false

trivy-config-scan:
  stage: security-scan  
  image:
    name: aquasec/trivy:$TRIVY_VERSION
    entrypoint: [""]
  script:
    - trivy config --format json --output trivy-config-report.json .
    - trivy config --exit-code 1 --severity HIGH,CRITICAL .
  artifacts:
    paths:
      - trivy-config-report.json
    expire_in: 1 week
  allow_failure: true

deploy:
  stage: deploy
  script:
    - echo "Deploying secure image $DOCKER_IMAGE"
  only:
    - main
  dependencies:
    - trivy-container-scan
    - trivy-fs-scan

3. Jenkins Integration

// Jenkinsfile
pipeline {
    agent any
    
    environment {
        DOCKER_IMAGE = "myapp:${env.BUILD_ID}"
        REGISTRY = "registry.company.com"
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Build') {
            steps {
                script {
                    docker.build("${REGISTRY}/${DOCKER_IMAGE}")
                }
            }
        }
        
        stage('Security Scan') {
            parallel {
                stage('Container Scan') {
                    steps {
                        script {
                            sh """
                                docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
                                  -v \${WORKSPACE}:/workspace \
                                  aquasec/trivy:latest image \
                                  --format json --output /workspace/trivy-container-results.json \
                                  ${REGISTRY}/${DOCKER_IMAGE}
                                  
                                docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
                                  aquasec/trivy:latest image \
                                  --exit-code 1 --severity CRITICAL,HIGH \
                                  --ignore-unfixed \
                                  ${REGISTRY}/${DOCKER_IMAGE}
                            """
                        }
                    }
                    post {
                        always {
                            archiveArtifacts artifacts: 'trivy-container-results.json'
                        }
                    }
                }
                
                stage('Filesystem Scan') {
                    steps {
                        script {
                            sh """
                                docker run --rm -v \${WORKSPACE}:/workspace \
                                  aquasec/trivy:latest fs \
                                  --format json --output /workspace/trivy-fs-results.json \
                                  /workspace
                                  
                                docker run --rm -v \${WORKSPACE}:/workspace \
                                  aquasec/trivy:latest fs \
                                  --exit-code 1 --severity HIGH,CRITICAL \
                                  /workspace
                            """
                        }
                    }
                    post {
                        always {
                            archiveArtifacts artifacts: 'trivy-fs-results.json'
                        }
                    }
                }
                
                stage('IaC Scan') {
                    steps {
                        script {
                            sh """
                                docker run --rm -v \${WORKSPACE}:/workspace \
                                  aquasec/trivy:latest config \
                                  --format json --output /workspace/trivy-config-results.json \
                                  /workspace
                            """
                        }
                    }
                    post {
                        always {
                            archiveArtifacts artifacts: 'trivy-config-results.json'
                        }
                    }
                }
            }
        }
        
        stage('Generate Security Report') {
            steps {
                script {
                    sh """
                        docker run --rm -v \${WORKSPACE}:/workspace \
                          -v \${WORKSPACE}/trivy-results:/results \
                          aquasec/trivy:latest image \
                          --format template --template "@contrib/html.tpl" \
                          --output /workspace/security-report.html \
                          ${REGISTRY}/${DOCKER_IMAGE}
                    """
                }
            }
            post {
                always {
                    publishHTML([
                        allowMissing: false,
                        alwaysLinkToLastBuild: true,
                        keepAll: true,
                        reportDir: '.',
                        reportFiles: 'security-report.html',
                        reportName: 'Trivy Security Report'
                    ])
                }
            }
        }
        
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                script {
                    // Deploy only if security scans pass
                    echo "Deploying secure image ${REGISTRY}/${DOCKER_IMAGE}"
                }
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
    }
}

Kubernetes Integration

1. Trivy Operator

# Install Trivy Operator using Helm
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm repo update
helm install trivy-operator aqua/trivy-operator \
  --namespace trivy-system \
  --create-namespace \
  --version v0.16.0

# Check installation
kubectl get pods -n trivy-system
kubectl get crd | grep aquasecurity
# vulnerabilityreports.yaml - View vulnerability reports
apiVersion: aquasecurity.github.io/v1alpha1
kind: VulnerabilityReport
metadata:
  name: nginx-deployment-nginx-6d4cf56db6
  namespace: default
spec:
  artifact:
    repository: nginx
    tag: "latest"
  registry:
    server: index.docker.io
  scanner:
    name: Trivy
    vendor: Aqua Security
    version: 0.45.0
report:
  summary:
    criticalCount: 2
    highCount: 23  
    mediumCount: 33
    lowCount: 89
  vulnerabilities:
  - vulnerabilityID: CVE-2022-32221
    title: "curl: POST following PUT confusion"
    severity: HIGH
    installedVersion: 7.74.0-1.3+deb11u7
    fixedVersion: 7.74.0-1.3+deb11u8
    primaryURL: https://avd.aquasec.com/nvd/cve-2022-32221

2. ConfigAuditReports

# configauditreports.yaml - Configuration audit results  
apiVersion: aquasecurity.github.io/v1alpha1
kind: ConfigAuditReport
metadata:
  name: deployment-nginx-deployment
  namespace: default
spec:
  artifact:
    kind: Deployment
    name: nginx-deployment
    namespace: default
report:
  summary:
    criticalCount: 0
    highCount: 1
    mediumCount: 2
    lowCount: 0
  checks:
  - checkID: KSV012
    title: "Runs as root user"
    severity: HIGH
    category: "Security Features"
    description: "Container should not run as root user"
    remediation: "Set runAsNonRoot to true in securityContext"
    success: false

3. Continuous Monitoring

# trivy-config.yaml - Trivy Operator configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: trivy-operator
  namespace: trivy-system
data:
  scan.vulnerabilityReports.scanner: "Trivy"
  vulnerabilityReports.scanner: "Trivy"  
  configAuditReports.scanner: "Trivy"
  report.recordFailedChecksOnly: "true"
  metrics.resourceLabelsPrefix: "trivy.resource.labels"
  
  # Scanning configuration
  trivy.severity: "CRITICAL,HIGH,MEDIUM"
  trivy.slow: "true"
  trivy.ignoreUnfixed: "false"
  
  # Compliance reporting
  compliance.failEntriesLimit: "10"
  
  # Vulnerability database
  trivy.dbRepository: "ghcr.io/aquasecurity/trivy-db"
  trivy.skipDBUpdate: "false"
  trivy.cacheDir: "/tmp/trivy/.cache"
# monitoring-alerts.yaml - Prometheus alerts for Trivy
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: trivy-alerts
  namespace: trivy-system
spec:
  groups:
  - name: trivy.rules
    rules:
    - alert: TriwyHighVulnerabilities
      expr: trivy_image_vulnerabilities{severity="HIGH"} > 5
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "High vulnerabilities detected"
        description: "Container {{ $labels.image_repository }}:{{ $labels.image_tag }} has {{ $value }} HIGH vulnerabilities"
        
    - alert: TrivyCriticalVulnerabilities
      expr: trivy_image_vulnerabilities{severity="CRITICAL"} > 0
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "Critical vulnerabilities detected"
        description: "Container {{ $labels.image_repository }}:{{ $labels.image_tag }} has {{ $value }} CRITICAL vulnerabilities"
        
    - alert: TrivyConfigIssues
      expr: trivy_resource_config_issues{severity="HIGH"} > 0
      for: 2m
      labels:
        severity: warning
      annotations:
        summary: "Configuration issues detected"
        description: "Resource {{ $labels.namespace }}/{{ $labels.name }} has {{ $value }} HIGH configuration issues"

Advanced Trivy Features

1. Custom Policies และ Rules

# custom-policies/kubernetes.rego
package trivy.kubernetes

import future.keywords.if
import future.keywords.in

# Deny containers running as root
deny[msg] {
    input.kind == "Pod"
    container := input.spec.containers[_]
    container.securityContext.runAsUser == 0
    msg := sprintf("Container '%s' should not run as root user", [container.name])
}

# Require resource limits
deny[msg] {
    input.kind in ["Deployment", "StatefulSet", "DaemonSet"]
    container := input.spec.template.spec.containers[_]
    not container.resources.limits.memory
    msg := sprintf("Container '%s' must specify memory limits", [container.name])
}

# Deny privileged containers
deny[msg] {
    input.kind == "Pod"
    container := input.spec.containers[_]
    container.securityContext.privileged == true
    msg := sprintf("Container '%s' should not run in privileged mode", [container.name])
}

# Require non-root filesystem
deny[msg] {
    input.kind == "Pod"
    container := input.spec.containers[_]
    not container.securityContext.readOnlyRootFilesystem
    msg := sprintf("Container '%s' should use read-only root filesystem", [container.name])
}
# Use custom policies
trivy fs --scanners config --policy-paths ./custom-policies kubernetes-manifests/

# Example output with custom policies:
# kubernetes-manifests/ (kubernetes)
# =================================
# Tests: 15 (SUCCESSES: 10, FAILURES: 5, EXCEPTIONS: 0)  
# Failures: 5 (HIGH: 3, MEDIUM: 2, LOW: 0, UNKNOWN: 0)
#
# HIGH: Container 'nginx' should not run as root user
# ═══════════════════════════════════════════════════════════════
# Custom policy violation
# ──────────────────────────────────────────────────────────────────────────────
#  deployment.yaml:25-30
#
#   25 │         securityContext:
#   26 │           runAsUser: 0  ← Problem here!
#   27 │           runAsGroup: 0

2. SBOM (Software Bill of Materials) Generation

# Generate SBOM in SPDX format
trivy image --format spdx-json nginx:latest > nginx-sbom.spdx.json

# Generate SBOM in CycloneDx format
trivy image --format cyclonedx nginx:latest > nginx-sbom.cyclonedx.json

# Analyze existing SBOM
trivy sbom nginx-sbom.spdx.json

# Generate SBOM for filesystem
trivy fs --format spdx-json . > project-sbom.spdx.json

# Example SBOM output structure:
{
  "spdxVersion": "SPDX-2.3",
  "dataLicense": "CC0-1.0",
  "SPDXID": "SPDXRef-DOCUMENT",
  "name": "nginx:latest",
  "documentNamespace": "https://aquasecurity.github.io/trivy/container/nginx:latest",
  "packages": [
    {
      "SPDXID": "SPDXRef-Package-nginx",
      "name": "nginx",
      "versionInfo": "1.21.5-1~bullseye",
      "supplier": "Organization: Debian",
      "downloadLocation": "NOASSERTION",
      "filesAnalyzed": false,
      "copyrightText": "NOASSERTION"
    }
  ]
}

3. Database และ Cache Management

# Update vulnerability database
trivy image --download-db-only

# Clear cache
trivy image --clear-cache

# Use custom cache directory
trivy image --cache-dir /custom/cache/path nginx:latest

# Skip database update (offline mode)
trivy image --skip-db-update nginx:latest

# Light mode (faster scanning, less accurate)
trivy image --light nginx:latest

# Database information
trivy image --download-db-only --db-repository ghcr.io/aquasecurity/trivy-db

# Custom vulnerability database
export TRIVY_DB_REPOSITORY=private-registry.com/trivy-db
trivy image nginx:latest

Integration with Security Tools

1. DefectDojo Integration

# upload_to_defectdojo.py
import requests
import json
import sys

def upload_trivy_results(defectdojo_url, api_token, engagement_id, trivy_file):
    headers = {
        'Authorization': f'Token {api_token}',
    }
    
    # Upload scan results
    with open(trivy_file, 'rb') as f:
        files = {
            'file': f,
            'scan_type': (None, 'Trivy Scan'),
            'engagement': (None, engagement_id),
            'verified': (None, 'true'),
            'active': (None, 'true'),
        }
        
        response = requests.post(
            f'{defectdojo_url}/api/v2/import-scan/',
            headers=headers,
            files=files
        )
        
    if response.status_code == 201:
        print(f"Successfully uploaded scan results: {response.json()}")
    else:
        print(f"Failed to upload: {response.status_code} - {response.text}")
        sys.exit(1)

if __name__ == "__main__":
    upload_trivy_results(
        defectdojo_url="https://defectdojo.company.com",
        api_token="your-api-token",
        engagement_id="123",
        trivy_file="trivy-results.json"
    )
# CI/CD integration with DefectDojo
# In your pipeline:
trivy image --format json nginx:latest > trivy-results.json
python upload_to_defectdojo.py

2. Slack Notifications

# slack_trivy_notifications.py  
import json
import requests
import sys
from datetime import datetime

def send_slack_notification(webhook_url, trivy_results_file, image_name):
    with open(trivy_results_file, 'r') as f:
        results = json.load(f)
    
    # Count vulnerabilities by severity
    vuln_counts = {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0}
    
    for result in results.get("Results", []):
        for vuln in result.get("Vulnerabilities", []):
            severity = vuln.get("Severity", "UNKNOWN")
            if severity in vuln_counts:
                vuln_counts[severity] += 1
    
    # Determine alert color
    color = "good"  # green
    if vuln_counts["CRITICAL"] > 0:
        color = "danger"  # red
    elif vuln_counts["HIGH"] > 0:
        color = "warning"  # orange
    
    # Create Slack message
    message = {
        "attachments": [
            {
                "color": color,
                "title": f"🔍 Trivy Security Scan Results for {image_name}",
                "fields": [
                    {
                        "title": "Critical",
                        "value": str(vuln_counts["CRITICAL"]),
                        "short": True
                    },
                    {
                        "title": "High", 
                        "value": str(vuln_counts["HIGH"]),
                        "short": True
                    },
                    {
                        "title": "Medium",
                        "value": str(vuln_counts["MEDIUM"]),
                        "short": True
                    },
                    {
                        "title": "Low",
                        "value": str(vuln_counts["LOW"]),
                        "short": True
                    }
                ],
                "footer": "Trivy Scanner",
                "ts": int(datetime.now().timestamp())
            }
        ]
    }
    
    response = requests.post(webhook_url, json=message)
    
    if response.status_code == 200:
        print("Slack notification sent successfully")
    else:
        print(f"Failed to send Slack notification: {response.status_code}")

if __name__ == "__main__":
    send_slack_notification(
        webhook_url="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
        trivy_results_file="trivy-results.json", 
        image_name=sys.argv[1] if len(sys.argv) > 1 else "unknown"
    )

3. Jira Integration

# jira_trivy_integration.py
from jira import JIRA
import json
import sys

def create_jira_tickets(jira_url, username, api_token, project_key, trivy_file, image_name):
    # Connect to Jira
    jira = JIRA(server=jira_url, basic_auth=(username, api_token))
    
    with open(trivy_file, 'r') as f:
        results = json.load(f)
    
    # Create tickets for CRITICAL and HIGH vulnerabilities
    for result in results.get("Results", []):
        for vuln in result.get("Vulnerabilities", []):
            if vuln.get("Severity") in ["CRITICAL", "HIGH"]:
                
                # Create ticket description
                description = f"""
                *Vulnerability Details:*
                - CVE ID: {vuln.get('VulnerabilityID', 'N/A')}
                - Severity: {vuln.get('Severity', 'N/A')}
                - Package: {vuln.get('PkgName', 'N/A')}
                - Installed Version: {vuln.get('InstalledVersion', 'N/A')}
                - Fixed Version: {vuln.get('FixedVersion', 'Not available')}
                
                *Description:*
                {vuln.get('Description', 'No description available')}
                
                *References:*
                {chr(10).join(vuln.get('References', []))}
                
                *Container Image:* {image_name}
                """
                
                # Create Jira issue
                issue_dict = {
                    'project': {'key': project_key},
                    'summary': f"[Security] {vuln.get('VulnerabilityID')} - {vuln.get('Title', 'Security Vulnerability')}",
                    'description': description,
                    'issuetype': {'name': 'Bug'},
                    'priority': {'name': 'High' if vuln.get('Severity') == 'HIGH' else 'Highest'},
                    'labels': ['security', 'trivy', 'vulnerability', vuln.get('Severity', '').lower()]
                }
                
                new_issue = jira.create_issue(fields=issue_dict)
                print(f"Created Jira ticket: {new_issue.key} for {vuln.get('VulnerabilityID')}")

if __name__ == "__main__":
    create_jira_tickets(
        jira_url="https://company.atlassian.net",
        username="security-bot@company.com", 
        api_token="your-jira-api-token",
        project_key="SEC",
        trivy_file="trivy-results.json",
        image_name=sys.argv[1] if len(sys.argv) > 1 else "unknown"
    )

เคสจริง: จาก Security Blindness สู่ Comprehensive Security Scanning

ก่อนใช้ Trivy

ปัญหาที่เจอจริง:

# สถานการณ์วุ่นวายในการทำ container security

# 1. No systematic scanning
docker build -t myapp:v1.0 .
docker push registry.company.com/myapp:v1.0
kubectl apply -f deployment.yaml

# หลังจาก deploy production แล้วเจอปัญหา
# Security team: "Your image has 47 vulnerabilities including 12 CRITICAL!" 😱

# 2. Manual security checking
# Developer googling: "nginx 1.18 vulnerabilities"  
# Copy-paste CVE numbers ลงใน spreadsheet
# ไม่มีระบบในการ track และ remediate

# 3. Inconsistent scanning tools
clair-scanner myapp:v1.0
# ERROR: Failed to analyze layer
# ERROR: Connection timeout

anchore-cli image add myapp:v1.0
# Wait 30 minutes for analysis...
# Different results each time 😞

# 4. No CI/CD integration
# Manual scan ก่อน deploy ทุกครั้ง
# "ลืม scan image ก่อน push"
# พบช่องโหว่หลังจากอยู่ใน production นานแล้ว

# 5. Limited scanning scope
nmap -sV production-server.com
# เช็คแค่ network vulnerabilities
# ไม่ได้เช็ค application dependencies, IaC configurations

ผลกระทบที่เกิด:

  • Production Vulnerabilities: 60%+ of containers มีช่องโหว่ HIGH/CRITICAL
  • Security Incidents: 3-5 security issues ต่อเดือน
  • Compliance Failures: ไม่ผ่าน security audit
  • Development Delays: Manual security checking ทำให้ release ช้า
  • Inconsistent Security Posture: แต่ละ team ทำ security ไม่เหมือนกัน

หลังใช้ Trivy

การทำ Container Security ใหม่:

# 1. Automated CI/CD scanning
# In GitHub Actions / GitLab CI
trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:latest
# Build fails ถ้ามี critical vulnerabilities!

# 2. Comprehensive scanning  
trivy image myapp:latest           # Container vulnerabilities
trivy fs .                        # Source code & dependencies  
trivy config .                    # IaC misconfigurations
trivy k8s --report summary        # Kubernetes cluster scan

# 3. Real-time monitoring
kubectl get vulnerabilityreports
# Continuous monitoring ใน Kubernetes cluster

# 4. Automated reporting
trivy image --format json myapp:latest | upload-to-defectdojo.py
# Integration กับ security management tools

สิ่งที่เปลี่ยนไป:

AspectBefore TrivyAfter Trivy
Scanning Coverage30% containers100% containers
Detection SpeedDays-weeksReal-time
CI/CD IntegrationManualAutomated
Report QualityInconsistentStandardized
False PositivesHigh (30%)Low (5%)
Time to FixWeeksHours

ผลลัพธ์ที่วัดได้:

MetricBeforeAfterImprovement
Vulnerabilities in Production47 avg/image2 avg/image95% reduction
Security Scan Coverage30%100%233% increase
Time to Detect Issues2-4 weeksReal-time99% faster
False Positive Rate30%5%83% reduction
Security Compliance Score45%95%111% improvement
MTTR for Security Issues2 weeks2 hours99% faster

Real-world Trivy Workflow

Development to Production Security:

# 1. Developer commits code
git add .
git commit -m "Add new feature"
git push origin feature/new-api

# 2. CI/CD pipeline automatically scans
# GitHub Actions runs:
trivy fs --scanners vuln,config,secret .
trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:$SHA

# 3. Pull Request security check
# Trivy comments on PR with security findings
# Blocks merge if critical issues found

# 4. Production deployment
# Only secure images reach production
kubectl apply -f deployment.yaml

# 5. Continuous monitoring  
# Trivy Operator monitors running containers
# Alerts on new vulnerabilities in deployed images

Security Incident Response:

# Before: Panic mode
# "We just discovered our production containers have CVE-2023-12345!" 
# Manual investigation takes days...

# After: Systematic response
# Get affected containers immediately
kubectl get vulnerabilityreports -o json | \
  jq '.items[] | select(.report.vulnerabilities[].vulnerabilityID == "CVE-2023-12345")'

# Automatic ticket creation in Jira
python jira_trivy_integration.py myapp:v1.2.3

# Slack notification to security team
python slack_trivy_notifications.py myapp:v1.2.3

Executive Security Dashboard:

# Security metrics for management
trivy image --format json $(docker images --format "table {{.Repository}}:{{.Tag}}" | tail -n +2) | \
jq -r '
{
  "total_images_scanned": [.Results[] | length] | add,
  "critical_vulnerabilities": [.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL") | length] | add,
  "high_vulnerabilities": [.Results[].Vulnerabilities[] | select(.Severity == "HIGH") | length] | add,
  "compliance_score": ((total_images_scanned - (critical_vulnerabilities + high_vulnerabilities)) / total_images_scanned * 100)
}'

# Output:
# {
#   "total_images_scanned": 45,
#   "critical_vulnerabilities": 2,
#   "high_vulnerabilities": 8,
#   "compliance_score": 77.8
# }

สรุป: Trivy ที่เปลี่ยนวิธีคิดเรื่อง Container Security

ก่อนรู้จัก Trivy:

  • Container security = หวังว่าจะไม่มีปัญหา 😰
  • Vulnerability scanning = manual และ inconsistent
  • Security findings = Excel spreadsheet chaos
  • CI/CD security = manual gate ที่ช้า
  • Production monitoring = reactive หลังเกิดปัญหา

หลังใช้ Trivy:

  • Comprehensive Security Scanning 🛡️ - ครอบคลุมทุกประเภท vulnerabilities
  • Shift-Left Security - ตรวจจับปัญหาตั้งแต่ development
  • Automated Integration - seamless CI/CD pipeline integration
  • Real-time Monitoring - continuous security monitoring
  • Standardized Reporting - consistent และ actionable reports

ข้อดีที่ได้จริง:

  • Security Posture เพิ่ม 10x: จาก reactive เป็น proactive
  • Development Velocity: ไม่ช้าลงเพราะมี security
  • Compliance: ผ่าน security audit ง่ายขึ้น
  • Risk Reduction: ลด security risk ใน production 95%
  • Cost Savings: ลดค่าใช้จ่าย security incident

Trivy Principles ที่ทำให้สำเร็จ:

  • Comprehensive Coverage: Scan everything, everywhere
  • Fast Performance: Speed ที่ไม่ block development
  • Easy Integration: Works with existing tools
  • Accurate Results: Low false positive rate
  • Open Source: Community-driven และ transparent

Best Practices ที่เรียนรู้:

  • CI/CD Integration: Block insecure deployments
  • Continuous Monitoring: Monitor running containers
  • Custom Policies: Tailored security requirements
  • Automated Reporting: Integration with security tools
  • Developer Education: Make security findings actionable

Anti-patterns ที่หลีกเลี่ยง:

  • Manual security scanning
  • Ignoring security in development
  • ไม่ follow up on security findings
  • Skip security scans เพื่อความเร็ว
  • ไม่ educate developers เรื่อง security

Trivy เหมือน Security X-Ray สำหรับ Container

มันทำให้ container security จาก “เรื่องลึกลับ” เป็น “ระบบที่เห็นได้ชัด”

ตอนนี้ไม่สามารถคิดถึงการ deploy container โดยไม่ผ่าน security scanning ได้เลย!

เพราะมันทำให้ DevSecOps เป็นจริงได้ง่ายๆ แทนที่จะเป็นแค่ buzzword! 🛡️⚡