Chapter 2. Clair concepts

The following sections provide a conceptual overview of how Clair works.

2.1. Clair in practice

A Clair analysis is broken down into three distinct parts: indexing, matching, and notification.

2.1.1. Indexing

Clair’s indexer service is responsible for indexing a manifest. In Clair, manifests are representations of a container image. The indexer service is the component that Clair uses to understand the contents of layers. Clair leverages the fact that Open Container Initiative (OCI) manifests and layers are content addressed to reduce duplicate work.

Indexing involves taking a manifest representing a container image and computing its constituent parts. The indexer tries to discover what packages exist in the image, what distribution the image is derived from, and what package repositories are used within the image. When this information is computed, it is persisted into an IndexReport.

The IndexReport is stored in Clair’s database. It can be fed to a matcher node to compute the vulnerability report.

2.1.1.1. Content addressability

Clair treats all manifests and layers as content addressable. In the context of Clair, content addressable means that when a specific manifest is indexed, it is not indexed again unless it is required; this is the same for individual layers.

For example, consider how many images in a registry might use ubuntu:artful as a base layer. If the developers prefer basing their images off of Ubuntu, it could be a large majority of images. Treating the layers and manifests as content addressable means that Clair only fetches and analyzes the base layer one time.

In some cases, Clair should re-index a manifest. For example, when an internal component such as a package scanner is updated, Clair performs the analysis with the new package scanner. Clair has enough information to determine that a component has changed and that the IndexReport might be different the second time, and as a result it re-indexes the manifest.

A client can track Clair’s index_state endpoint to understand when an internal component has changed, and can subsequently issue re-indexes. See the Clair API guide to learn how to view Clair’s API specification.

2.1.2. Matching

With Clair, a matcher node is responsible for matching vulnerabilities to a provided IndexReport.

Matchers are responsible for keeping the database of vulnerabilities up to date. Matchers will typically run a set of updaters, which periodically probe their data sources for new content. New vulnerabilities are stored in the database when they are discovered.

The matcher API is designed to be used often. It is designed to always provide the most recent VulnerabilityReport when queried. The VulnerabilityReport summarizes both a manifest’s content and any vulnerabilities affecting the content.

2.1.2.1. Remote matching

A remote matcher acts similar to a matcher, however remote matchers use API calls to fetch vulnerability data for a provided IndexReport. Remote matchers are useful when it is impossible to persist data from a given source into the database.

The CRDA remote matcher is responsible for fetching vulnerabilities from Red Hat Code Ready Dependency Analytics (CRDA). By default, this matcher serves 100 requests per minute. The rate limiting can be lifted by requesting a dedicated API key, which is done by submitting the API key request form.

To enable CRDA remote matching, see "Enabling CRDA for Clair".

2.1.3. Notifications

Clair uses a notifier service that keeps track of new security database updates and informs users if new or removed vulnerabilities affect an indexed manifest.

When the notifier becomes aware of new vulnerabilities affecting a previously indexed manifest, it uses the configured methods in your config.yaml file to issue notifications about the new changes. Returned notifications express the most severe vulnerability discovered because of the change. This avoids creating excessive notifications for the same security database update.

When a user receives a notification, it issues a new request against the matcher to receive an up to date vulnerability report.

The notification schema is the JSON marshalled form of the following types:

// Reason indicates the catalyst for a notification
type Reason string
const (
    Added   Reason = "added"
    Removed Reason = "removed"
    Changed Reason = "changed"
)
type Notification struct {
    ID            uuid.UUID        `json:"id"`
    Manifest      claircore.Digest `json:"manifest"`
    Reason        Reason           `json:"reason"`
    Vulnerability VulnSummary      `json:"vulnerability"`
}
type VulnSummary struct {
    Name           string                  `json:"name"`
    Description    string                  `json:"description"`
    Package        *claircore.Package      `json:"package,omitempty"`
    Distribution   *claircore.Distribution `json:"distribution,omitempty"`
    Repo           *claircore.Repository   `json:"repo,omitempty"`
    Severity       string                  `json:"severity"`
    FixedInVersion string                  `json:"fixed_in_version"`
    Links          string                  `json:"links"`
}

You can subscribe to notifications through the following mechanics:

  • Webhook delivery
  • AMQP delivery
  • STOMP delivery

Configuring the notifier is done through the Clair YAML configuration file.

2.1.3.1. Webhook delivery

When you configure the notifier for webhook delivery, you provide the service with the following pieces of information:

When the notifier has determined an updated security database has been changed the affected status of an indexed manifest, it delivers the following JSON body to the configured target:

{
  "notifiction_id": {uuid_string},
  "callback": {url_to_notifications}
}

On receipt, the server can browse to the URL provided in the callback field.

2.1.3.2. AMQP delivery

The Clair notifier also supports delivering notifications to an AMQP broker. With AMQP delivery, you can control whether a callback is delivered to the broker or whether notifications are directly delivered to the queue. This allows the developer of the AMQP consumer to determine the logic of notification processing.

