Skip to content

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

DistributionNamespaceStatus
Alpinealpine:v3.xSupported
CentOScentos:7,8Supported
Debiandebian:9,10,11Supported
Ubuntuubuntu:18.04,20.04Supported
RHELrhel:7,8Supported
Oracleoracle:7,8Supported

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