Clair
Clair is an open-source container vulnerability scanner that analyzes container image manifests for known security vulnerabilities.
Installation
Docker Deployment
# Pull official image
docker pull quay.io/coreos/clair:latest
# Run Clair with PostgreSQL
docker run -d \
--name clair \
-p 6060:6060 \
-e CLAIR_DATABASE_URL="postgresql://user:password@postgres:5432/clair" \
quay.io/coreos/clair:latest
# Health check
curl -s http://localhost:6060/api/v1/namespaces | jq '.'
Docker Compose
version: '3'
services:
postgres:
image: postgres:13
environment:
POSTGRES_DB: clair
POSTGRES_USER: clair
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
clair:
image: quay.io/coreos/clair:latest
ports:
- "6060:6060"
environment:
CLAIR_DATABASE_URL: "postgresql://clair:secret@postgres:5432/clair"
depends_on:
- postgres
volumes:
postgres_data:
Kubernetes
apiVersion: v1
kind: Service
metadata:
name: clair
spec:
selector:
app: clair
ports:
- port: 6060
targetPort: 6060
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: clair
spec:
replicas: 1
selector:
matchLabels:
app: clair
template:
metadata:
labels:
app: clair
spec:
containers:
- name: clair
image: quay.io/coreos/clair:latest
ports:
- containerPort: 6060
env:
- name: CLAIR_DATABASE_URL
value: "postgresql://user:pass@postgres:5432/clair"
API Operations
Core API Endpoints
# Health check
curl http://localhost:6060/api/v1/namespaces
# Get status
curl -s http://localhost:6060/health
# API version
curl -s http://localhost:6060/api/v1/vulnerabilities/os/debian
Analyze Image Manifest
# Create layer to analyze (Linux)
curl -X POST http://localhost:6060/api/v1/layers \
-H "Content-Type: application/json" \
-d '{
"Layer": {
"Name": "sha256:abc123...",
"ParentName": "",
"Format": "Docker",
"Path": "https://registry.example.com/v2/image/blobs/sha256:abc123..."
}
}' | jq '.'
# Get layer vulnerabilities
curl http://localhost:6060/api/v1/layers/sha256:abc123.../vulnerabilities \
| jq '.Vulnerabilities'
Vulnerability Database
# List supported namespaces (distros)
curl -s http://localhost:6060/api/v1/namespaces | jq '.Namespaces[]'
# Query specific namespace
curl -s http://localhost:6060/api/v1/namespaces/debian:9 \
| jq '.Namespace'
# Get vulnerability details
curl -s "http://localhost:6060/api/v1/vulnerabilities/debian:9/CVE-2021-1234" \
| jq '.Vulnerability'
Integration with Container Registries
Quay.io Integration
# Configure Quay to use Clair
# Quay settings:
# - FEATURE_SECURITY_SCANNER=true
# - SECURITY_SCANNER_ENDPOINT=http://clair:6060
# - SECURITY_SCANNER_V4_ENDPOINT=http://clair:6060
# Trigger scan
curl -X POST \
-H "Authorization: Bearer $QUAY_TOKEN" \
https://quay.io/api/v1/repository/org/image/scan
Docker Registry v2
# Scan image in local registry
MANIFEST=$(curl -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
http://registry:5000/v2/image/manifests/latest)
# Push manifest to Clair
curl -X POST http://localhost:6060/api/v1/layers \
-H "Content-Type: application/json" \
-d '{
"Layer": {
"Name": "'$SHA'",
"Format": "Docker",
"Path": "http://registry:5000/v2/image/blobs/'$SHA'"
}
}'
Vulnerability Analysis
Layer Analysis
# Analyze full image (multi-layer)
for layer in $(jq -r '.config.digest' image-manifest.json); do
curl -X POST http://localhost:6060/api/v1/layers \
-d '{
"Layer": {
"Name": "'$layer'",
"Format": "Docker",
"Path": "https://registry.example.com/v2/image/blobs/'$layer'"
}
}'
done
# Wait for analysis
sleep 10
# Get all vulnerabilities
curl -s "http://localhost:6060/api/v1/layers/$LAYER_ID/vulnerabilities?pageSize=100" \
| jq '.Vulnerabilities[] | {Name, Severity, Description}'
Filtering Results
# High/Critical vulnerabilities only
curl -s http://localhost:6060/api/v1/layers/$LAYER_ID/vulnerabilities \
| jq '.Vulnerabilities[] | select(.Severity | test("High|Critical"))'
# By package
curl -s http://localhost:6060/api/v1/layers/$LAYER_ID/vulnerabilities \
| jq '.Vulnerabilities[] | select(.FeatureName == "openssl")'
# CVE list
curl -s http://localhost:6060/api/v1/layers/$LAYER_ID/vulnerabilities \
| jq -r '.Vulnerabilities[].Name'
CVE Database Management
Update Vulnerability Data
# Force database refresh
curl -X POST http://localhost:6060/api/v1/database/update
# Check update status
curl -s http://localhost:6060/api/v1/database | jq '.'
# Database metrics
curl -s http://localhost:6060/metrics | grep clair
Supported Distributions
| Distribution | Namespace | Status |
|---|---|---|
| Alpine | alpine:v3.x | Supported |
| CentOS | centos:7,8 | Supported |
| Debian | debian:9,10,11 | Supported |
| Ubuntu | ubuntu:18.04,20.04 | Supported |
| RHEL | rhel:7,8 | Supported |
| Oracle | oracle:7,8 | Supported |
Security Scanning Workflow
Complete Scan Process
#!/bin/bash
# Scan Docker image end-to-end
IMAGE="myapp:latest"
REGISTRY="registry.example.com"
TOKEN="$API_TOKEN"
# 1. Get manifest
MANIFEST=$(curl -s -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer $TOKEN" \
https://$REGISTRY/v2/$IMAGE/manifests/latest)
# 2. Extract config blob
CONFIG_SHA=$(echo "$MANIFEST" | jq -r '.config.digest')
# 3. Analyze layers
echo "$MANIFEST" | jq -r '.layers[] | .digest' | while read layer; do
echo "Analyzing layer: $layer"
curl -s -X POST http://localhost:6060/api/v1/layers \
-H "Content-Type: application/json" \
-d '{
"Layer": {
"Name": "'$layer'",
"Format": "Docker",
"Path": "https://'$REGISTRY'/v2/'$IMAGE'/blobs/'$layer'"
}
}'
done
# 4. Wait for processing
sleep 15
# 5. Get vulnerabilities
curl -s http://localhost:6060/api/v1/layers/$CONFIG_SHA/vulnerabilities \
| jq '.Vulnerabilities' > vulnerabilities.json
Clairctl Command-Line Tool
Installation
# Build from source
git clone https://github.com/quay/clairctl.git
cd clairctl
go build -o clairctl ./cmd/clairctl
# Export to PATH
sudo mv clairctl /usr/local/bin/
Usage
# Report on image
clairctl report --image-ref myapp:latest
# Get vulnerabilities
clairctl report --image-ref docker.io/library/nginx:latest \
--output-format json > report.json
# Filter by severity
clairctl report --image-ref myapp:latest \
--minimum-severity "High"
Troubleshooting
Common Issues
# Database connection failure
# Check PostgreSQL logs
docker logs postgres
# Clair service logs
docker logs clair
# Verify connectivity
nc -zv localhost 6060
curl -v http://localhost:6060/api/v1/namespaces
# Test database
psql -h localhost -U clair -d clair -c "SELECT COUNT(*) FROM Vulnerability;"
Performance Tuning
# clair-config.yaml
database:
type: postgres
options:
source: "postgresql://user:pass@postgres:5432/clair"
max_open_conns: 25
max_idle_conns: 10
matcher:
max_conn_pool: 100
Best Practices
- Scan images on push to registry
- Monitor vulnerability database for updates
- Set severity threshold policy (block high/critical)
- Schedule periodic re-scans of archived images
- Integrate with image signing (cosign)
- Maintain false positive suppression list
- Store scan results for compliance audits
- Use distro-specific vulnerability data for accuracy
- Configure webhook notifications for new findings