Security AWS

Clair on AWS User Guide

| Product: Clair on AWS

Overview

This image runs Clair, the open source project for the static analysis of vulnerabilities in application containers. Clair indexes the package contents of a container image's layers and reports the known CVEs that affect them, so you can gate images in CI, audit a registry, or answer what is vulnerable in what you are about to ship. The image is delivered as a ready-to-use appliance: postgresql.service and clair.service are enabled and start on boot, so the moment an engineer connects over SSH the container vulnerability scanning API is already answering.

Clair runs in combo mode, which runs the indexer, matcher and notifier in a single process backed by a locally installed PostgreSQL database on the same instance. The REST API is served on port 6060 and the bundled clairctl command line tool drives it. No container runtime or Docker daemon is required on the instance to scan an image, because clairctl pulls the image manifest itself.

This is a headless service. There is no web interface. The Clair REST API is unauthenticated by Clair's own default, and the shipped security group opens port 22 only, so the API is never exposed to the network until you deliberately open port 6060 behind your own access controls. The health and Prometheus metrics endpoint is bound to the loopback interface (127.0.0.1:8089). A strong, unique PostgreSQL password is generated automatically on the first boot of every instance and written to a root-only credentials file, so no shared secret is ever baked into the image.

The PostgreSQL data directory is placed on a dedicated, independently resizable EBS data volume mounted at /var/lib/postgresql, so the vulnerability database and indexed image data grow on durable storage you can expand without touching the boot volume.

Prerequisites

Before you deploy this image you need:

  • An Amazon Web Services account where you can launch EC2 instances
  • IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
  • An EC2 key pair in the target Region for SSH access to the instance
  • A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network
  • The AWS CLI (version 2) installed locally if you plan to deploy from the command line

Step 1: Launch the Instance from the AWS Marketplace

Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images choose AWS Marketplace AMIs and search for Clair. Select the cloudimg listing and choose Select, then Continue on the subscription summary.

Pick an instance type of m5.large or larger. Clair's matcher downloads and imports a large body of vulnerability data on first start, so allow at least 8 GB of memory. Choose your EC2 key pair under Key pair (login). Under Network settings select your VPC and subnet, and either create or select a security group that opens port 22 from your management network. Leave the root volume at the default size or larger.

Select Launch instance. First boot initialisation takes a short time after the instance state becomes Running and the status checks pass: the image generates the per-instance database password, rotates the PostgreSQL role, and starts Clair against it.

Step 2: Launch the Instance from the AWS CLI

The following block launches an instance from the cloudimg Clair Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens port 22 from your management network.

aws ec2 run-instances \
  --image-id <ami-id> \
  --instance-type m5.large \
  --key-name <key-name> \
  --subnet-id <subnet-id> \
  --security-group-ids <security-group-id> \
  --metadata-options "HttpTokens=required" \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=clair}]'

When the instance state is Running and both status checks pass, note its public or private IP address for the SSH step.

Step 3: Connect over SSH

Connect to the instance with your key pair. The login user depends on the operating system variant you launched.

Connecting to your instance

OS variant SSH login user
Ubuntu 24.04 ubuntu
ssh -i /path/to/<key-name>.pem ubuntu@<public-ip>

Step 4: Read the Per-Instance Credentials

On first boot the image generates a unique PostgreSQL password for the clair role and records it, together with the API address, in a root-only credentials file. Read it with sudo:

sudo cat /root/clair-credentials.txt
# Clair 4.9.0 - generated on first boot. Unique to this instance.
# Keep this file safe. The Clair REST API is UNAUTHENTICATED by default;
# port 6060 is NOT opened on the instance security group by default - open it
# only behind your own network controls.

CLAIR_API_URL=http://<instance-public-ip>:6060/
CLAIR_HEALTH_URL=http://127.0.0.1:8089/healthz
CLAIR_DB_NAME=clair
CLAIR_DB_USER=clair
CLAIR_DB_PASSWORD=<generated-per-instance>

The PostgreSQL password is used internally by Clair over the loopback interface. PostgreSQL is not exposed on the network.

Step 5: Confirm the Services Are Running

Confirm both services are active and that Clair is listening. The REST API binds 0.0.0.0:6060 and the health and metrics endpoint binds loopback 127.0.0.1:8089; PostgreSQL listens on loopback only.

