วันที่ 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
สิ่งที่เปลี่ยนไป:
| Aspect | Before Trivy | After Trivy |
|---|---|---|
| Scanning Coverage | 30% containers | 100% containers |
| Detection Speed | Days-weeks | Real-time |
| CI/CD Integration | Manual | Automated |
| Report Quality | Inconsistent | Standardized |
| False Positives | High (30%) | Low (5%) |
| Time to Fix | Weeks | Hours |
ผลลัพธ์ที่วัดได้:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Vulnerabilities in Production | 47 avg/image | 2 avg/image | 95% reduction |
| Security Scan Coverage | 30% | 100% | 233% increase |
| Time to Detect Issues | 2-4 weeks | Real-time | 99% faster |
| False Positive Rate | 30% | 5% | 83% reduction |
| Security Compliance Score | 45% | 95% | 111% improvement |
| MTTR for Security Issues | 2 weeks | 2 hours | 99% 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! 🛡️⚡