Note

AMQP delivery only supports AMQP 0.x protocol (for example, RabbitMQ). If you need to publish notifications to AMQP 1.x message queue (for example, ActiveMQ), you can use STOMP delivery.

2.1.3.2.1. AMQP direct delivery

If the Clair notifier’s configuration specifies direct: true for AMQP delivery, notifications are delivered directly to the configured exchange.

When direct is set, the rollup property might be set to instruct the notifier to send a maximum number of notifications in a single AMQP. This provides balance between the size of the message and the number of messages delivered to the queue.

2.1.3.3. Notifier testing and development mode

The notifier has a testing and development mode that can be enabled with the NOTIFIER_TEST_MODE parameter. This parameter can be set to any value.

When the NOTIFIER_TEST_MODE parameter is set, the notifier begins sending fake notifications to the configured delivery mechanism every poll_interval interval. This provides an easy way to implement and test new or existing deliverers.

The notifier runs in NOTIFIER_TEST_MODE until the environment variable is cleared and the service is restarted.

2.1.3.4. Deleting notifications

To delete the notification, you can use the DELETE API call. Deleting a notification ID manually cleans up resources in the notifier. If you do not use the DELETE API call, the notifier waits a predetermined length of time before clearing delivered notifications from its database.

2.2. Clair authentication

In its current iteration, Clair v4 (Clair) handles authentication internally.

Note

Previous versions of Clair used JWT Proxy to gate authentication.

Authentication is configured by specifying configuration objects underneath the auth key of the configuration. Multiple authentication configurations might be present, but they are used preferentially in the following order:

  1. PSK. With this authentication configuration, Clair implements JWT-based authentication using a pre-shared key.
  2. Configuration. For example:

    auth:
      psk:
        key: >-
          MDQ4ODBlNDAtNDc0ZC00MWUxLThhMzAtOTk0MzEwMGQwYTMxCg==
        iss: 'issuer'

    In this configuration the auth field requires two parameters: iss, which is the issuer to validate all incoming requests, and key, which is a base64 coded symmetric key for validating the requests.

2.3. Clair updaters

Clair uses Go packages called updaters that contain the logic of fetching and parsing different vulnerability databases.

Updaters are usually paired with a matcher to interpret if, and how, any vulnerability is related to a package. Administrators might want to update the vulnerability database less frequently, or not import vulnerabilities from databases that they know will not be used.

2.3.1. Configuring updaters

Updaters can be configured by the updaters key at the top of the configuration. If updaters are being run automatically within the matcher process, which is the default setting, the period for running updaters is configured under the matcher’s configuration field.

2.3.1.1. Updater sets

The following sets can be configured with Clair updaters:

  • alpine
  • aws
  • debian
  • enricher/cvss
  • libvuln/driver
  • oracle
  • photon
  • pyupio
  • rhel
  • rhel/rhcc
  • suse
  • ubuntu
  • updater

2.3.1.2. Selecting updater sets

Specific sets of updaters can be selected by the sets list. For example:

updaters:
  sets:
    - rhel

If the sets field is not populated, it defaults to using all sets.

2.3.1.3. Filtering updater sets

To reject an updater from running without disabling an entire set, the filter option can be used.

In the following example, the string is interpreted as a Go regexp package. This rejects any updater with a name that does not match.

Note

This means that an empty string matches any string. It does not mean that it matches no strings.

updaters:
  filter: '^$'

2.3.1.4. Configuring specific updaters

Configuration for specific updaters can be passed by putting a key underneath the config parameter of the updaters object. The name of an updater might be constructed dynamically, and users should examine logs to ensure updater names are accurate. The specific object that an updater expects should be covered in the updater’s documentation.

In the following example, the rhel updater fetches a manifest from a different location:

updaters:
  config:
    rhel:
      url: https://example.com/mirror/oval/PULP_MANIFEST

2.3.1.5. Disabling the Clair Updater component

In some scenarios, users might want to disable the Clair updater component. Disabling updaters is required when running Red Hat Quay in a disconnected environment.

In the following example, Clair updaters are disabled:

matcher:
  disable_updaters: true

2.3.2. Clair updater URLs

The following are the HTTP hosts and paths that Clair will attempt to talk to in a default configuration. This list is non-exhaustive. Some servers issue redirects and some request URLs are constructed dynamically.

  • https://secdb.alpinelinux.org/
  • http://repo.us-west-2.amazonaws.com/2018.03/updates/x86_64/mirror.list
  • https://cdn.amazonlinux.com/2/core/latest/x86_64/mirror.list
  • https://www.debian.org/security/oval/
  • https://linux.oracle.com/security/oval/
  • https://packages.vmware.com/photon/photon_oval_definitions/
  • https://github.com/pyupio/safety-db/archive/
  • https://catalog.redhat.com/api/containers/
  • https://www.redhat.com/security/data/
  • https://support.novell.com/security/oval/
  • https://people.canonical.com/~ubuntu-security/oval/