systemctl is-active clair postgresql
sudo ss -tlnp | grep -E '6060|8089|5432'
active
active
LISTEN 0 4096  127.0.0.1:8089  0.0.0.0:*  users:(("clair",pid=1213,fd=3))
LISTEN 0 200   127.0.0.1:5432  0.0.0.0:*  users:(("postgres",pid=740,fd=6))
LISTEN 0 4096          *:6060         *:*  users:(("clair",pid=1213,fd=13))

The clair and postgresql services active and Clair listening on port 6060 for the REST API and loopback port 8089 for health and metrics, with the bundled clairctl reporting version v4.9.0

Step 6: Check Health and Readiness

The introspection server on loopback port 8089 serves liveness and readiness endpoints. Both return HTTP 200 when Clair is healthy and its database migrations have completed.

curl -s -o /dev/null -w 'healthz: %{http_code}\n' http://127.0.0.1:8089/healthz
curl -s -o /dev/null -w 'readyz:  %{http_code}\n' http://127.0.0.1:8089/readyz
curl -s -o /dev/null -w 'index_state: %{http_code}\n' http://127.0.0.1:6060/indexer/api/v1/index_state
healthz: 200
readyz:  200
index_state: 200

Step 7: Scan a Container Image

Use the bundled clairctl to scan an image. Point it at the local REST API with --host; clairctl pulls the image manifest, submits it to the indexer, waits for the vulnerability report from the matcher, and prints the result. No Docker daemon is needed. The first scan after launch is best run once the matcher has finished importing its vulnerability data.

clairctl report --host http://127.0.0.1:6060/ mirror.gcr.io/library/debian:11

The matcher matches every installed package against its continuously updated vulnerability data and returns the CVEs affecting the image. For JSON output suitable for CI, add --out json:

clairctl report --host http://127.0.0.1:6060/ --out json mirror.gcr.io/library/debian:11 \
  | jq '[.vulnerabilities | to_entries[]] | length'
96

A representative sample of the findings for that image, showing the CVE identifier, the affected package and the normalised severity:

CVE-2016-2781   pkg=coreutils  severity=Medium
CVE-2025-27587  pkg=openssl    severity=Low
CVE-2024-26458  pkg=krb5       severity=Low
CVE-2011-3374   pkg=apt        severity=Low
CVE-2023-50495  pkg=ncurses    severity=Unknown

clairctl scanning the debian 11 container image against the local Clair API and returning 96 real CVE findings, with a sample listing CVE identifiers mapped to the coreutils, openssl, krb5 and apt packages

Step 8: Expose the REST API Safely

The Clair REST API is unauthenticated by Clair's own default and is closed to the network by the shipped security group. To let registries, CI runners or clairctl on other hosts reach it, open port 6060 in the instance security group to your trusted CIDR only, and front it with your own access controls and TLS. Clair itself supports PSK/JWT authentication on the API; consult the upstream documentation to enable it. Never expose port 6060 to the public internet without authentication in front of it.

# Example: allow port 6060 from your CI subnet only
aws ec2 authorize-security-group-ingress \
  --group-id <security-group-id> \
  --protocol tcp --port 6060 --cidr <your-mgmt-cidr>

The Clair health endpoint on loopback port 8089 returning healthz and readyz 200 and the indexer index_state endpoint returning 200, confirming the combo-mode service is live and ready on the per-instance rotated PostgreSQL backend

Step 9: The Vulnerability Database and the Data Volume

Clair stores its indexer, matcher and notifier state in the local PostgreSQL database clair, whose data directory lives on the dedicated EBS data volume mounted at /var/lib/postgresql. The matcher's updaters refresh vulnerability data from distribution security trackers and public feeds in the background; the first full import runs after the initial boot and subsequent refreshes are incremental. You can grow the data volume with the standard EBS resize workflow without touching the boot volume.

findmnt /var/lib/postgresql
df -h /var/lib/postgresql

Support

This image is supported 24/7 by cloudimg. For help with the REST API and clairctl, indexing and matching, the vulnerability updaters, the PostgreSQL backend, integrating Clair with registries and CI, backups, capacity planning and upgrade planning, contact cloudimg support. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.