Implementing Image Scanning in CI/CD Pipelines
Implementing Image Scanning in CI/CD Pipelines
Container image scanning must occur at multiple pipeline stages to ensure comprehensive security. Build-time scanning catches vulnerabilities before images are published, while registry scanning monitors stored images for newly discovered vulnerabilities. Pre-deployment scanning provides final validation before containers run in production. Each stage requires different tools and approaches optimized for specific requirements.
# GitLab CI container security pipeline
stages:
- build
- scan
- publish
- deploy
variables:
IMAGE_NAME: myapp
REGISTRY: registry.example.com
SEVERITY_THRESHOLD: "HIGH"
build-image:
stage: build
script:
# Build with security best practices
- |
cat > .dockerignore << EOF
.git
.env
*.key
*.pem
node_modules
__pycache__
EOF
# Multi-stage build for smaller attack surface
- docker build
--target production
--build-arg BUILDKIT_INLINE_CACHE=1
--tag $IMAGE_NAME:$CI_COMMIT_SHA .
# Save image for subsequent stages
- docker save $IMAGE_NAME:$CI_COMMIT_SHA > image.tar
artifacts:
paths:
- image.tar
expire_in: 1 hour
# Trivy scan for comprehensive vulnerability detection
scan-trivy:
stage: scan
script:
- docker load < image.tar
# Run Trivy scan with detailed output
- |
trivy image
--severity $SEVERITY_THRESHOLD
--exit-code 1
--no-progress
--format json
--output trivy-report.json
$IMAGE_NAME:$CI_COMMIT_SHA
# Generate human-readable report
- |
trivy image
--severity $SEVERITY_THRESHOLD
--no-progress
--format template
--template "@contrib/html.tpl"
--output trivy-report.html
$IMAGE_NAME:$CI_COMMIT_SHA
# Parse results for metrics
- python3 scripts/parse_trivy_results.py trivy-report.json
artifacts:
reports:
container_scanning: trivy-report.json
paths:
- trivy-report.html
expire_in: 30 days
allow_failure: false
# Anchore Grype scan for software composition analysis
scan-grype:
stage: scan
script:
- docker load < image.tar
# Scan with Grype
- |
grype $IMAGE_NAME:$CI_COMMIT_SHA
--output json
--file grype-report.json
--fail-on $SEVERITY_THRESHOLD
# Check for specific vulnerable packages
- python3 scripts/check_vulnerable_packages.py grype-report.json
artifacts:
paths:
- grype-report.json
# Docker Scout for supply chain analysis
scan-scout:
stage: scan
script:
- docker load < image.tar
# Analyze with Docker Scout
- |
docker scout cves $IMAGE_NAME:$CI_COMMIT_SHA
--format json
--output scout-report.json
# Check base image recommendations
- docker scout recommendations $IMAGE_NAME:$CI_COMMIT_SHA
artifacts:
paths:
- scout-report.json
# Policy validation with OPA
validate-policies:
stage: scan
script:
- docker load < image.tar
# Export image configuration
- docker inspect $IMAGE_NAME:$CI_COMMIT_SHA > image-config.json
# Validate against security policies
- |
opa eval -d policies/ -i image-config.json
"data.docker.image.deny[_]" > policy-violations.json
# Fail if policy violations exist
- |
if [ -s policy-violations.json ]; then
echo "Policy violations found:"
cat policy-violations.json
exit 1
fi
Base image selection significantly impacts container security. Official images from verified publishers generally receive better maintenance and security updates. Minimal base images like Alpine Linux or distroless images reduce attack surface by including only essential components. However, minimal images may complicate debugging and require careful dependency management.