This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Integration

Integrations Available for Anchore Enterprise

Integration handling in Enterprise

Anchore Enterprise exposes an API that lets the software entities (agents, plugins, and other components) integrating external systems with Enterprise be tracked and monitored. Today this API is used by the Kubernetes Inventory agent; other integration instances will adopt it over time. Existing agents and plugins continue to function whether or not they participate in this API — participating instances simply gain the ability to be tracked and monitored.

The API has two parts: integration registration and integration health reporting. Both are described below.

Terminology

Integration instance: A software entity, like an agent or plugin, that integrates and external system with Anchore Enterprise. A deployed Kubernetes Inventory agent, Kubernetes Admission Controller, and ECS Inventory agent are all examples of integration instances.

Integration status: The (life-cycle) status of the integration instance as perceived by Enterprise. After registration is completed, this status is determined based on if health reports are received or not.

Reported status: The status of the integration instance as perceived by the integration instance itself. This is determined from the contents of the health reports, if they contain any errors or not.

Integration registration

When an integration instance that supports integration registration and health reporting is started it will perform registration with Anchore Enterprise. This is a kind of handshake where the integration instance introduces itself, declaring which type it is and presenting various other information about itself. In response, Anchore Enterprise provides the integration instance with the uuid that identifies the integration instance from that point onwards.

The registration request includes two identifiers: registration_id and registration_instance_id. Anchore Enterprise maintains a record of the association between integration uuid and <registration_id, registration_instance_id>.

If an integration instance is restarted, it will perform registration again. Assuming the <registration_id, registration_instance_id> pair in that re-registration remains the same as in the original registration, Enterprise will consider the integration instance to be the same (and thus provide the integration instance with the same uuid). Should the <registration_id, registration_instance_id> pair be different, then Enterprise will consider the integration instance to be different and assign it a new uuid.

Integrations deployed as multiple replicas

An integration can be deployed as multiple replicas. An example is the Kubernetes Inventory agent, which helm chart deploys it as a K8s Deployment. That deployment can be specified to have replicas > 1 (although it is not advisable as the agent is strictly speaking not implemented to be executed as multiple replicas, it will work but only add unnecessary load).

In such a case, each replica will have identical configuration. They will register as integration instances and be given their own uuid. By inspecting the registration_id and registration_instance_id it is often possible to determine if they instances are part of the same replica set. They will then have registered with identical registration_id but different registration_instance_id. The exception is if each integration instance self-generated a unique registration_id that they used during registration. In that case they cannot be identified to belong to the same replica set this way.

Integration health reporting

Once registered, an integration instance can send periodic health reports to Anchore Enterprise Enterprise. The interval between two health reports can be configured to be 30 to 600 seconds. A default values will typically be 60 seconds.

Each health report includes a uuid that identifies it and timestamp when it was sent. These can be used when searching the integration instance’s log file during troubleshooting. The health report also includes the uptime of the integration instance as well as an ’errors’ property that contains errors that the integration wants to make Anchore Enterprise aware of. In addition to the above, health reports can also include data specific to the type of integration.

Reported status derived from health reports

When Anchore Enterprise receives a health report that contains errors from an integration instance, it will set that instance’s reportedStatus.state to unhealthy and the reportedStatus.healthReportUuid is set to the uuid of the health report. If subsequent health reports do no contain errors, the instance’s reportedStatus.state is set to healthy and the reportedStatus.healthReportUuid is unset.

This is an example of what the reported status can look like from an integration instance that sends health reports indicating errors:

{
    "reportedStatus": {
      "details": {
        "errors": [
          "unable to report Inventory to Anchore account account0: failed to report data to Anchore: \u0026{Status:4",
          "user account not found (account1) | ",
          "unable to report Inventory to Anchore account account2: failed to report data to Anchore: \u0026{Status:4",
          "user account not found (account3) | "
        ],
        "healthReportUuid": "d676f221-0cc7-485e-b909-a5a1dd8d244e"
      },
      "reason": "Health report included errors",
      "state": "unhealthy"
    }
}

The details.errors list indicates that there is some issues related to ‘account0’, ‘account1’, ‘account2’ and ‘account3’. To fully triage and troubleshoot these issues one will typically have to search the log file for the integration instance.

This is an example of reported status for case without errors:

{
    "reportedStatus": {
      "state": "healthy"
    }
}

The below figure illustrates how the reportedStatus.state property will transition between its states.

Reported Status states

Integration status derived from health reports

When an integration instance registers with Anchore Enterprise, it will declare at what interval it will send health reports. A typical value will be 60 seconds.

As long as health reports are received from an integration instance, Enterprise will consider it to be active. This is reflected in the integration instance’s integrationStatus.state which is set to active.

If three (3) consecutive health reports fail to be received by Anchore Enterprise, it will set the integration instance’s integrationStatus.state to inactive.

This is an example of what the integration status can look like when health reports have not been received from an integration instance:

{
  "integrationStatus": {
    "reason": "Integration last_seen timestamp is older than 2024-10-21 15:33:07.534974",
    "state": "inactive",
    "updatedAt": "2024-10-21T15:46:07Z"
    }
}

A next step to triage this could be to check if the integration instance is actually running or if there is some network connectivity issue preventing health reports from being received.

This is an example of integration status when health reports are received as expected:

{
  "integrationStatus": {
    "state": "active",
    "updatedAt": "2024-10-21T08:21:01Z"
  }
}

The below figure illustrates how the integrationStatus.state will transition between its (lifecycle) states.

Integration Status states

Integration instance properties

An integration instance has the below properties. Some properties may not have a value.

  • accountName: The account that integration instance used during registration (and thus belongs to).
  • accounts: List of account names that the integration instance handles. The list is updated from information contained in health reports from the integration instance. For the Kubernetes Inventory agent, this list holds all accounts that the agent has recently attempted to send inventory reports for (regardless if the attempt succeeded or not).
  • clusterName: The cluster where the integration instance executes. This will typically be a Kubernetes cluster.
  • description: Short arbitrary text description of the integration instance.
  • explicitlyAccountBound: List of account names that the integration instance is explicitly configured to handle. This does not include account names that an integration instance could learn dynamically. For instance, the Kubernetes Inventory agent can learn about account names to handle via a special label set on the namespaces. Such account names are not included in this property.
  • healthReportInterval: Interval in seconds between health reports from the integration instance.
  • integrationStatus: The (life cycle) status of the integration instance.
  • lastSeen: Timestamp when the last health report was received from the integration instance.
  • name: Name of the integration instance.
  • namespace: The namespace where the integration executes. This will typically be a Kubernetes namespace.
  • namespaces: List of namespaces that the integration is explicitly configured to handle.
  • registrationId: Registration id that the integration instance used during registration.
  • registrationInstanceId: Registration instance id that the integration instance used during registration.
  • "reportedStatus: The health status of the integration instance derived from information reported in the last health report.
  • startedAt: Timestamp when the integration instance was started.
  • type: The type of the integration instance. In Enterprise v5.11.0, k8s_inventory_agent is the only value.
  • uptime: Uptime (in seconds) of the integration instance.
  • username: Username that the integration instance registered using.
  • uuid: The UUID of the integration instance. Used in REST API to specify instance.
  • version: Software version that the integration instance runs.

1 - Container Registries

Anchore Enterprise can analyze images from any Docker V2 compatible registry. A registry in Anchore Enterprise is a stored credential configuration: it tells the deployment how to authenticate to a registry host, and on its own it does not pull or analyze any images. (Repositories are the unit of analysis; see Watch a Repository.)

Anchore Enterprise attempts to download images from any registry without further configuration. You only need to define a registry when it requires authentication: once a registry and its credentials are defined, every pull for an image from that registry uses them.

A few options and behaviors apply to every registry, regardless of how you add it:

  • TLS certificate verification is on by default. Anchore Enterprise verifies the registry’s TLS certificate. You can turn verification off for a registry that presents a self-signed certificate or one signed by an unknown CA.
  • Credential validation is on by default. Anchore Enterprise validates the credential when a registry is added. Because validation methods for public registries change over time, you can skip the check, which is useful when a valid credential fails validation or when adding a credential before it is active at the registry.
  • Multiple credentials per host. You can store different credentials for different repositories on the same host (for example, two private repositories on docker.io) by qualifying each entry with a repository path.
  • Passwords are write-only. A registry’s password cannot be retrieved through the GUI, AnchoreCTL, or the API.

Most Docker V2 registries authenticate with a username and password. Amazon ECR, Google GCR, and Microsoft Azure also support their own native credentialing; see the registry-specific configuration below.

Manage Registries in the Anchore Enterprise GUI

Registry management lives under System → Configuration → Registries. Listing and creating registries requires a user in the admin account or a member of the read-write role for the account.

To add a registry, open the Registries tab and select Let’s add one! (or Add New Registry if registries already exist). In the modal, provide the Registry (hostname with optional port), the Type (for example docker_v2 or awsecr), and the Username and Password. Two toggles set the behavior described above: Allow Self Signed turns off TLS certificate verification, and Validate on Add skips credential validation.

After a registry is added, edit its credentials and options from the Actions column. The setup help for each registry type is also available inline via “Need some help setting up your registry?” near the bottom of the modal.

To store different credentials for repositories on the same host, add each entry with a repository path (for example, docker.io/anchore/*).

Manage Registries with AnchoreCTL

List the defined registries:

anchorectl registry list

Add a registry. The registry argument is the fully qualified hostname and optional port (for example registry.example.com:5000):

ANCHORECTL_REGISTRY_PASSWORD=<password> anchorectl registry add <registry> --username <username>

Add separate credentials for repositories on the same host with a path:

ANCHORECTL_REGISTRY_PASSWORD=<password> anchorectl registry add docker.io/anchore/* --username <username>

Both registry add and registry update accept --secure-connection=<true|false> (TLS certificate verification) and --validate=<true|false> (credential validation at add time); each defaults to true.

Get the details of a specific registry (the password is never returned):

anchorectl registry get <registry>

Update a registry’s username, password, or connection options:

ANCHORECTL_REGISTRY_PASSWORD=<newpassword> anchorectl registry update <registry> --username <newusername> --validate=<true|false> --secure-connection=<true|false>

Delete a registry. Deleting a registry record does not delete the image or tag records associated with it:

anchorectl registry delete <registry>

Manage Registries with the API

Registry configuration is managed through the Registries endpoints:

MethodEndpointDescription
GET/registriesList configured registries (list_registries)
POST/registriesAdd a registry (create_registry)
GET/registries/{registry}Get a registry configuration (get_registry)
PUT/registries/{registry}Update a registry (update_registry)
DELETE/registries/{registry}Delete a registry (delete_registry)

The full request and response schemas are in the API browser; search for the Registries tag.

Registry-Specific Configuration

The credential fields are the same whether you add a registry through the GUI, AnchoreCTL, or the API. For registries with native credentialing, see the registry-specific guides:

1.1 - Amazon Elastic Container Registry

This page describes how to give Anchore Enterprise access to images stored in Amazon Elastic Container Registry (ECR).

Anchore Enterprise can scan ECR images two ways. In centralized analysis — the focus of this page — you register ECR credentials and the deployment pulls and analyzes images itself. In distributed analysis, a CI/CD job pulls the image, generates the SBOM locally, and uploads only the SBOM; see Distributed Analysis from ECR in CI/CD.

Authentication Modes

When you register an ECR registry for centralized analysis, the Username and Password you supply select one of three authentication modes. Which to use depends on where Anchore Enterprise runs and how its AWS access to the registry is configured. The registry --type is always awsecr.

ModeUsernamePasswordUse when
API KeysAWS access key IDAWS secret access keyYou authenticate with long-lived access/secret keys, ideally from a dedicated, restricted IAM user.
Local CredentialsawsautoawsautoAnchore Enterprise should adopt the AWS credentials of its execution environment — an EC2 instance profile, ECS task role, or Kubernetes (IRSA/Pod Identity) service account role.
ECR Assume Role_iam_roleTarget role ARNAnchore Enterprise should assume a role different from the one it runs under to reach the registry.

API Keys

Provide an access key ID and secret access key from an AWS account or IAM user — ideally a dedicated IAM user scoped to only the ECR permissions it needs. Pass the access key ID as the username and the secret access key as the password:

ANCHORECTL_REGISTRY_PASSWORD=<MY_AWS_SECRET_ACCESS_KEY> anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username <MY_AWS_ACCESS_KEY_ID> --type awsecr

The --type awsecr flag tells Anchore Enterprise to treat these as AWS credentials; if omitted, AnchoreCTL infers the type from the registry URL. Anchore Enterprise uses the keys to generate ECR authentication tokens and refreshes them automatically as they expire (typically every 12 hours). Do not store an aws ecr get-login token as the credential itself — it expires after 12 hours and would need constant manual updates.

Local Credentials

In this mode Anchore Enterprise adopts the AWS credentials from its own execution environment — environment variables, ~/.aws/credentials, or (most commonly) an IAM role inherited from the instance, task, or pod it runs in. Set both the username and password to awsauto:

ANCHORECTL_REGISTRY_PASSWORD=awsauto anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username awsauto --type awsecr

How that execution role is assigned depends on the deployment:

  • Docker Compose or a self-managed install on EC2 — the deployment inherits the EC2 instance profile. See Grant an EC2 Instance Profile Role below.
  • Helm on EKS (or other Kubernetes) — the deployment inherits a role from its service account via IRSA or EKS Pod Identity. Set serviceAccountName in your Helm values, as described in Amazon S3 IAM Role Authentication and the EKS deployment guide. If one role serves both S3 and ECR, combine their permissions into it.

Grant an EC2 Instance Profile Role

When Anchore Enterprise runs directly on EC2, give the instance a role that includes the AmazonEC2ContainerRegistryReadOnly policy (or an equivalent policy scoped to your registries). You can configure this manually from the launch-instance wizard, or with an automation tool such as Terraform or CloudFormation:

Step 1: Select Create new IAM role.

logo

Step 2: Under type of trusted entity select EC2.

logo

Ensure that the AmazonEC2ContainerRegistryReadOnly policy is selected.

Step 3: Attach Permissions to the Role.

logo

Step 4: Name the role.

Give a name to the role and add this role to the instance you are launching.

On the running EC2 instance you can manually verify that the instance has inherited the correct role by running the following command:

curl http://169.254.169.254/latest/meta-data/iam/info
{
  "Code": "Success",
  "LastUpdated": "2018-01-12T18:45:12Z",
  "InstanceProfileArn": "arn:aws:iam::123456789012:instance-profile/ECR-ReadOnly",
  "InstanceProfileId": "ABCDEFGHIJKLMNOP"
}

Step 5: Enable IAM authentication in Anchore Enterprise by adding the following entry to config.yaml (it is disabled by default):

allow_awsecr_iam_auto: True

Step 6: Add the registry with awsauto for both the username and password, as shown above.

ECR Assume Role

Use this mode to have Anchore Enterprise assume a role different from the one it currently runs under. Anchore Enterprise uses the AWS credentials from its execution environment — the same ambient credentials as Local Credentials — to assume the role you specify. The execution credentials must be granted permission to assume the target role (sts:AssumeRole), and the target role must hold the ECR permissions. This mode does not require the allow_awsecr_iam_auto flag.

Set the username to _iam_role and the password to the ARN of the role to assume:

ANCHORECTL_REGISTRY_PASSWORD=arn:aws:iam::123456789012:role/anchore-ecr-access anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username _iam_role --type awsecr

To require an external ID when assuming the role, append it to the ARN in the password, separated by a semicolon (<ROLE_ARN>;<EXTERNAL_ID>):

ANCHORECTL_REGISTRY_PASSWORD='arn:aws:iam::123456789012:role/anchore-ecr-access;my-external-id' anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username _iam_role --type awsecr

Cross-Account Access

Assume-role is the standard way to scan a registry that lives in a different AWS account from the one Anchore Enterprise runs in. Point the registry URL at the ECR-owning account and set the password to the ARN of a role in that account — Anchore requests the authorization token for the registry account it parses from the URL, using the assumed role’s credentials.

The example below uses two accounts and two roles:

AccountRole
Anchore account — where Anchore Enterprise runs111111111111anchore-enterprise-execution (its execution role)
ECR account — where the registry and images live123456789012anchore-ecr-access (the role Anchore assumes)

Three IAM pieces are required, across both accounts:

IAM policyIn accountAttach toPurpose
Identity policy (below)111111111111 (Anchore)anchore-enterprise-executionAllows Anchore’s execution role to assume the target role
Trust policy (below)123456789012 (ECR)anchore-ecr-accessAllows the target role to be assumed by Anchore’s execution role
ECR permissions123456789012 (ECR)anchore-ecr-accessGrants pull/inspect access to the registry — attach the AWS-managed AmazonEC2ContainerRegistryReadOnly policy, or a least-privilege equivalent. Add ecr:BatchImportUpstreamImage for uncached pull-through images (see Scanning Images from an ECR Pull-Through Cache).
Identity Policy

Attach to anchore-enterprise-execution in the Anchore account (111111111111). It lets Anchore’s execution role call sts:AssumeRole on the target role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AssumeEcrAccessRole",
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::123456789012:role/anchore-ecr-access"
    }
  ]
}
Trust Policy

Set as the trust relationship on anchore-ecr-access in the ECR account (123456789012). It names Anchore’s execution role as a principal allowed to assume the role. For cross-account or third-party trust, AWS recommends requiring an external ID to guard against the confused-deputy problem; the Condition below requires the external ID my-external-id:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111:role/anchore-enterprise-execution"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "my-external-id"
        }
      }
    }
  ]
}
Register the Registry

With the policies in place, add the registry using _iam_role and the target role’s ARN, pointing the registry URL at the ECR account (123456789012). Because the trust policy requires an external ID, append it to the ARN after a semicolon (and quote the value so the shell does not split on the ;):

ANCHORECTL_REGISTRY_PASSWORD='arn:aws:iam::123456789012:role/anchore-ecr-access;my-external-id' anchorectl registry add 123456789012.dkr.ecr.us-east-1.amazonaws.com --username _iam_role --type awsecr

Distributed Analysis from ECR in CI/CD

In a CI/CD pipeline you often want distributed analysis rather than a registered, stored credential: the job pulls the image, AnchoreCTL generates the SBOM locally, and only the SBOM is uploaded. The image bytes never leave the runner, and you do not register the registry with Anchore Enterprise at all.

This pairs naturally with short-lived ECR credentials. A stored credential must be long-lived — the reason the API Keys mode avoids the 12-hour get-login token — but a CI job is ephemeral, so a token minted at the start of the job and used immediately is a perfect fit.

A typical job assumes an IAM role, exchanges it for a short-lived ECR token, and hands that token to AnchoreCTL:

# 1. Assume the role that has ECR pull permissions (plus ecr:BatchImportUpstreamImage
#    if you are analyzing uncached pull-through images). Skip this step if the runner
#    already carries the role via an instance profile or IRSA.
CREDS=$(aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/anchore-ci-scanner \
  --role-session-name anchore-scan)
export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo "$CREDS" | jq -r '.Credentials.SessionToken')

# 2. Exchange the AWS credentials for a short-lived ECR registry token and hand it to
#    AnchoreCTL. The username for an ECR token is always AWS.
export ANCHORECTL_REGISTRY_AUTH_AUTHORITY=123456789012.dkr.ecr.us-east-1.amazonaws.com
export ANCHORECTL_REGISTRY_AUTH_USERNAME=AWS
export ANCHORECTL_REGISTRY_AUTH_PASSWORD=$(aws ecr get-login-password --region us-east-1)

# 3. Pull and analyze locally, then upload only the SBOM.
anchorectl image add \
  123456789012.dkr.ecr.us-east-1.amazonaws.com/docker-hub/anchore/enterprise:v6.1.0 \
  --from registry --wait

The --from registry flag is what makes this distributed: AnchoreCTL pulls the image itself using the ANCHORECTL_REGISTRY_AUTH_* credentials, so no docker login or local Docker daemon is required. Set the ANCHORECTL_URL and ANCHORECTL_USERNAME/ANCHORECTL_PASSWORD (or ANCHORECTL_API_KEY) variables that point AnchoreCTL at your deployment as usual; they are omitted here for brevity. See AnchoreCTL Configuration for all registry-auth settings.


Scanning Images from an ECR Pull-Through Cache

Amazon ECR pull-through cache rules let ECR transparently cache images from an upstream registry (such as Docker Hub) under a local namespace. For example, an upstream image pulled through a cache rule with the prefix docker-hub:

docker.io/anchore/enterprise:v6.1.0

appears in ECR as:

123456789012.dkr.ecr.us-east-1.amazonaws.com/docker-hub/anchore/enterprise:v6.1.0

Anchore Enterprise can scan pull-through cached images directly, including images that have not yet been cached — there is no need for a separate step to pre-warm the cache. This does, however, require the scanning principal to hold the permission that triggers the upstream import.

Grant the Import Permission

When Anchore inspects a tag that has never been cached, ECR must import it from the upstream registry on that first request. If the principal cannot perform the import, the inspect fails and ECR returns an error that looks like a missing image rather than a missing permission:

manifest unknown: Requested image not found

The underlying cause is that the principal could read already-cached images but lacked permission to import uncached ones. Granting ecr:BatchImportUpstreamImage allows that first inspect or pull to trigger the upstream import; the image is cached from then on.

The following policy grants import permission, scoped to the pull-through cache prefix:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowECRPullThrough",
      "Effect": "Allow",
      "Action": [
        "ecr:BatchImportUpstreamImage",
        "ecr:CreateRepository"
      ],
      "Resource": [
        "arn:aws:ecr:us-east-1:123456789012:repository/docker-hub/*"
      ]
    }
  ]
}

Keeping ecr:BatchImportUpstreamImage scoped to the docker-hub/* prefix (rather than *) follows least-privilege and is the recommended scope.

Analyze Uncached Images by Tag

Granting the import permission is what allows the upstream import, but it does not by itself pull anything. ECR only imports an uncached image when something requests that specific tag. To have Anchore import and analyze an image that has never been cached, add the specific tag for analysis:

anchorectl image add 123456789012.dkr.ecr.us-east-1.amazonaws.com/docker-hub/anchore/enterprise:v6.1.0

That request triggers the upstream import for the tag, ECR caches it, and Anchore analyzes it.

A registry or repository watch (subscription) does not force uncached images to import. A watch discovers images that already exist in the ECR repository — those that have been cached — and it will pick up new images as they become cached going forward. It does not enumerate the upstream registry, so a tag that has never been pulled through the cache will not appear until it is imported at least once by an explicit tag analysis.

Auto-Creating Repositories for New Images

The prefix scope above (docker-hub/*) covers images whose repository already exists under that prefix. The very first pull of an upstream image whose repository has never been created also requires that repository to be created automatically. If this is not permitted, ECR returns an error such as:

repository ... does not exist (name unknown)

There are two ways to allow the repository to be created:

  • Grant ecr:CreateRepository, as shown in the policy above. CreateRepository is a registry-level action, so it is typically scoped to arn:aws:ecr:us-east-1:123456789012:repository/* rather than a sub-prefix.
  • Configure a repository creation template for the pull-through prefix. This lets ECR auto-create repositories without granting the principal CreateRepository directly, and also lets you enforce scan-on-push, tags, and encryption on the cached repositories.

Troubleshooting Authentication Errors

When Anchore Enterprise (or AnchoreCTL) cannot authenticate to ECR, the errors it surfaces often read like the image is missing rather than like an auth failure — ECR and skopeo report an authorization denial as authentication required or Not Authorized. An image that scans fine in one account can therefore fail in another that lacks the credential. The messages below all trace back to a missing or invalid registry credential, expired keys, or a failed role assumption.

Where you see itErrorRoot cause
anchorectl registry add … --type awsecrcannot ping supplied registry with supplied credentials [HTTP 406]The AWS keys or role supplied at registration failed to authenticate to ECR.
anchorectl image add (centralized)cannot fetch image digest/manifest from registry [HTTP 400]No valid ECR credential for this registry in the account you are using.
Event system.image_analysis.registry_lookup_failedMessage Referenced image not found in registry; detail contains authentication required and error_code=REGISTRY_PERMISSION_DENIEDSame as above — the “not found” wording is misleading; the deployment could not authenticate to pull the manifest.
anchorectl image add … --from registry (distributed)401 Unauthorized: Not Authorized, or DENIED: Your authorization token has expired. Reauthenticate and try again.AnchoreCTL’s local ECR credentials (ANCHORECTL_REGISTRY_AUTH_*) are missing or the token has expired.

To diagnose and resolve:

  • Confirm a credential exists in the right account. Registry credentials are per-account — run anchorectl registry list in the same account you are analyzing under. An image that fails in one account but succeeds in another almost always means the failing account has no ECR credential.
  • Read the full cause. Fetch the event detail with anchorectl event get <EVENT_ID> -o json, or check the catalog service logs, where the underlying skopeo inspect failure (authentication required, REGISTRY_PERMISSION_DENIED) is logged.
  • By mode: for API Keys, verify the access key and secret and that the IAM principal holds ECR read permissions; for Local Credentials, confirm the execution role is attached and allow_awsecr_iam_auto: True is set; for ECR Assume Role, confirm the sts:AssumeRole grant, the target role’s trust policy, and any external ID.
  • Distributed jobs: re-run aws ecr get-login-password to mint a fresh token — ECR tokens expire after 12 hours.

1.2 - Azure Container Registry

To use an Azure Registry, you can configure Anchore Enterprise to use either the admin credential(s) or a service principal. Refer to Azure documentation for differences and how to setup each. When you’ve chosen a credential type, use the following to determine which registry command options correspond to each value for your credential type

  • Admin Account

    • Registry: The login server (Ex. myregistry1.azurecr.io)
    • Username: The username in the ‘az acr credential show –name ’ output
    • Password: The password or password2 value from the ‘az acr credential show’ command result
  • Service Principal

    • Registry: The login server (Ex. myregistry1.azurecr.io)
    • Username: The service principal app id
    • Password: The service principal password
      Note: You can follow Microsoft Documentation for creating a Service Principal.

To add an azure registry credential, invoke anchorectl as follows:

ANCHORECTL_REGISTRY_PASSWORD=<password> anchorectl registry add <registry> --username <username> <Password>

Once a registry has been added, any image that is added (e.g. anchorectl image add <Registry>/some/repo:sometag) will use the provided credential to download/inspect and analyze the image.

1.3 - Google Container Registry

When working with Google Container Registry it is recommended that you use JSON keys rather than the short lived access tokens.

JSON key files are long-lived and are tightly scoped to individual projects and resources. You can read more about JSON credentials in Google’s documentation at the following URL: Google Container Registry advanced authentication

Once a JSON key file has been created with permissions to read from the container registry then the registry should be added with the username _json_key and the password should be the contents of the key file.

In the following example a file named key.json in the current directory contains the JSON key with readonly access to the my-repo repository within the my-project Google Cloud project.

ANCHORECTL_REGISTRY_PASSWORD="$(cat key.json)" anchorectl registry add us.gcr.io --username _json_key

1.4 - Harbor

Harbor is an open-source, cloud-native container registry. Anchore Enterprise integrates with Harbor in two ways: as a registry it pulls images from for analysis, and as a scanner that Harbor delegates its vulnerability scans to.

Use Harbor as a Registry

To let Anchore Enterprise pull and analyze images from Harbor, add it as a Docker V2 registry with your Harbor credentials:

  1. Harbor URL — the base URL of your Harbor registry.
  2. Harbor username — a Harbor account with access to the repositories you want analyzed (for example, the admin account).
  3. Harbor password — the corresponding password.
ANCHORECTL_REGISTRY_PASSWORD=Harbor12345 anchorectl registry add core.harbor.domain --username admin

Once the registry is added, any image you add (for example, anchorectl image add core.harbor.domain/some/repo:sometag) uses the stored credential to download, inspect, and analyze the image. See Container Registries for the full registry-management surface across the GUI, AnchoreCTL, and the API.

Harbor Scanner Adapter

For a deeper integration, the Harbor Scanner Adapter for Anchore lets Harbor issue scans to Anchore Enterprise directly. The adapter is a bridge between the two systems: Harbor schedules scans — on push, on a recurring schedule, or on demand — and the adapter forwards them to your Anchore Enterprise deployment, with results surfaced in both Harbor and the Anchore Enterprise GUI.

1.4.1 - Harbor Scanner Adapter Setup

Integrating Harbor

The Harbor Scanner Adapter for Anchore can be used to integrate Harbor with Anchore Enterprise. This scanner provides a gateway for Harbor to communicate with your Anchore Enterprise deployment thereby making it possible for jobs to be scheduled for scans through Harbor.

The adapter’s configuration can be customized using environment variables defined in the harbor-adapter-anchore.yaml.

You can edit this file to adjust the environment variables as needed to fit your deployment. You must configure how the adapter connects to Anchore Enterprise. The following variables are compulsory to be configured:

ANCHORE_ENDPOINT 
ANCHORE_USERNAME 
ANCHORE_PASSWORD

Note: It is highly recommended that you create a new account in the Anchore Enterprise deployment and a new user with credentials dedicated to the Harbor adapter. When using Enterprise 5+, you can also utilize api keys. Learn how to generate them here

For full Harbor Adapter configuration options, see here

Once you have edited the value file, use the updated file to deploy the Harbor Scanner Adapter by executing:

kubectl apply -f harbor-adapter-anchore.yaml  

Once the adapter has been configured as shown above, you will need to add Anchore as the default scanner in Harbor.

Adding Anchore as default scanner

Setting Anchore as the default scanner in Harbor ensures that all image scans, unless specified otherwise, are automatically sent to your Anchore Enterprise deployment for scanning. Follow the steps below to add Anchore as a scanner and set it as the default:

In the Harbor UI login as an admin and navigate to Administration->Interrogation Services->Scanners and click “+ New Scanner”. In older versions of Harbor, this can be found under Configuration->Scanners.

alt text

In ‘Endpoint’, use the adapter hostname/url. The default is the following:

http://harbor-scanner-anchore:8080  

Leave the authorization field empty, as no API key was set in the adapter deployment environment for this example.

Please untick use internal registry address. Anchore Enterprise could have issues accessing the Harbor registry otherwise

Click “Test Connection” to verify the connection. Then, click “Add” to add the scanner.

Now to ensure all projects in Harbor makes use of the newly configured Anchore scanner, you must make the Anchore scanner your default Scanner. In the Harbor UI, navigate to the project->scanner and click “Select Scanner” click on the radio button next to the selected Anchore Scanner to make it the default scanner.

alt text

Configuring Timeouts

Since Harbor and Anchore Enterprise are separate systems, an API call is needed for communication between them. As a result, configuring timeouts may be necessary depending on factors such as your network, the proximity of the two systems, and overall latency.

The ANCHORE_CLIENT_TIMEOUT_SECONDS setting determines the timeout duration (in seconds) for API calls from the Harbor Adapter to the Anchore Enterprise service. By default, it is set to 60 seconds. If the API call to Anchore exceeds this time, the scan may fail or be delayed. A shorter timeout can result in more frequent timeouts during scans, especially if the system is under heavy load or if Anchore’s response time is slower than expected.

The proximity of Anchore Enterprise to the registry also plays a crucial role in scan performance. If Anchore Enterprise is geographically distant or on a separate network from the registry, network latency could increase, leading to slower scan times or potential timeouts. Keeping Anchore Enterprise close to the registry in terms of network topology can reduce latency, improving scan efficiency and reducing the likelihood of timeouts.

To increase the ANCHORE_CLIENT_TIMEOUT_SECONDS, set the environment variable in your harbor-adapter-anchore.yaml file and reapply it.

{
  "username": "harbor",
  "password": "harboruserpass123",
  "endpoint": "http://somehost",
  "timeoutseconds": 120,
  "tlsverify": false
} 

1.4.2 - Using Harbor with Anchore

After configuration is complete, you can move on to scanning images.

Image Tagging and Pushing to Harbor

To add your first image to the Harbor registry and perform a vulnerability analysis. Follow these steps:

Login to Harbor using Docker CLI

On your host machine, log in to Harbor using the Docker CLI:

docker login -u <user_name> core.harbor.domain  

Replace <user_name> with your Harbor username. Enter the password when prompted.

If your credentials and certificates are correct, you’ll see a “Login Succeeded” message.

Tag Your Image

Tag the image you want to push to Harbor with the appropriate format:

docker tag <IMAGE:TAG> core.harbor.domain/library/<IMAGE:TAG>

Replace IMAGE:TAG with the name and tag of your image (e.g. redis:4).

The library part refers to the project in Harbor. Adjust it if your image belongs to a different project.

Push Your Image to Harbor

Push the tagged image to your Harbor registry:

docker push core.harbor.domain/library/<IMAGE:TAG>

You can now see the pushed image in the Harbor UI by Navigating to the project under the project menu

Pushed Image

Initiate a Vulnerability Scan

To scan your image for vulnerabilities select the image from the repository list. Click SCAN VULNERABILITY under the Actions menu:

Scan Vulnerability

During integration you will have configured Anchore Enterprise as your default scanner. This means vulnerability scan requests will be sent to your Anchore Enterprise deployment. Once the scan is complete, the results will appear in both Harbor and the Anchore Enterprise GUI. You can view details about the vulnerabilities, including severity and remediation options.

Scan result from Harbor

Scan result from Anchore

Scheduling a Vulnerability Scan

Harbor allows you to schedule automated vulnerability scans on your container images. These scans can be performed using the configured scanner (Anchore Enterprise) and will help identify vulnerabilities within the images.

Navigate to Interrogation Services. Under the Vulnerability tab you will see options on scheduling scans (Hourly, daily, weekly or custom). You can also initiate scan of all your images immediately by clicking the SCAN NOW button.

Scan now

Information regarding scan in progress will be provided on this page.

Scan_info

It is important to note that weekly scans can take time, especially if you have many images. Anchore Enterprise will fetch the latest vulnerability results only if it hasn’t scanned the image before since it caches images it has previously seen. This helps to reduce the overall time required for weekly scans. Additionally, number of analyzers, network latency and timeouts can impact the time taken for a weekly scan to complete.

Enable Image Scanning on Push

By enabling the Scan on Push option under the project’s configuration, Harbor will automatically scan any new images pushed to the project, helping you identify and manage potential security risks efficiently. To enable this. Navigate to the desired project -> configuration and look for the option vulnerability scanning as shown in the picture

Enable Image Scanning on Push

Prevent vulnerable images from running

To prevent vulnerable images from being pulled and run, you can set up a policy which uses the last known vulnerability results.

Please note: Anchore Enterprise is still able to pull images to conduct scans.

To do this, navigate to the desired Project -> Configuration and enable the Vulnerability Scanning option

Locate the Deployment Security option, enable it, and choose the severity level to enforce.

Prevent vulnerable images from running

Adding Proxy Registries

Harbor has the ability to act as a proxy registry linking to preconfigured upstream registries like DockerHub. This allows users to pull images from Harbor directly which in turn using pre configured credentials pulls and caches the images from an upstream source.

You can learn more about how to set this up here

Use Case: A common use case is that customers want to restrict registry access in a production and/or secure environment to only their Harbor registry and as such Anchore’s own Enterprise images are published and accessible via DockerHub and Iron Bank which might not be accessible. To resolve this, you can setup a proxy cache registry in Harbor and then pull the image from your Harbor deployment.

docker pull <harbor_server_name>/<proxy_project_name>/anchore/enterprise:v6.X.X

Don’t forget you can also configure your Anchore Enterprise values.yaml file so that your deployment will pull the images from your private Harbor registry

image: <harbor_server_name>/<proxy_project_name>/anchore/enterprise:v6.X.X
ui:
  image: <harbor_server_name>/<proxy_project_name>/anchore/enterprise-ui:v6.X.X

Finally, an added benefit is that you have a local copy of the Anchore Enterprise Images rather than relying on a public services such as DockerHub or Iron Bank.

Debugging scan issues

When image scanning fails in Harbor using Anchore, it’s important to review logs from three key components: Harbor, the Anchore Adapter, and Anchore Enterprise. Collecting these logs and generating a support bundle can help diagnose the issue. You can then share this information with the Anchore Customer Success team for further assistance.

For example to collect Harbor Adapter logs

kubectl logs <harbor-scanner-adapter-pod-name> -n <harbor-scanner-adapter-namespace>

For Anchore Enterprise, follow instructions here to generate a support bundle

2 - CI / CD Integration

Integrating Anchore Enterprise into your CI/CD pipeline enables fast shift-left feedback, so developers can identify and resolve security issues early in the software development lifecycle.

Platform-specific guides are available for GitHub, GitLab, Jenkins, Azure Pipelines, and AWS CodeBuild; see the subpages in this section. This page covers the requirements and the integration patterns common to all of them.

Requirements

  • Network access. Anchore Enterprise must be deployed so its API is reachable from your pipeline runners. For centralized analysis, the deployment must also be able to reach the container registries that host your images.
  • Authentication. API keys are recommended for authenticating from a pipeline, though username and password authentication is also supported.
  • AnchoreCTL. AnchoreCTL is the primary interface for CI/CD automation and should be version-aligned with your deployment. A common practice is to fetch it from your Anchore Enterprise deployment during the job so the client always matches the server.

Choose an Analysis Mode

Anchore Enterprise supports two analysis modes, described in full under Centralized and Distributed Analysis:

  • Distributed analysis is the recommended default for CI. AnchoreCTL generates the SBOM on the runner and uploads it, so image content never leaves the pipeline. Give your runners fast CPU and I/O, and enable cataloger parallelism to speed up SBOM generation. See Using Distributed Analysis Mode for the AnchoreCTL configuration.
  • Centralized analysis is required only when you need malware scanning, which unpacks image layers server-side. The deployment pulls and analyzes the image itself.

Gate the Pipeline on Policy

Use the Anchore Enterprise policy engine to turn raw findings into a pass/fail decision the pipeline can act on. Both policy evaluation scopes expose --fail-based-on-results, which returns a non-zero exit code when the result is fail and so fails the CI step.

Evaluate a standalone image in the image catalog against the active policy:

anchorectl image check <MY_IMAGE> --fail-based-on-results --detail

Or check the status of an app version, which rolls up policy results across every asset attached to the version:

anchorectl app version policy status get <VERSION> --app <APP> --fail-based-on-results

On image check, --detail adds the gate, trigger, and remediation detail developers need to resolve violations; for an app version, list the findings behind the verdict with anchorectl app version policy findings list <VERSION> --app <APP>.

One-Time Scan (Stateless Evaluation)

When a pipeline only needs fast pass/fail feedback and does not need the build’s SBOM persisted in the deployment, use a stateless One-Time Scan:

anchorectl image one-time-scan <MY_IMAGE> --from registry --fail-on-policy-error

For the full behavior, output options, and how it appears in usage reporting, see One-Time Scan.

2.1 - AWS CodeBuild

Image scanning can be integrated into your AWS CodeBuild pipeline using anchorectl. This guide provides an end-to-end example that creates all required AWS resources (ECR, CodeCommit, S3, IAM roles, CodeBuild, and CodePipeline) from scratch. If you already have an existing CodeBuild project and CodePipeline, the key integration points are the install phase (to install anchorectl), the post_build commands, and the artifacts section of the buildspec.yml in Step 5.

Requirements

  1. Anchore Enterprise is deployed in your environment, with the API accessible from your AWS CodeBuild environment.
  2. An AWS account with permissions to create ECR repositories, CodeCommit repositories, CodeBuild projects, CodePipeline pipelines, S3 buckets, and IAM roles.
  3. The AWS CLI installed and configured with valid credentials.

1. Configure Variables

Set the following shell variables for use throughout the guide. Replace the placeholder values with your actual Anchore Enterprise deployment URL, username, and password. The ANCHORECTL_PASSWORD value should be treated as a secret to prevent exposure in logs.

export AWS_REGION=us-east-1
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

export APP_NAME=myapp
export ECR_REPO_NAME=$APP_NAME
export CODECOMMIT_REPO_NAME=$APP_NAME-repo
export CODEBUILD_PROJECT_NAME=$APP_NAME-build
export CODEPIPELINE_NAME=$APP_NAME-pipeline

export ARTIFACT_BUCKET=${APP_NAME}-codepipeline-artifacts-${AWS_ACCOUNT_ID}-${AWS_REGION}

export CODEBUILD_ROLE_NAME=${APP_NAME}-codebuild-role
export CODEPIPELINE_ROLE_NAME=${APP_NAME}-codepipeline-role

### Anchore Enterprise connection details
export ANCHORECTL_URL=http://anchore-enterprise-api.example.com:8228
export ANCHORECTL_USERNAME=admin
export ANCHORECTL_PASSWORD=foobar

2. Create AWS Resources

a) ECR Repository

Create an ECR repository to store your container images. Image tag immutability is enabled to ensure each build produces a unique, traceable image tag derived from the git commit hash.

aws ecr create-repository \
  --region "$AWS_REGION" \
  --repository-name "$ECR_REPO_NAME" \
  --image-tag-mutability IMMUTABLE \
  --image-scanning-configuration scanOnPush=true

b) CodeCommit Repository

Create a CodeCommit repository to host your application source code.

aws codecommit create-repository \
  --region "$AWS_REGION" \
  --repository-name "$CODECOMMIT_REPO_NAME" \
  --repository-description "Source repo for $APP_NAME"

c) S3 Artifact Bucket

Create an S3 bucket for CodePipeline to store build artifacts (including Anchore scan results). Versioning, encryption, and public access blocking are enabled for security best practices.

### us-east-1 does not support LocationConstraint
if [ "$AWS_REGION" = "us-east-1" ]; then
  aws s3api create-bucket \
    --bucket "$ARTIFACT_BUCKET" \
    --region "$AWS_REGION"
else
  aws s3api create-bucket \
    --bucket "$ARTIFACT_BUCKET" \
    --region "$AWS_REGION" \
    --create-bucket-configuration LocationConstraint="$AWS_REGION"
fi

### Enable versioning
aws s3api put-bucket-versioning \
  --bucket "$ARTIFACT_BUCKET" \
  --versioning-configuration Status=Enabled

### Enable server-side encryption
aws s3api put-bucket-encryption \
  --bucket "$ARTIFACT_BUCKET" \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "aws:kms"
        },
        "BucketKeyEnabled": true
      }
    ]
  }'

### Block all public access
aws s3api put-public-access-block \
  --bucket "$ARTIFACT_BUCKET" \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

3. Create IAM Roles

a) CodeBuild Role

The CodeBuild role needs permissions for CloudWatch Logs, S3 artifact access, CodeCommit source pulls, and ECR push/pull operations.

cat > codebuild-trust-policy.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "codebuild.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

cat > codebuild-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Logs",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ArtifactsBucket",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::$ARTIFACT_BUCKET",
        "arn:aws:s3:::$ARTIFACT_BUCKET/*"
      ]
    },
    {
      "Sid": "CodeCommitSource",
      "Effect": "Allow",
      "Action": [
        "codecommit:GitPull"
      ],
      "Resource": "arn:aws:codecommit:$AWS_REGION:$AWS_ACCOUNT_ID:$CODECOMMIT_REPO_NAME"
    },
    {
      "Sid": "ECRAuth",
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ECRPushPull",
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:CompleteLayerUpload",
        "ecr:GetDownloadUrlForLayer",
        "ecr:InitiateLayerUpload",
        "ecr:PutImage",
        "ecr:UploadLayerPart",
        "ecr:BatchGetImage"
      ],
      "Resource": "arn:aws:ecr:$AWS_REGION:$AWS_ACCOUNT_ID:repository/$ECR_REPO_NAME"
    }
  ]
}
EOF

aws iam create-role \
  --role-name "$CODEBUILD_ROLE_NAME" \
  --assume-role-policy-document file://codebuild-trust-policy.json

aws iam put-role-policy \
  --role-name "$CODEBUILD_ROLE_NAME" \
  --policy-name "${APP_NAME}-codebuild-inline" \
  --policy-document file://codebuild-policy.json

export CODEBUILD_ROLE_ARN=$(aws iam get-role \
  --role-name "$CODEBUILD_ROLE_NAME" \
  --query 'Role.Arn' \
  --output text)

b) CodePipeline Role

The CodePipeline role needs permissions for S3 artifact access, CodeCommit source operations, and CodeBuild build triggers.

cat > codepipeline-trust-policy.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "codepipeline.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

cat > codepipeline-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3Artifacts",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:GetBucketVersioning",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::$ARTIFACT_BUCKET",
        "arn:aws:s3:::$ARTIFACT_BUCKET/*"
      ]
    },
    {
      "Sid": "CodeCommitSource",
      "Effect": "Allow",
      "Action": [
        "codecommit:GetBranch",
        "codecommit:GetCommit",
        "codecommit:UploadArchive",
        "codecommit:GetUploadArchiveStatus",
        "codecommit:CancelUploadArchive"
      ],
      "Resource": "arn:aws:codecommit:$AWS_REGION:$AWS_ACCOUNT_ID:$CODECOMMIT_REPO_NAME"
    },
    {
      "Sid": "CodeBuildStart",
      "Effect": "Allow",
      "Action": [
        "codebuild:BatchGetBuilds",
        "codebuild:StartBuild"
      ],
      "Resource": "arn:aws:codebuild:$AWS_REGION:$AWS_ACCOUNT_ID:project/$CODEBUILD_PROJECT_NAME"
    }
  ]
}
EOF

aws iam create-role \
  --role-name "$CODEPIPELINE_ROLE_NAME" \
  --assume-role-policy-document file://codepipeline-trust-policy.json

aws iam put-role-policy \
  --role-name "$CODEPIPELINE_ROLE_NAME" \
  --policy-name "${APP_NAME}-codepipeline-inline" \
  --policy-document file://codepipeline-policy.json

export CODEPIPELINE_ROLE_ARN=$(aws iam get-role \
  --role-name "$CODEPIPELINE_ROLE_NAME" \
  --query 'Role.Arn' \
  --output text)

4. Create the CodeBuild Project

The CodeBuild project defines the build environment and passes Anchore Enterprise credentials as environment variables. The privilegedMode setting is required for Docker-in-Docker builds.

Note: The ANCHORECTL_PASSWORD is included as a PLAINTEXT environment variable here for simplicity. For production use, store it in AWS Secrets Manager or SSM Parameter Store and reference it with type: SECRETS_MANAGER or type: PARAMETER_STORE in the environmentVariables block.

cat > create-project.json <<EOF
{
  "name": "$CODEBUILD_PROJECT_NAME",
  "serviceRole": "$CODEBUILD_ROLE_ARN",
  "source": {
    "type": "CODEPIPELINE",
    "buildspec": "buildspec.yml"
  },
  "artifacts": {
    "type": "CODEPIPELINE"
  },
  "environment": {
    "type": "LINUX_CONTAINER",
    "image": "aws/codebuild/standard:7.0",
    "computeType": "BUILD_GENERAL1_MEDIUM",
    "privilegedMode": true,
    "environmentVariables": [
      {
        "name": "AWS_REGION",
        "value": "$AWS_REGION",
        "type": "PLAINTEXT"
      },
      {
        "name": "IMAGE_REPO_NAME",
        "value": "$ECR_REPO_NAME",
        "type": "PLAINTEXT"
      },
      {
        "name": "ANCHORECTL_URL",
        "value": "$ANCHORECTL_URL",
        "type": "PLAINTEXT"
      },
      {
        "name": "ANCHORECTL_USERNAME",
        "value": "$ANCHORECTL_USERNAME",
        "type": "PLAINTEXT"
      },
      {
        "name": "ANCHORECTL_PASSWORD",
        "value": "$ANCHORECTL_PASSWORD",
        "type": "PLAINTEXT"
      }
    ]
  },
  "timeoutInMinutes": 60
}
EOF

aws codebuild create-project \
  --region "$AWS_REGION" \
  --cli-input-json file://create-project.json

5. Configure Scanning Mode

a) Distributed Mode

This is the most easily scalable method for scanning images. Distributed scanning uses the anchorectl utility to build the SBOM directly on the CodeBuild runner and then pushes the SBOM to Anchore Enterprise through the API. This avoids the need to provide registry credentials in the Enterprise backend, since the image is loaded directly from the local Docker daemon.

Clone the CodeCommit repository and create a buildspec.yml with the following content. The buildspec installs anchorectl directly from your Anchore Enterprise deployment (ensuring version compatibility), builds and tags the Docker image using the git commit hash, scans the image with Anchore Enterprise, and exports all scan artifacts.

git clone "$(aws codecommit get-repository \
  --region "$AWS_REGION" \
  --repository-name "$CODECOMMIT_REPO_NAME" \
  --query 'repositoryMetadata.cloneUrlHttp' \
  --output text)"

cd "$CODECOMMIT_REPO_NAME"
git checkout -b main

cat > buildspec.yml <<'EOF'
version: 0.2

phases:
  install:
    commands:
      ### install anchorectl from your Anchore Enterprise deployment to ensure version compatibility
      - curl -sSfL -u "${ANCHORECTL_USERNAME}:${ANCHORECTL_PASSWORD}" "${ANCHORECTL_URL}/v2/system/anchorectl?operating_system=linux&architecture=amd64" | tar -zx -C /usr/local/bin anchorectl
  pre_build:
    commands:
      - AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
      ### use the short git commit hash as the image tag for traceability
      - IMAGE_TAG=$(echo "$CODEBUILD_RESOLVED_SOURCE_VERSION" | cut -c1-7)
      - IMAGE_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}
      - aws ecr get-login-password --region "$AWS_REGION" | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
  build:
    commands:
      - |
        docker build -t ${IMAGE_REPO_NAME}:${IMAGE_TAG} \
          --build-arg CI=aws-codepipeline \
          --build-arg REPO=${IMAGE_REPO_NAME} \
          --build-arg COMMIT=${CODEBUILD_RESOLVED_SOURCE_VERSION} \
          --build-arg COMMIT_SHORT=${IMAGE_TAG} \
          --build-arg PIPELINE=${CODEBUILD_INITIATOR} \
          --build-arg REGION=${AWS_REGION} \
          .
      - docker tag ${IMAGE_REPO_NAME}:${IMAGE_TAG} ${IMAGE_URI}
  post_build:
    commands:
      ### scan the image from the local Docker daemon (distributed mode) and wait for analysis to complete
      ### --get all=./ exports all scan artifacts (SBOMs, vulnerabilities, policy evaluation) to the working directory
      - |
        anchorectl image add ${IMAGE_URI} --dockerfile Dockerfile --from docker --dockerfile Dockerfile --no-auto-subscribe --wait --get all=./ \
          --annotation "ci=aws-codepipeline" \
          --annotation "repo=${IMAGE_REPO_NAME}" \
          --annotation "commit=${CODEBUILD_RESOLVED_SOURCE_VERSION}" \
          --annotation "commit_short=${IMAGE_TAG}" \
          --annotation "pipeline=${CODEBUILD_INITIATOR}" \
          --annotation "region=${AWS_REGION}"
      ### evaluate the image against your Anchore Enterprise policy
      ### set --fail-based-on-results to break the build if the policy evaluation returns FAIL
      - anchorectl image check ${IMAGE_URI} --fail-based-on-results --detail
      - docker push ${IMAGE_URI}
      - printf '[{"name":"app","imageUri":"%s"}]' "${IMAGE_URI}" > imagedefinitions.json
artifacts:
  files:
    - imagedefinitions.json
    - content.json
    - image-metadata.json
    - policy-evaluation.json
    - sbom.json
    - sbomcyclonedx.json
    - sbomspdx.json
    - vulnerability.json
EOF

cat > Dockerfile <<'EOF'
FROM public.ecr.aws/ubuntu/ubuntu:latest

ARG CI
ARG REPO
ARG COMMIT
ARG COMMIT_SHORT
ARG PIPELINE
ARG REGION

LABEL org.opencontainers.image.source="${REPO}" \
      org.opencontainers.image.revision="${COMMIT}" \
      com.anchore.ci="${CI}" \
      com.anchore.commit.short="${COMMIT_SHORT}" \
      com.anchore.pipeline="${PIPELINE}" \
      com.anchore.region="${REGION}"

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
       python3 \
       python3-pip \
    && rm -rf /var/lib/apt/lists/*

CMD ["python3", "--version"]
EOF

git add .
git commit -m "Initial commit"
git push origin main

cd ..

The --get all=./ flag on anchorectl image add exports the following scan artifacts to the build directory, which are then stored as pipeline artifacts in S3:

ArtifactDescription
sbom.jsonAnchore-native SBOM format
sbomcyclonedx.jsonCycloneDX SBOM (industry standard)
sbomspdx.jsonSPDX SBOM (industry standard)
vulnerability.jsonFull vulnerability report
policy-evaluation.jsonPolicy evaluation results
content.jsonPackage and file content listing
image-metadata.jsonImage metadata (digest, distro, layers)

b) Centralized Mode

This method uses the “analyzer” pods in the Anchore Enterprise deployment to build the SBOM. This can create queuing if there are not enough analyzer processes, and this method will require the operator to provide ECR registry credentials in the Enterprise backend. This method may be preferred in cases where the Anchore Enterprise operator does not control the image build process (the analyzers can simply poll registries to look for new image builds as they are pushed), and this method also allows the operator to simply queue up the image for asynchronous scanning later if vulnerability and policy results are not required immediately. If the user wants malware scanning results from Anchore Enterprise’s clamav integration, the Centralized Scanning method is required.

To use centralized mode, replace the post_build commands in the buildspec above with the following. Note that --from docker is removed, so Anchore Enterprise will pull the image from the registry after it is pushed.

  post_build:
    commands:
      ### push the image first so Anchore Enterprise can pull it from the registry
      - docker push ${IMAGE_URI}
      ### queue the image for scanning by Anchore Enterprise analyzers
      ### --no-auto-subscribe prevents automatic re-scanning on future tag updates
      - |
        anchorectl image add ${IMAGE_URI} --no-auto-subscribe --wait --get all=./ \
          --annotation "ci=aws-codepipeline" \
          --annotation "repo=${IMAGE_REPO_NAME}" \
          --annotation "commit=${CODEBUILD_RESOLVED_SOURCE_VERSION}" \
          --annotation "commit_short=${IMAGE_TAG}" \
          --annotation "pipeline=${CODEBUILD_INITIATOR}" \
          --annotation "region=${AWS_REGION}"
      ### evaluate the image against your Anchore Enterprise policy
      - anchorectl image check ${IMAGE_URI} --fail-based-on-results --detail
      - printf '[{"name":"app","imageUri":"%s"}]' "${IMAGE_URI}" > imagedefinitions.json

6. Create the CodePipeline

The pipeline has two stages: a Source stage that pulls from CodeCommit on each commit to the main branch, and a Build stage that runs the CodeBuild project.

cat > pipeline.json <<EOF
{
  "pipeline": {
    "name": "$CODEPIPELINE_NAME",
    "roleArn": "$CODEPIPELINE_ROLE_ARN",
    "artifactStore": {
      "type": "S3",
      "location": "$ARTIFACT_BUCKET"
    },
    "stages": [
      {
        "name": "Source",
        "actions": [
          {
            "name": "Source",
            "actionTypeId": {
              "category": "Source",
              "owner": "AWS",
              "provider": "CodeCommit",
              "version": "1"
            },
            "runOrder": 1,
            "configuration": {
              "RepositoryName": "$CODECOMMIT_REPO_NAME",
              "BranchName": "main",
              "PollForSourceChanges": "false"
            },
            "outputArtifacts": [
              {
                "name": "SourceOutput"
              }
            ],
            "inputArtifacts": []
          }
        ]
      },
      {
        "name": "Build",
        "actions": [
          {
            "name": "Build",
            "actionTypeId": {
              "category": "Build",
              "owner": "AWS",
              "provider": "CodeBuild",
              "version": "1"
            },
            "runOrder": 1,
            "configuration": {
              "ProjectName": "$CODEBUILD_PROJECT_NAME"
            },
            "inputArtifacts": [
              {
                "name": "SourceOutput"
              }
            ],
            "outputArtifacts": [
              {
                "name": "BuildOutput"
              }
            ]
          }
        ]
      }
    ],
    "version": 1
  }
}
EOF

aws codepipeline create-pipeline \
  --region "$AWS_REGION" \
  --cli-input-json file://pipeline.json

7. Run the Pipeline

Start the pipeline manually:

aws codepipeline start-pipeline-execution \
  --region "$AWS_REGION" \
  --name "$CODEPIPELINE_NAME"

8. View Results

When the pipeline completes, view the build results in the AWS Console under CodeBuild > Build history > select your build > Build logs. The logs will display the anchorectl output including vulnerability counts and policy evaluation results.

The scan artifacts (SBOMs, vulnerability report, policy evaluation) are stored as build artifacts in the S3 artifact bucket. You can download them from CodePipeline > select your pipeline > BuildOutput artifact, or directly from the S3 bucket.

2.2 - GitLab

Requirements

  1. Anchore Enterprise is deployed in your environment, with the API accessible from your GitLab CI environment.
  2. Credentials for your GitLab Container Registry are added to Anchore Enterprise, under the Anchore account that you intend to use with GitLab CI. See Container Registries. For information on what registry/credentials must be added to allow Anchore Enterprise to access your GitLab Container Registry, see https://docs.gitlab.com/ee/user/packages/container_registry/.

1. Configure Variables

Ensure that the following variables are set in your GitLab repository (settings -> CI/CD -> Variables -> Expand -> Add variable) or GitLab Group:

ANCHORECTL_USERNAME
ANCHORECTL_PASSWORD (masked)
ANCHORECTL_URL

Set Variables

2. Create config file

Create a new file in your repository. Name the file .gitlab-ci.yml.

Set Variables

3. Configure scanning mode

a) Distributed Mode

This is the most easily scalable method for scanning images. Distributed scanning uses the anchorectl utility to build the SBOM directly on the build runner and then pushes the SBOM to Anchore Enterprise through the API. To use this scanning method, paste the following workflow script into your new .gitlab-ci.yml file. After building the image from your Dockerfile and scanning it with anchorectl, this workflow will display vulnerabilities and policy results in the build log. After pasting, click “Commit changes” to save the new file.

### Anchore Distributed Scan
  # you will need three variables defined:
  # ANCHORECTL_USERNAME
  # ANCHORECTL_PASSWORD
  # ANCHORECTL_URL

image: docker:latest
services:
- docker:dind
stages:
- build
- anchore
variables:
  ### set this to true if you want the result of the policy check to determine whether the job succeeds or not
  ANCHORECTL_FAIL_BASED_ON_RESULTS: "false"
  ANCHORE_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG}

Build:
  stage: build
  script:
    ### build and push docker image
    - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
    - docker build -t ${ANCHORE_IMAGE} .
    - docker push ${ANCHORE_IMAGE}

Anchore:
  stage: anchore
  before_script:
    ### install anchorectl binary
    - apk add --no-cache curl
    - 'curl "$ANCHORECTL_URL/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx anchorectl && mv -v anchorectl /usr/bin && chmod +x /usr/bin/anchorectl && /usr/bin/anchorectl version'
    - export PATH="${HOME}/.local/bin/:${PATH}"
  script:
    ### provide registry credentials for anchorectl
    - export ANCHORECTL_REGISTRY_AUTH_AUTHORITY=$CI_REGISTRY
    - export ANCHORECTL_REGISTRY_AUTH_USERNAME="$CI_REGISTRY_USER"
    - export ANCHORECTL_REGISTRY_AUTH_PASSWORD="$CI_REGISTRY_PASSWORD"
    ### scan image and push to anchore enterprise
    - anchorectl image add --no-auto-subscribe --wait --dockerfile ./Dockerfile --from registry ${ANCHORE_IMAGE}
    ### then get the results:
    - anchorectl image vulnerabilities ${ANCHORE_IMAGE}
    - anchorectl image check --detail ${ANCHORE_IMAGE}

b) Centralized Mode

This method uses the “analyzer” pods in the Anchore Enterprise deployment to build the SBOM. This can create queuing if there are not enough analyzer processes, and this method will require the operator to provide registry credentials in the Enterprise backend (if the images to be scanned are in private registries). This method may be preferred in cases where the Anchore Enterprise operator does not control the image build process (the analyzers can simply poll registries to look for new image builds as they are pushed), and this method also allows the operator to simply queue up the image for asynchronous scanning later if vulnerability and policy results are not required immediately. If the user wants malware scanning results from Anchore Enterprise’s clamav integration, the Centralized Scanning method is required. To use this scanning method, paste the following workflow script into your new .gitlab-ci.yml file. After building the image from your Dockerfile,, this workflow will tell Anchore Enterprise to scan the image, then it will display the vulnerability and policy results in the build log. After pasting, click “Commit changes” to save the new file.

### Anchore Centralized Scan
  # you will need three variables defined:
  # ANCHORECTL_USERNAME
  # ANCHORECTL_PASSWORD
  # ANCHORECTL_URL

image: docker:latest
services:
- docker:dind
stages:
- build
- anchore
variables:
  ANCHORECTL_FAIL_BASED_ON_RESULTS: "false"
  ANCHORE_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG}

Build:
  stage: build
  script:
    ### build and push docker image
    - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
    - docker build -t ${ANCHORE_IMAGE} .
    - docker push ${ANCHORE_IMAGE}

Anchore:
  stage: anchore
  before_script:
    ### install anchorectl binary
    - apk add --no-cache curl
    - 'curl "$ANCHORECTL_URL/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx anchorectl && mv -v anchorectl /usr/bin && chmod +x /usr/bin/anchorectl && /usr/bin/anchorectl version'
    - export PATH="${HOME}/.local/bin/:${PATH}"
  script:
    ### note that private registries will require registry credentials to be configured in your Anchore deployment
    ### queue image for scanning
    - anchorectl image add --no-auto-subscribe --wait --dockerfile ./Dockerfile ${ANCHORE_IMAGE} 
    ### then get the results:
    - anchorectl image vulnerabilities ${ANCHORE_IMAGE}
    - anchorectl image check --detail ${ANCHORE_IMAGE}

4. View pipeline

Gitlab will automatically start a pipeline. Navigate to “Build” -> “Pipelines” and then on your running pipeline.

Set Variables

5. View output

Once the build is complete, click on the “anchore” stage and view the output of the job. You will see the results of the vulnerability match and policy evaluation in the output.

2.3 - Azure Pipelines

Anchore Enterprise can be integrated into Azure DevOps pipelines to generate and analyze SBOMs, perform vulnerability scanning, and enforce policy evaluation as a pipeline gate. This page covers two integration approaches: distributed analysis and centralized analysis.


Prerequisites

The following are required for both integration approaches:

  • A running Anchore Enterprise instance. See Deployment for setup instructions.
  • An Azure DevOps pipeline.
  • An Azure Key Vault variable group named anchoreCredentials containing your Anchore Enterprise credentials. The following variables are required:
VariableDescription
anchore_urlThe URL of your Anchore Enterprise instance
anchore_endpointThe hostname of your Anchore Enterprise instance (used to download AnchoreCTL)
anchore_userYour Anchore username, or _api_key if using an API key
anchore_passYour Anchore password or API key value

Distributed Analysis

In distributed analysis, AnchoreCTL generates the SBOM locally on the pipeline agent and uploads it to Anchore Enterprise for vulnerability matching and policy evaluation. The image is not required to be in a remote registry before scanning.

This is the recommended approach for most pipelines. It requires less infrastructure than centralized analysis and avoids the need for a staging registry.

How It Works

The anchorectl image add command accepts a --from flag that specifies the source from which AnchoreCTL should generate the SBOM:

  • --from docker:<image> — generates the SBOM from a locally available Docker image on the pipeline agent.
  • --from registry — pulls the image from a remote registry for local analysis. Use this when the image has already been pushed to a registry in a prior pipeline step, as it captures the registry-assigned digest, which remains consistent as the image moves through environments.

The first positional argument to image add is the tag Anchore Enterprise uses to identify the image in its database. This does not need to be a pullable registry path.

Distributed Pipeline

trigger:
- master

resources:
- repo: self

variables:
- name: imageRef
  value: 'production/simpleserver:$(Build.BuildId)'
- group: anchoreCredentials

stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - task: Docker@2
      displayName: Build image
      inputs:
        command: build
        repository: simpleserver
        dockerfile: Dockerfile
        tags: |
          $(Build.BuildId)

- stage: Security
  displayName: Security scan stage
  dependsOn: Build
  jobs:
  - job: Security
    displayName: Security
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: curl -X GET "https://$(anchore_endpoint)/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx anchorectl
      displayName: Install AnchoreCTL

    - script: |
        export PATH=$PATH:$HOME/.local/bin
        export ANCHORECTL_URL=$(anchore_url)
        export ANCHORECTL_USERNAME=$(anchore_user)
        export ANCHORECTL_PASSWORD=$(anchore_pass)
        # To authenticate with an API key instead:
        # export ANCHORECTL_USERNAME=_api_key
        # export ANCHORECTL_PASSWORD=$(api_token)
        ./anchorectl image add $(imageRef) --from docker:simpleserver:$(Build.BuildId) --dockerfile Dockerfile --wait
        ./anchorectl image vulnerabilities $(imageRef)
        ./anchorectl image check $(imageRef) --fail-based-on-results
      displayName: Anchore Security Scan

- stage: Production
  displayName: Production stage
  dependsOn: Security
  # Push the image to your production registry and deploy

Centralized Analysis

In centralized analysis, the image is pushed to a staging registry and Anchore Enterprise pulls and analyzes it directly using the analyzer service. The SBOM is stored in Anchore Enterprise and available for post-scan reporting, compliance auditing, and policy justification.

This approach is required when malware scanning is enabled. See Malware Scanning for configuration details. Note that enabling malware scanning increases overall scan time.

Providing the Dockerfile via --dockerfile also enables Dockerfile-specific policy checks, such as validating the effective user ID or flagging exposed ports.

Additional Prerequisites

The following are required in addition to the common prerequisites:

  • A staging registry. Images are pushed here before scanning and promoted to production only after passing policy evaluation. The example below provisions an Azure Container Registry using Terraform:

    terraform {
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~> 4.0"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
    resource "azurerm_resource_group" "blog" {
      name     = "blog"
      location = "West US"
    }
    
    resource "azurerm_container_registry" "blog" {
      name                = "staging"
      resource_group_name = azurerm_resource_group.blog.name
      location            = azurerm_resource_group.blog.location
      sku                 = "Standard"
      admin_enabled       = true
    }
    

    Note: admin_enabled = true enables the ACR built-in admin account, which uses a single shared credential and cannot be scoped or audited per consumer. For production use, set admin_enabled = false and grant access using a service principal or managed identity with the AcrPull and AcrPush roles as appropriate. See Azure Container Registry authentication options for details.

  • An Azure DevOps service connection. Required for the pipeline to push images to the staging registry. Configure a Docker Registry service connection targeting your Azure Container Registry. See Azure DevOps service connections for instructions.

  • Registry credentials in Anchore Enterprise. Anchore Enterprise must be able to pull images from the staging registry. See Container Registries for instructions.

Centralized Pipeline

trigger:
- master

resources:
- repo: self

variables:
- name: stagedImage
  value: 'staging/simpleserver:$(Build.BuildId)'
- name: productionImage
  value: 'production/simpleserver:$(Build.BuildId)'
- group: anchoreCredentials

stages:
- stage: Build
  displayName: Build and push to staging
  # Build and push the image to the staging registry

- stage: Security
  displayName: Security scan stage
  dependsOn: Build
  jobs:
  - job: Security
    displayName: Security
    pool:
      vmImage: 'ubuntu-latest'
    steps:
    - script: curl -X GET "https://$(anchore_endpoint)/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx anchorectl
      displayName: Install AnchoreCTL

    - script: |
        export PATH=$PATH:$HOME/.local/bin
        export ANCHORECTL_URL=$(anchore_url)
        export ANCHORECTL_USERNAME=$(anchore_user)
        export ANCHORECTL_PASSWORD=$(anchore_pass)
        ./anchorectl image add $(stagedImage) --dockerfile Dockerfile --wait
        ./anchorectl image vulnerabilities $(stagedImage)
        ./anchorectl image check $(stagedImage) --fail-based-on-results
      displayName: Anchore Security Scan

- stage: Production
  displayName: Production stage
  dependsOn: Security
  # Push the image to your production registry and deploy

Failing a Pipeline on Policy Evaluation

The --fail-based-on-results flag (shorthand: -f) on anchorectl image check causes AnchoreCTL to return a non-zero exit code when the policy evaluation result is stop. This fails the pipeline stage and prevents the image from being promoted.

anchorectl image check <image> --fail-based-on-results

Example output for a failed evaluation:

 ✔ Evaluated against policy                  [failed]
Tag: docker.io/anchore/test_images:convertigo-7.9.2
Digest: sha256:b649023ebd9751db65d2f9934e3cfeeee54a010d4ba90ebaab736100a1c34d7d
Policy ID: anchore_secure_default
Last Evaluation: 2026-02-20T17:19:26Z
Evaluation: fail
Final Action: stop
Reason: policy_evaluation
error: 1 error occurred:
        * failed policies:

One-Time Analysis

Anchore Enterprise supports one-time analysis, which performs vulnerability scanning and policy evaluation without storing the SBOM. This is useful for quick feedback during development before pushing to a registry.

For details, see the One-Time Scan documentation or contact the customer success team via the Support Portal.


Next Steps

2.4 - GitHub

Image Scanning can be easily integrated into your GitHub Actions pipeline using anchorectl.

1. Configure Variables

Ensure that the following variables/secrets are set in your GitHub repository (repository settings -> secrets and variables -> actions):

  • Variable ANCHORECTL_URL
  • Variable ANCHORECTL_USERNAME
  • Secret ANCHORECTL_PASSWORD

These are necessary for the integration to access your Anchore Enterprise deployment. The ANCHORECTL_PASSWORD value should be created as a repository secret to prevent exposure of the value in job logs, while ANCHORECTL_URL and ANCHORECTL_USERNAME can be created as repository variables.

Set Variables

2. Configure Permissions

(“Settings” -> “Actions” -> “General” -> “Workflow permissions”) select “Read and write permissions” and click “Save”.

Set Variables

3. Create config file

In your repository, create a new file ( “Add file” -> “Create new file”) and name it .github/workflows/anchorectl.yaml.

Set Variables

4. Set scanning mode

a) Distributed Mode

This is the most easily scalable method for scanning images. Distributed scanning uses the anchorectl utility to build the SBOM directly on the build runner and then pushes the SBOM to Anchore Enterprise through the API. To use this scanning method, paste the following workflow script into your new anchorectl.yaml file. After building the image from your Dockerfile and scanning it with anchorectl, this workflow will display vulnerabilities and policy results in the build log.

name: Anchore Enterprise Distributed Scan

on:
  workflow_dispatch:
    inputs:
      mode:
        description: 'On-Demand Build'  
        
env:
  ANCHORECTL_URL: ${{ vars.ANCHORECTL_URL }}
  ANCHORECTL_USERNAME: ${{ vars.ANCHORECTL_USERNAME }}
  ANCHORECTL_PASSWORD: ${{ secrets.ANCHORECTL_PASSWORD }}
  ## set ANCHORECTL_FAIL_BASED_ON_RESULTS to true if you want to break the pipeline based on the evaluation
  ANCHORECTL_FAIL_BASED_ON_RESULTS: false
  REGISTRY: ghcr.io
     
jobs:
  Build:
    runs-on: ubuntu-latest
    steps:
    
    - name: "Set IMAGE environmental variables"
      run: |
        echo "IMAGE=${REGISTRY}/${GITHUB_REPOSITORY}:${GITHUB_REF_NAME}" >> $GITHUB_ENV
        
    - name: Checkout Code
      uses: actions/checkout@v3
      
    - name: Log in to the Container registry
      uses: docker/login-action@v2
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}      
      
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2


    - name: build local container
      uses: docker/build-push-action@v3
      with:
        tags: ${{ env.IMAGE }}
        push: true
        load: false


  Anchore:
    runs-on: ubuntu-latest
    needs: Build
    steps:
    
    - name: "Set IMAGE environmental variables"
      run: |
        echo "IMAGE=${REGISTRY}/${GITHUB_REPOSITORY}:${GITHUB_REF_NAME}" >> $GITHUB_ENV
        
    - name: Checkout Code
      ### only need to do this if you want to pass the dockerfile to Anchore during scanning
      uses: actions/checkout@v3
        
    - name: Install Latest anchorectl Binary
      run: |
        mkdir -p $HOME/.local/bin
        curl -sSfL "${ANCHORECTL_URL}/v2/system/anchorectl?operating_system=linux&architecture=amd64" \
            -H "accept: /" | tar -zx -C $HOME/.local/bin anchorectl
        echo "$HOME/.local/bin" >> $GITHUB_PATH
            
    - name: Generate SBOM and Push to Anchore
      run: |        
        anchorectl image add --no-auto-subscribe --wait --from registry --dockerfile Dockerfile ${IMAGE}
        
    - name: Pull Vulnerability List
      run: |
        anchorectl image vulnerabilities ${IMAGE} 
        
    - name: Pull Policy Evaluation
      run: |
        # set "ANCHORECTL_FAIL_BASED_ON_RESULTS=true" (see above in the "env:" section) to break the pipeline here if the 
        # policy evaluation returns FAIL or add -f, --fail-based-on-results to this command for the same result
        #
        anchorectl image check --detail ${IMAGE}

b) Centralized Mode

This method uses the “analyzer” pods in the Anchore Enterprise deployment to build the SBOM. This can create queuing if there are not enough analyzer processes, and this method may require the operator to provide registry credentials in the Enterprise backend (if the images to be scanned are in private registries). This method may be preferred in cases where the Anchore Enterprise operator does not control the image build process (the analyzers can simply poll registries to look for new image builds as they are pushed), and this method also allows the operator to simply queue up the image for asynchronous scanning later if vulnerability and policy results are not required immediately. If the user wants malware scanning results from Anchore Enterprise’s clamav integration, the Centralized Scanning method is required. To use this scanning method, paste the following workflow script into your new anchorectl.yaml file. After building the image from your Dockerfile,, this workflow will tell Anchore Enterprise to scan the image, then it will display the vulnerability and policy results in the build log.

name: Anchore Enterprise Centralized Scan

on:
  workflow_dispatch:
    inputs:
      mode:
        description: 'On-Demand Build'  

env:
  ANCHORECTL_URL: ${{ vars.ANCHORECTL_URL }}
  ANCHORECTL_USERNAME: ${{ vars.ANCHORECTL_USERNAME }}
  ANCHORECTL_PASSWORD: ${{ secrets.ANCHORECTL_PASSWORD }}
  ## set ANCHORECTL_FAIL_BASED_ON_RESULTS to true if you want to break the pipeline based on the evaluation
  ANCHORECTL_FAIL_BASED_ON_RESULTS: false
  REGISTRY: ghcr.io

jobs:

  Build:
    runs-on: ubuntu-latest
    steps:
    
    - name: "Set IMAGE environmental variables"
      run: |
        echo "IMAGE=${REGISTRY}/${GITHUB_REPOSITORY}:${GITHUB_REF_NAME}" >> $GITHUB_ENV
        
    - name: Checkout Code
      uses: actions/checkout@v3
      
    - name: Log in to the Container registry
      uses: docker/login-action@v2
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}      
      
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2


    - name: build local container
      uses: docker/build-push-action@v3
      with:
        tags: ${{ env.IMAGE }}
        push: true
        load: false

  Anchore:
    runs-on: ubuntu-latest
    needs: Build

    steps:
    
    - name: "Set IMAGE environmental variables"
      run: |
        echo "IMAGE=${REGISTRY}/${GITHUB_REPOSITORY}:${GITHUB_REF_NAME}" >> $GITHUB_ENV
        
    - name: Checkout Code
      uses: actions/checkout@v3
        
    - name: Install Latest anchorectl Binary
      run: |
        mkdir -p $HOME/.local/bin
        curl -sSfL "${ANCHORECTL_URL}/v2/system/anchorectl?operating_system=linux&architecture=amd64" \
            -H "accept: /" | tar -zx -C $HOME/.local/bin anchorectl
        echo "$HOME/.local/bin" >> $GITHUB_PATH
    
    - name: Queue Image for Scanning by Anchore Enterprise
      run: |        
       anchorectl image add --no-auto-subscribe --wait --dockerfile ./Dockerfile ${IMAGE} 
                
    - name: Pull Vulnerability List
      run: |
        anchorectl image vulnerabilities ${IMAGE} 
        
    - name: Pull Policy Evaluation
      run: |
        # set "ANCHORECTL_FAIL_BASED_ON_RESULTS=true" (see above in the "env:" section) to break the pipeline here if the 
        # policy evaluation returns FAIL or add -f, --fail-based-on-results to this command for the same result
        #
        anchorectl image check --detail ${IMAGE}

5. Run Workflow

Go to “Actions” -> “Anchore Enterprise with anchorectl” and hit “Run workflow”.

Set Variables

6. View Results

When the workflow completes, view the results by clicking on the workflow name (“Anchore Enterprise with anchorectl”), then on the job (“Anchore”), then expand the “Pull Vulnerability List” and/or “Pull Policy Evaluation” steps to see the details.

Set Variables

7. Notifications

You can also integrate your Anchore Enterprise deployment with the GitHub API so that Anchore Enterprise notifications are sent to GitHub Notifications as new issues in a repository.

To configure and enable this please review the GitHub Notifications documentation.

2.5 - Jenkins

Anchore Enterprise can be integrated into Jenkins pipelines to generate and analyze SBOMs, perform vulnerability scanning, and enforce policy evaluation as a pipeline gate.


Configure Variables

Before getting started, you need to configure your Jenkins instance with the required credentials. Make sure the following values are added under Dashboard → Manage Jenkins → Credentials as credentials of type Secret text:

ANCHORECTL_USERNAME 
ANCHORECTL_PASSWORD
ANCHORECTL_URL

These are necessary for the integration to access your Anchore Enterprise deployment. The ANCHORECTL_PASSWORD value should be created as a repository secret to prevent exposure of the value in job logs, while ANCHORECTL_URL and ANCHORECTL_USERNAME can be created as repository variables.

Configure Jenkins credentials


Add a Pipeline Example to Jenkins

Each groovy block below is a complete declarative pipeline. Use one as a starting point in either of the following ways:

  • Pipeline script. Create or open a Jenkins Pipeline job, then under Pipeline → Definition select Pipeline script and paste the example directly into the script text area.
  • Pipeline script from SCM. Save the example as a Jenkinsfile in your repository, then under Pipeline → Definition select Pipeline script from SCM, point it at that repository, and set the Script Path to the file’s location (Jenkinsfile at the repository root by default).

Either way, the parameters block at the top of each example (REGISTRY, APP_NAME, and so on) surfaces as build-time inputs on the job’s Build with Parameters page, so you don’t need to edit the script to point it at a different image or app version.


Configure Scanning Mode

Anchore Enterprise exposes two scopes for scanning and policy evaluation. Pick the one that matches how your team organizes software.

ScopeWhat is scannedTypical use
App-ScopedEvery asset attached to an app version — container images, analyzed filesystems, or externally supplied SBOMsAggregated, deduplicated vulnerability and policy results across an app version; the v6-native path
Image-ScopedA single container image, identified by digestAd-hoc image checks, image-stage CI gates, and any workflow that has not yet adopted the apps, versions, and assets model

App-Scoped Pipelines

You can also manage apps, versions, and assets directly from your pipeline. The pipeline creates or reuses an app and an app version, attaches the build output to the version as an asset, and performs the analysis. See Managing Assets in an App Version to learn more about apps.

Bring Your Own SBOM (BYOS) Asset

This pipeline creates or reuses an app and an app version, then uploads an existing SBOM — from a vendor, a different SCA tool, AnchoreCTL/Syft, or an earlier stage in this same pipeline — and attaches it to the version as an asset, before reporting vulnerabilities and evaluating policy for the app version.

pipeline {

    agent any

    // Define parameters for user input
    parameters {
        string(name: 'APP_NAME', defaultValue: 'storefront', description: 'The app to attach the SBOM to.', trim: true)
        string(name: 'APP_VERSION', defaultValue: '1.0.0', description: 'The app version to update.', trim: true)
        string(name: 'SBOM_FILE', defaultValue: 'sbom.json', description: 'Path to an existing SBOM file to upload.', trim: true)
        choice(name: 'ANCHORECTL_QUIET', choices: ['true', 'false'], description: 'Suppress anchorectl informational messages.')
        choice(name: 'ANCHORECTL_FAIL_BASED_ON_RESULTS', choices: ['true', 'false'], description: 'How to handle fail signals (e.g., policy check outcomes)')
    }

    stages {
        stage('Anchore App Update') {
            environment {
              // This is the AnchoreCTL service endpoint (fetched securely from Jenkins credentials)
                ANCHORECTL_URL = credentials('ANCHORECTL_URL')
              // Define the Anchore account username
                ANCHORECTL_USERNAME = credentials('ANCHORECTL_USERNAME')

              // Define the Anchore account password
                ANCHORECTL_PASSWORD = credentials('ANCHORECTL_PASSWORD')

              // Whether to fail the pipeline based on the app version policy evaluation (controlled by Jenkins parameter)
                ANCHORECTL_FAIL_BASED_ON_RESULTS = "${params.ANCHORECTL_FAIL_BASED_ON_RESULTS}"

              // You can also choose to Suppress unnecessary output logs
                ANCHORECTL_QUIET = "${params.ANCHORECTL_QUIET}"

              // Make the downloaded anchorectl binary available to every step
                PATH = "${env.HOME}/.local/bin:${env.PATH}"
            }
            steps {
                script {
                    echo 'Starting app version update pipeline.'

                    // Download and configure the Anchore CLI
                    sh 'mkdir -p $HOME/.local/bin && curl -sSfL "${ANCHORECTL_URL%/}/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx -C $HOME/.local/bin anchorectl'

                    // Create the app if it does not exist yet
                    sh "anchorectl app get '${params.APP_NAME}' -o id > /dev/null 2>&1 || anchorectl app add '${params.APP_NAME}' --contact-name 'Platform Team'"

                    // Create the app version if it does not exist yet
                    sh "anchorectl app version get '${params.APP_VERSION}' --app '${params.APP_NAME}' -o id > /dev/null 2>&1 || anchorectl app version add '${params.APP_VERSION}' --app '${params.APP_NAME}' --status in_progress"

                    // Upload the existing SBOM and attach it to the app version as an asset
                    sh "anchorectl app version asset add sbom '${params.SBOM_FILE}' --app '${params.APP_NAME}' --version '${params.APP_VERSION}' --asset '${params.SBOM_FILE}' --wait"

                    // Retrieve and archive the vulnerability report for the app version
                    sh "anchorectl app version vuln list '${params.APP_VERSION}' --app '${params.APP_NAME}' | tee app-vulnerabilities.txt"
                    archiveArtifacts artifacts: 'app-vulnerabilities.txt'

                    // Run and archive the app version policy evaluation (fails the build when ANCHORECTL_FAIL_BASED_ON_RESULTS is true and the evaluation result is 'fail')
                    sh """#!/bin/bash
                        set -o pipefail
                        anchorectl app version policy status get '${params.APP_VERSION}' --app '${params.APP_NAME}' | tee app-policy-status.txt
                    """
                    archiveArtifacts artifacts: 'app-policy-status.txt'
                }
            }
        }
    }
}

Container Image Asset

To attach a container image instead of an SBOM file, keep the same app/app-version creation, vulnerability retrieval, and policy evaluation steps as the BYOS example above, and swap only the parameters and the asset-add step:

  • Replace the SBOM_FILE parameter with image parameters (REGISTRY, REPOSITORY, TAG), as in the Image-Scoped Pipelines examples.
  • Replace the asset-add step with the container-image asset type, which has AnchoreCTL scan the image before attaching it:
// Scan the image and attach it to the app version as an asset
sh "anchorectl app version asset add container-image '${params.REGISTRY}/${params.REPOSITORY}:${params.TAG}' --app '${params.APP_NAME}' --version '${params.APP_VERSION}' --asset '${params.REPOSITORY}:${params.TAG}' --wait"

Image-Scoped Pipelines

Below are examples of the types of image scans. For a detailed explanation of their differences, refer to the Images concept page.

Distributed

This is the most easily scalable method for scanning images. Distributed scanning uses the anchorectl utility to build the SBOM directly on the build runner and then pushes the SBOM to Anchore Enterprise through the API. The example below demonstrates how to automate distributed analysis within a pipeline.

pipeline {
        
    // Define parameters for user input
    parameters {
        string(name: 'REGISTRY', defaultValue: 'docker.io', description: 'The container registry to use.', trim: true)
        string(name: 'REPOSITORY', defaultValue: 'library/nginx', description: 'The image repository path.', trim: true)
        string(name: 'TAG', defaultValue: 'latest', description: 'The image tag to analyze.', trim: true)
        choice(name: 'ANCHORECTL_QUIET', choices: ['true', 'false'], description: 'Suppress anchorectl informational messages.')
        choice(name: 'ANCHORECTL_FORMAT', choices: ['json', 'csv'], description: 'The output format for anchorectl (e.g., json, csv).')
        choice(name: 'ANCHORECTL_FAIL_BASED_ON_RESULTS', choices: ['true', 'false'], description: 'How to handle fail signals (e.g., policy check outcomes)')
    }

    stages {
        stage('Anchore Image Scan') {
            environment {
              // This is the AnchoreCTL service endpoint (fetched securely from Jenkins credentials)
                ANCHORECTL_URL = credentials('ANCHORECTL_URL')
              // Define the Anchore account username
                ANCHORECTL_USERNAME = credentials('ANCHORECTL_USERNAME')

              // Define the Anchore account password
                ANCHORECTL_PASSWORD = credentials('ANCHORECTL_PASSWORD')

              // Whether to fail the pipeline based on AnchoreCTL scan results (controlled by Jenkins parameter)
                ANCHORECTL_FAIL_BASED_ON_RESULTS = "${params.ANCHORECTL_FAIL_BASED_ON_RESULTS}"

              // You can also choose to Suppress unnecessary output logs
                ANCHORECTL_QUIET = "${params.ANCHORECTL_QUIET}"

              // Define the Output format for AnchoreCTL results
                ANCHORECTL_FORMAT = "${params.ANCHORECTL_FORMAT}"

              // Make the downloaded anchorectl binary available to every step
                PATH = "${env.HOME}/.local/bin:${env.PATH}"
            }
            steps {
                script {
                    echo 'Starting image analysis pipeline.'
                    
                    // Download and configure the Anchore CLI
                    sh 'mkdir -p $HOME/.local/bin && curl -sSfL "${ANCHORECTL_URL%/}/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx -C $HOME/.local/bin anchorectl'

                    // Add the image to Anchore and wait for analysis to complete
                    sh "anchorectl image add --wait --from registry ${params.REGISTRY}/${params.REPOSITORY}:${params.TAG}"

                    // Retrieve and archive vulnerability report
                    sh "anchorectl image vulnerabilities ${params.REGISTRY}/${params.REPOSITORY}:${params.TAG} | tee vulnerabilities.${ANCHORECTL_FORMAT}"
                    archiveArtifacts artifacts: "vulnerabilities.${env.ANCHORECTL_FORMAT}"
                    
                    // Run and archive the policy check
                    sh """#!/bin/bash
                        set -o pipefail
                        anchorectl image check --detail ${params.REGISTRY}/${params.REPOSITORY}:${params.TAG} | tee policy-check.${ANCHORECTL_FORMAT}
                    """
                    archiveArtifacts artifacts: "policy-check.${env.ANCHORECTL_FORMAT}"

                    // Post-build action to handle policy failure, if configured
                    if (env.ANCHORECTL_FAIL_BASED_ON_RESULTS == 'true') {
                        def policyCheckResult = sh(script: "grep -q 'Policy Evaluation: PASS' policy-check.${ANCHORECTL_FORMAT}", returnStatus: true)
                        if (policyCheckResult != 0) {
                            error('Policy check failed based on results.')
                        }
                    }
                }
            }
        }
    }
}

Centralized

Centralized Scanning uses analyzer pods in Anchore Enterprise to generate the SBOM. This method is ideal when the operator does not control the image build process, supports asynchronous scanning, and is required for malware detection through ClamAV. After your container image is built, you can trigger a scan by adding the provided stage to your pipeline, which will instruct Anchore Enterprise to analyze the image and display vulnerability and policy results in the build log. Below is an example of how to achieve centralized scanning in your pipeline

pipeline {
    
    // Define parameters for user input
    parameters {
        string(name: 'REGISTRY', defaultValue: 'docker.io', description: 'The container registry to use.', trim: true)
        string(name: 'REPOSITORY', defaultValue: 'library/nginx', description: 'The image repository path.', trim: true)
        string(name: 'TAG', defaultValue: 'latest', description: 'The image tag to analyze.', trim: true)
        choice(name: 'ANCHORECTL_QUIET', choices: ['true', 'false'], description: 'Suppress anchorectl informational messages.')
        choice(name: 'ANCHORECTL_FORMAT', choices: ['json', 'csv'], description: 'The output format for anchorectl (e.g., json, csv).')
        choice(name: 'ANCHORECTL_FAIL_BASED_ON_RESULTS', choices: ['true', 'false'], description: 'How to handle fail signals (e.g., policy check outcomes)')
    }

    stages {
        stage('Anchore Image Scan') {
            environment {
                // This is the AnchoreCTL service endpoint (fetched securely from Jenkins credentials)
                ANCHORECTL_URL = credentials('ANCHORECTL_URL')
              // Define the Anchore account username
                ANCHORECTL_USERNAME = credentials('ANCHORECTL_USERNAME')

              // Define the Anchore account password
                ANCHORECTL_PASSWORD = credentials('ANCHORECTL_PASSWORD')

              // Whether to fail the pipeline based on AnchoreCTL scan results (controlled by Jenkins parameter)
                ANCHORECTL_FAIL_BASED_ON_RESULTS = "${params.ANCHORECTL_FAIL_BASED_ON_RESULTS}"

              // You can also choose to Suppress unnecessary output logs
                ANCHORECTL_QUIET = "${params.ANCHORECTL_QUIET}"

              // Define the Output format for AnchoreCTL results
                ANCHORECTL_FORMAT = "${params.ANCHORECTL_FORMAT}"

              // Make the downloaded anchorectl binary available to every step
                PATH = "${env.HOME}/.local/bin:${env.PATH}"
            }
            steps {
                script {
                    echo "Starting image analysis for: ${params.REGISTRY}/${params.REPOSITORY}:${params.TAG}"

                    // Download and configure the Anchore CLI
                    sh 'mkdir -p $HOME/.local/bin && curl -sSfL "${ANCHORECTL_URL%/}/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx -C $HOME/.local/bin anchorectl'

                    // Add the image to Anchore and wait for analysis to complete
                    sh "anchorectl image add --wait ${params.REGISTRY}/${params.REPOSITORY}:${params.TAG}"

                    // Retrieve and archive vulnerability report
                    sh "anchorectl image vulnerabilities ${params.REGISTRY}/${params.REPOSITORY}:${params.TAG} | tee vulnerabilities.${ANCHORECTL_FORMAT}"
                    archiveArtifacts artifacts: "vulnerabilities.${env.ANCHORECTL_FORMAT}"

                    // Run and archive the policy check
                    sh """#!/bin/bash
                        set -o pipefail
                        anchorectl image check --detail ${params.REGISTRY}/${params.REPOSITORY}:${params.TAG} | tee policy-check.${ANCHORECTL_FORMAT}
                    """
                    archiveArtifacts artifacts: "policy-check.${env.ANCHORECTL_FORMAT}"

                    // Post-build action to handle policy failure, if configured
                    if (env.ANCHORECTL_FAIL_BASED_ON_RESULTS == 'true') {
                        def policyCheckResult = sh(script: "grep -q 'Policy Evaluation: PASS' policy-check.${ANCHORECTL_FORMAT}", returnStatus: true)
                        if (policyCheckResult != 0) {
                            error('Policy check failed based on results.')
                        }
                    }
                }
            }
        }
    }
}

One-Time Scan

Use anchorectl image one-time-scan to analyze an image against Anchore Enterprise policies without adding it to the image inventory or persisting its SBOM — a stateless variant of distributed analysis suited to CI pipelines that want fast pass/fail feedback. See One-Time Scan for the underlying mechanics.

pipeline {
    agent any

    stages {
        stage('Anchore One-Time Scan') {
            environment {
                ANCHORECTL_URL      = credentials('ANCHORECTL_URL')
                ANCHORECTL_USERNAME = credentials('ANCHORECTL_USERNAME')
                ANCHORECTL_PASSWORD = credentials('ANCHORECTL_PASSWORD')
                // Make the downloaded anchorectl binary available to every step
                PATH = "${env.HOME}/.local/bin:${env.PATH}"
            }
            steps {
                script {
                    sh 'mkdir -p $HOME/.local/bin && curl -sSfL "${ANCHORECTL_URL%/}/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx -C $HOME/.local/bin anchorectl'

                    sh 'anchorectl image one-time-scan -o json docker.io/library/nginx:latest | tee vulnerabilities.json'
                }
            }
        }
    }
}

Visualize Vulnerabilities with the Warnings NG Plugin

The Jenkins Warnings Next Generation plugin (warnings-ng) can parse anchorectl vulnerability output and surface findings as tracked issues directly in the Jenkins UI — complete with trend graphs, per-build issue counts, and configurable quality gates.

Prerequisites

  • Jenkins Warnings Next Generation plugin installed (Manage Jenkins → Plugins → Available plugins, search for “Warnings Next Generation”)
  • anchorectl available on the build runner (see Configure Variables above)

Supported Output Variants

The anchorectl() tool scans for files matching **/*vulnerabilities*.json and transparently handles all of the following output formats:

CommandOutput Format
anchorectl app version vuln list VERSION --app APP -o jsonAggregated, deduplicated vulnerability list for an app version
anchorectl image one-time-scan -o json IMAGESingle unified envelope (sbom, policyEvaluation, and vulnerabilities in one file)
anchorectl image one-time-scan -o json --output-directory DIR IMAGEStandalone *_vulnerabilities.json file (camelCase keys)
anchorectl image one-time-scan -o json-raw --output-directory DIR IMAGEStandalone *_vulnerabilities.json file (snake_case keys)
anchorectl image vulnerabilities -o json IMAGE > *_vulnerabilities.jsonVulnerability report (camelCase keys) from a previously analyzed image
anchorectl image vulnerabilities -o json-raw IMAGE > *_vulnerabilities.jsonVulnerability report (snake_case keys) from a previously analyzed image

App Version Vuln List

Use anchorectl app version vuln list to visualize the aggregated, deduplicated vulnerability list for an app version — across every asset attached to it. This assumes the app version already has assets attached; see App-Scoped Pipelines above.

pipeline {
    agent any

    // Define parameters for user input
    parameters {
        string(name: 'APP_NAME', defaultValue: 'storefront', description: 'The app to report on.', trim: true)
        string(name: 'APP_VERSION', defaultValue: '1.0.0', description: 'The app version to report on.', trim: true)
    }

    stages {
        stage('Anchore App Version Vuln List') {
            environment {
                ANCHORECTL_URL      = credentials('ANCHORECTL_URL')
                ANCHORECTL_USERNAME = credentials('ANCHORECTL_USERNAME')
                ANCHORECTL_PASSWORD = credentials('ANCHORECTL_PASSWORD')
                // Make the downloaded anchorectl binary available to every step
                PATH = "${env.HOME}/.local/bin:${env.PATH}"
            }
            steps {
                script {
                    sh 'mkdir -p $HOME/.local/bin && curl -sSfL "${ANCHORECTL_URL%/}/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx -C $HOME/.local/bin anchorectl'

                    sh "anchorectl app version vuln list '${params.APP_VERSION}' --app '${params.APP_NAME}' -o json | tee app_vulnerabilities.json"
                }
            }
        }
    }

    post {
        always {
            recordIssues(tools: [anchorectl(pattern: 'app_vulnerabilities.json')])
        }
    }
}

App-scoped vulnerability results in Jenkins

Image Add and Vulnerabilities

Use anchorectl image add to submit an image to Anchore Enterprise for centralized analysis. Once analysis is complete, retrieve the vulnerability report with anchorectl image vulnerabilities and save it to a file the plugin can detect.

pipeline {
    agent any

    stages {
        stage('Anchore Image Scan') {
            environment {
                ANCHORECTL_URL      = credentials('ANCHORECTL_URL')
                ANCHORECTL_USERNAME = credentials('ANCHORECTL_USERNAME')
                ANCHORECTL_PASSWORD = credentials('ANCHORECTL_PASSWORD')
                // Make the downloaded anchorectl binary available to every step
                PATH = "${env.HOME}/.local/bin:${env.PATH}"
            }
            steps {
                script {
                    sh 'mkdir -p $HOME/.local/bin && curl -sSfL "${ANCHORECTL_URL%/}/v2/system/anchorectl?operating_system=linux&architecture=amd64" -H "accept: */*" | tar -zx -C $HOME/.local/bin anchorectl'

                    sh 'anchorectl image add --wait docker.io/library/nginx:latest'

                    sh 'anchorectl image vulnerabilities -o json docker.io/library/nginx:latest > image_vulnerabilities.json'
                }
            }
        }
    }

    post {
        always {
            recordIssues(tools: [anchorectl(pattern: 'image_vulnerabilities.json')])
        }
    }
}

Image-scoped vulnerability results in Jenkins

Severity Mapping

Anchore severity levels are mapped to Jenkins issue severities as follows:

Anchore SeverityJenkins Severity
CriticalERROR
HighWARNING_HIGH
MediumWARNING_NORMAL
Low, NegligibleWARNING_LOW

3.1 - Kubernetes Admission Controller

Kubernetes can be configured to use an Admission Controller to validate that the container image is compliant with the user’s policy before allowing or preventing deployment.

Anchore Enterprise can be integrated with Kubernetes to ensure that only certified images are started within a cluster. The admission controller can be configured to make a webhook call into Anchore Enterprise. Anchore Enterprise exports a Kubernetes-specific API endpoint and will return the pass or fail response in the form of an ImageReview response. This approach allows the Kubernetes system to make the final decision on running a container image and does not require installation of any per-node plugins into Kubernetes.

Using native Kubernetes features allows the admission controller approach to be used in both on-prem and cloud-hosted Kubernetes environments.

Getting Started

Full information on installation and configuration of the Anchore Kubernetes Admission Controller can be found here.

Modes of Operation

The Anchore admission controller supports 3 different modes of operation allowing you to tune the tradeoff between control and intrusiveness for your environments.

Strict Policy-Based Admission Gating Mode

This is the strictest mode, and will admit only images that are already analyzed by Anchore Enterprise and receive a “pass” on policy evaluation. This enables you to ensure, for example, that no image is deployed into the cluster that has a known high-severity CVE with an available fix, or any of several other conditions. The Anchore Enterprise policy language supports sophisticated conditions on the properties of images, vulnerabilities, and metadata.

Analysis-Based Admission Gating Mode

Admit only images that are analyzed and known to Anchore, but do not execute or require a policy evaluation. This is useful in cases where you’d like to enforce requirement that all images be deployed via a CI/CD pipeline, for example, that itself manages the image scanning with Anchore, but allowing the CI/CD process to determine what should run based on other factors outside the context of the image or k8s itself.

Passive Analysis Trigger Mode

Trigger an Anchore analysis of images, but to no block execution on analysis completion or policy evaluation of the image. This is a way to ensure that all images that make it to deployment (test, staging, or prod) are guaranteed to have some form of analysis audit trail available and a presence in reports and notifications that are managed by Anchore. Image records in Anchore are given an annotation of “requestor=anchore-admission-controller” to help track their provenance.

3.2 - Kubernetes Runtime Inventory

Overview

Using Anchore’s runtime inventory agents provides Anchore Enterprise access to what images are being used in your deployments. This can help give insight into where vulnerabilities or policy violations are in your production workloads.

Anchore uses a go binary called anchore-k8s-inventory that leverages the Kubernetes Go SDK to reach out and list containers in a configurable set of namespaces to determine which images are running.

anchore-k8s-inventory can be deployed via its helm chart, embedded within your Kubernetes cluster as an agent. It will require access to the Anchore Enterprise API.

Runtime Inventory Workflow Visualization

General Runtime Configuration

Getting Started

The most common way to track inventory is to install anchore-k8s-inventory as an agent in your cluster. To do this you will need to configure credentials and information about your deployment in the values file. It is recommended to first configure a specific robot user for the account where you’ll want to track your Kubernetes inventory.

As an agent anchore-k8s-inventory is installed using helm and the helm chart is hosted as part of the https://charts.anchore.io repo. It is based on the anchore/k8s-inventory docker image.

To install the helm chart, follow these steps:

  1. Configure your username, password, Anchore account, Anchore URL and cluster name in the values file.
k8sInventory:
  # Path should not be changed, cluster value is used to tell Anchore which cluster this inventory is coming from
  kubeconfig:
    cluster: <unique-name-for-your-cluster>

  anchoreRegistration:
    #RegistrationId: ""
    IntegrationName: "<unique-name-for-your-cluster>"
    IntegrationDescription: ""

  anchore:
    url: <URL for your>

    # Note: recommend using the inventory-agent role
    user: <user>
    password: <password>
    account: <account>
  1. Run helm install in the cluster(s) you wish to track
$ helm repo add anchore https://charts.anchore.io
$ helm install <release> -f <values.yaml> anchore/k8s-inventory

anchore-k8s-inventory must be able to resolve the Anchore URL and requires API credentials. Review the anchore-k8s-inventory logs if you are not able to see the inventory results in the UI.

Note: the Anchore Enterprise API Password can be provided via a Kubernetes secret, or injected into the environment of the anchore-k8s-inventory container

  • For injecting the environment variable, see: injectSecretsViaEnv
  • For providing your own secret for the Anchore Enterprise API Password, see: useExistingSecret. K8s Inventory creates it’s own secret based on your values.yaml file for key k8sInventory.anchore.password, but the k8sInventory.useExistingSecret key allows you to create your own secret and provide it in the values file. See the K8s Inventory repo for more information about the K8s Inventory specific configuration

Usage

To verify that you are tracking Kubernetes Inventory you can access inventory results with the command anchorectl inventory list and look for results where the TYPE is kubernetes.

The UI also displays the Kubernetes Inventory and allows operators to visually navigate the images, vulnerability results, and see the results of the policy evaluation.

For more details about watching clusters, and reviewing policy results see the Using Kubernetes Inventory section.

Inventory Time-To-Live

As part of reporting on your runtime environment, Anchore Enterprise maintains an active record of the containers, the images they run, and other related metadata based on time they were last reported by an inventory agent.

The configuration setting below allow you to specify how long inventory should remain part of the Catalog Service’s working set. These are the default settings found in the values file.

services:
  catalog:
    runtime_inventory:
      inventory_ingest_overwrite: false
      inventory_ttl_days: 120

Below are a few examples on how you may want to use this feature.

Keep most recently reported inventory
inventory_ingest_overwrite: true
inventory_ttl_days: 7

For each cluster/namespace reported from the inventory agent, the system will delete any previously reported containers and images and replace it with the new inventory.

Note: The inventory_ttl_days is still needed to remove any cluster/namespaces that are no longer reported as well as some of the supporting metadata (ie. pods, nodes). This value should be configured to be long enough that inventory isn’t incorrectly removed in case of an outage from the reporting agent. The exact value depends on each deployment, but 7 days is a reasonable value here.

Keep inventory reported over a time period
inventory_ingest_overwrite: false
inventory_ttl_days: 14

This will delete any container and image that has not been reported by an agent in the last 14 days. This includes its supporting metadata (ie. pods, nodes).

Keep inventory indefinitely
inventory_ingest_overwrite: false
inventory_ttl_days: 0

This will keep any containers, images, and supporting metadata reported by an inventory agent indefinitely.

Deleting Inventory via API

Where it is not desirable to wait for the Image TTL to remove runtime inventory images it is possible to manually delete inventory items via the API by issuing a DELETE to /v2/inventories with the following query parameters.

  • inventory_type (required) - either ecs or kubernetes
  • context (required) - it must match a context as seen by the output of anchorectl inventory list
    • Kubernetes - this is a combination of cluster name (as defined by the anchore-k8s-inventory config) and a namespace containing running containers e.g. cluster1/default.
    • ECS - this is the cluster ARN e.g. arn:aws:ecs:eu-west-2:123456789012:cluster/myclustername
  • image_digest (optional) - set if you only want to remove a specific image

e.g. DELETE /v2/inventories?inventory_type=<string>&context=<string>&image_digest=<string>

Using curl: curl -X DELETE -u username:password "http://<servername:port>/v2/inventories?inventory_type=&context=&image_digest=

Agents

Anchore Enterprise provides agents for collecting the inventory of different container runtime environments:

4 - Amazon ECS

Anchore uses a go binary called anchore-ecs-inventory that leverages the AWS Go SDK to gather an inventory of containers and their images running on Amazon ECS and report back to Anchore Enterprise.

The Amazon ECS Inventory Agent can be deployed as an ECS Service on AWS Fargate or, where you already run Kubernetes, installed via Helm chart. A single agent can inventory the AWS account-region it runs in, or inventory several AWS account-regions by assuming an IAM role in each one.


Plan Your Deployment Architecture

The agent polls the Amazon ECS APIs one region at a time. Each region it covers is an inventory pass: the agent lists the clusters, services, and tasks in that region and reports the images it finds to Anchore Enterprise. By default the agent runs a single pass against its own account and region using whatever AWS credentials it starts with.

From ECS Inventory v1.5.0 onward, an agent can instead run a pass for each IAM role you configure, up to 20 roles. Because a role can live in any AWS account whose trust policy allows the agent to assume it, you are free to decide how many agents you run and which account-regions each one covers.

Requirements Shared by Every Architecture

No matter which architecture you choose, all of the following must hold:

  • The agent needs network access to the Anchore Enterprise API. The agent only makes outbound connections, so no inbound rules are required, but the subnets and security groups it runs in must be able to reach your Anchore Enterprise deployment. Give the agent a dedicated Anchore Enterprise user rather than reusing an administrator account.
  • Anchore Enterprise needs to be able to analyze the images the agent reports. The agent reports image references, not image content. If an image is not already analyzed, Anchore Enterprise pulls and analyzes it itself, which means the deployment needs network access to the registry holding that image and registry credentials for it — for example ECR credentials for images stored in Amazon ECR. Without this, images still appear in the inventory but never gain vulnerability or policy results.
  • All inventory from one agent lands in one Anchore Enterprise account. An agent has a single set of Anchore Enterprise credentials and a single anchore.account value, which applies to every pass it runs. If inventory from different AWS accounts must be separated into different Anchore Enterprise accounts, run a separate agent for each.

Deploy One Agent per AWS Account-Region

Run an independent agent in each AWS account-region you want to inventory, each using its own task role or service account. This is the pre-v1.5.0 model and remains the simplest option: no cross-account trust relationships, and a fault in one account cannot affect inventory collection in another.

flowchart LR
    subgraph ACC1["AWS account 123456789012"]
        AG1["ecs-inventory agent<br/>us-east-1"] --> E1["ECS clusters<br/>us-east-1"]
    end
    subgraph ACC2["AWS account 999999999999"]
        AG2["ecs-inventory agent<br/>eu-west-1"] --> E2["ECS clusters<br/>eu-west-1"]
    end
    AG1 --> AE["Anchore Enterprise API"]
    AG2 --> AE

Choose this when you want strict isolation between accounts, when different teams own each account, or when each AWS account’s inventory needs to report into a different Anchore Enterprise account.

Centralize Agents in One AWS Account

Run one agent in a central account and give it an assume-role entry for each account-region you want to inventory. The agent assumes a role in every target account and reports all of the resulting inventory to Anchore Enterprise.

flowchart LR
    subgraph HUB["AWS account 111111111111 — agent account"]
        AG["ecs-inventory agent<br/>AnchoreECSInventoryTaskRole"]
    end
    subgraph ACC1["AWS account 123456789012"]
        R1["anchore-ecs-inventory role"] --> E1["ECS clusters<br/>us-east-1"]
    end
    subgraph ACC2["AWS account 999999999999"]
        R2["anchore-ecs-inventory role"] --> E2["ECS clusters<br/>eu-west-1"]
    end
    AG -->|"sts:AssumeRole"| R1
    AG -->|"sts:AssumeRole"| R2
    AG --> AE["Anchore Enterprise API"]

Choose this when you want a single deployment to operate, patch, and monitor, and when one team owns runtime inventory across the organization. Only the central account needs network access to Anchore Enterprise.

Inventory Every Region of an Account from One Agent

A single agent can also cover several regions within one AWS account. Add one assume-role entry per region, all pointing at the same role ARN — each entry is an independent pass, so repeating a role ARN with a different region is expected.

flowchart LR
    subgraph ACC["AWS account 123456789012"]
        AG["ecs-inventory agent"] -->|"sts:AssumeRole"| R["anchore-ecs-inventory role"]
        R --> E1["ECS clusters<br/>us-east-1"]
        R --> E2["ECS clusters<br/>us-west-2"]
        R --> E3["ECS clusters<br/>eu-west-1"]
    end
    AG --> AE["Anchore Enterprise API"]

Choose this when a single account runs workloads in several regions. Because the role lives in the same account as the agent, no cross-account trust is involved.

Compare the Architectures

ArchitectureAgents to operateCross-account trustAnchore Enterprise accountsBest for
One agent per account-regionOne per account-regionNot requiredOne per agent, if desiredStrict isolation, per-team ownership, separate Anchore accounts
Centralized agentOneRequired for each target accountOne for all inventoryCentral ownership, fewest deployments to maintain
One agent per account, all regionsOne per accountNot requiredOne per account, if desiredA single account running workloads in several regions

These patterns combine. A common middle ground is one agent per organizational unit, each assuming roles into the accounts that unit owns, which keeps the number of deployments low without concentrating every account’s trust in one place.


Inventory Multiple AWS Account-Regions from a Single Agent

Add an assume-role list to the agent’s configuration file, giving each entry a role ARN, the region to inventory with those credentials, and an external ID where the target role’s trust policy requires one:

assume-role:
  - role-arn: arn:aws:iam::123456789012:role/anchore-ecs-inventory
    region: us-east-1
  - role-arn: arn:aws:iam::999999999999:role/anchore-ecs-inventory
    region: eu-west-1
    external-id: <external-id>

Each entry runs as an independent inventory pass on every polling cycle, up to 20 entries. Assumed credentials refresh automatically as they expire, so a long-running agent keeps working without intervention.

An empty assume-role list is the default and preserves the behavior of inventorying the agent’s own account using the top-level region.

Configuration Rules and Limits

RuleDetail
Config file onlyThe assume-role list can only be set in a configuration file. Setting ANCHORE_ECS_INVENTORY_ASSUME_ROLE is rejected at startup with an explicit error.
Region is required per entryEvery entry must name its own region. The top-level region, the --region flag, and ANCHORE_ECS_INVENTORY_REGION are all ignored when entries are configured, and the agent logs a warning if one is set.
Maximum of 20 entriesThe agent refuses to start if more than 20 entries are configured. This bounds the number of ECS and STS calls made per polling cycle.
External ID is optionalSet external-id only when the target role’s trust policy requires one.
Roles may repeatThe same role-arn can appear in several entries with different regions, which is how one role covers multiple regions.

Startup Validation and Failure Handling

Every configured role is checked at startup and the agent exits if one cannot be assumed, so a misconfigured role surfaces immediately instead of as quietly missing inventory. Once the polling loop is running, a role that fails on one cycle is logged and retried on the next, and the remaining passes continue to report.

Passes run serially within a cycle, so a slow account or a long list of roles can push a cycle past polling-interval-seconds. When that happens the agent logs a warning and the next cycle starts immediately. If you see this warning, raise polling-interval-seconds or split the roles across more agents.

Rotating the static base credentials the agent uses to call STS requires a restart. In the no-role case the agent rebuilds its AWS configuration each cycle, so rotated static credentials are picked up on the next poll.


IAM Role Configuration

Grant ECS Read Permissions

The identity that queries Amazon ECS — the task role, the service account role, the static credentials, or an assumed role — needs read and list access to ECS. The following policy grants exactly the API actions the agent calls:

cat <<EOF > ecs-read-only-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AnchoreEcsInventoryRead",
            "Effect": "Allow",
            "Action": [
                "ecs:ListClusters",
                "ecs:ListServices",
                "ecs:ListTasks",
                "ecs:DescribeServices",
                "ecs:DescribeTasks",
                "ecs:ListTagsForResource"
            ],
            "Resource": "*"
        }
    ]
}
EOF
aws iam create-policy \
  --policy-name ECSReadOnly \
  --policy-document file://ecs-read-only-policy.json

When the agent inventories multiple account-regions, this policy belongs on the role named by each assume-role entry, in the account being inventoried — not on the agent’s own identity.

Allow the Agent to Assume a Role

Assuming a role requires permissions on both sides of the trust relationship. In the examples below, 111111111111 is the account the agent runs in and 123456789012 is an account being inventoried.

First, the agent’s base identity — the ECS task role, the Kubernetes service account role, or the static credentials the agent starts with — must be allowed to assume each target role:

cat <<EOF > assume-role-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AnchoreEcsInventoryAssumeRole",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": [
                "arn:aws:iam::123456789012:role/anchore-ecs-inventory",
                "arn:aws:iam::999999999999:role/anchore-ecs-inventory"
            ]
        }
    ]
}
EOF
aws iam put-role-policy \
  --role-name AnchoreECSInventoryTaskRole \
  --policy-name AnchoreECSInventoryAssumeRole \
  --policy-document file://assume-role-policy.json

Second, each target role must trust the agent’s base identity. Create the role in the account being inventoried with a trust policy naming the agent’s role as the principal, then attach the ECS read policy to it:

cat <<EOF > trust-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/AnchoreECSInventoryTaskRole"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
EOF
aws iam create-role \
  --role-name anchore-ecs-inventory \
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy \
  --role-name anchore-ecs-inventory \
  --policy-arn arn:aws:iam::123456789012:policy/ECSReadOnly

For a role in the same account as the agent, the trust policy is identical — the principal simply references a role in the same account.

Require an External ID

An external ID is a shared secret that the agent must present when assuming a role. It protects against the confused deputy problem, where a third party who can persuade your agent to assume a role gains access it was never meant to have. Use one when the account being inventoried is operated by a different team or organization from the account running the agent.

Add the sts:ExternalId condition to the target role’s trust policy:

cat <<EOF > trust-policy-external-id.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111111111111:role/AnchoreECSInventoryTaskRole"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "sts:ExternalId": "<external-id>"
                }
            }
        }
    ]
}
EOF
aws iam create-role \
  --role-name anchore-ecs-inventory \
  --assume-role-policy-document file://trust-policy-external-id.json

Then set the matching value on the corresponding assume-role entry in the agent’s configuration file:

assume-role:
  - role-arn: arn:aws:iam::123456789012:role/anchore-ecs-inventory
    region: us-east-1
    external-id: <external-id>

Deploying as an ECS Service on AWS Fargate

You can deploy the ecs-inventory container as an ECS Service on AWS ECS/Fargate. Running the agent as a service ensures that ECS automatically restarts the task if it stops, maintaining continuous inventory reporting to Anchore Enterprise.

Set Environment Variables

Set the following environment variables before running the commands below. Replace the placeholder values with your own.

export aws_account_id=$(aws sts get-caller-identity --query Account --output text)
export AWS_DEFAULT_REGION=<your_aws_region>

# VPC and networking
export vpc_id=<your_vpc_id>
export subnet_ids=<your_subnet_id_1>,<your_subnet_id_2>
export security_group_id=<your_security_group_id>

# Anchore Enterprise connection details
export ANCHORE_URL=<your_anchore_enterprise_url>
export ANCHORE_ACCOUNT=<your_anchore_account>
export ANCHORE_USERNAME=<your_anchore_username>
export ANCHORE_PASSWORD=<your_anchore_password>

Create IAM Roles and Policies

Create the IAM policy, roles, and permissions required by the ECS task.

aws iam create-policy \
  --policy-name ECSReadOnly \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AnchoreEcsInventoryRead",
        "Action": [
          "ecs:ListClusters",
          "ecs:ListServices",
          "ecs:ListTasks",
          "ecs:DescribeServices",
          "ecs:DescribeTasks",
          "ecs:ListTagsForResource"
        ],
        "Effect": "Allow",
        "Resource": "*"
      }
    ]
  }'

aws iam create-role \
  --role-name AnchoreECSInventoryTaskRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "ecs-tasks.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

aws iam create-role \
  --role-name AnchoreECSInventoryExecutionRole \
  --assume-role-policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "ecs-tasks.amazonaws.com"
        },
        "Action": "sts:AssumeRole"
      }
    ]
  }'

aws iam attach-role-policy \
  --role-name AnchoreECSInventoryExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

aws iam attach-role-policy \
  --role-name AnchoreECSInventoryTaskRole \
  --policy-arn arn:aws:iam::${aws_account_id}:policy/ECSReadOnly

Store the Anchore Enterprise Password in AWS Systems Manager Parameter Store

aws ssm put-parameter \
  --name "/ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD" \
  --type "SecureString" \
  --value "${ANCHORE_PASSWORD}" \
  --overwrite

aws iam put-role-policy \
  --role-name AnchoreECSInventoryExecutionRole \
  --policy-name ECSInventorySSMAccess \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "ssm:GetParameters",
          "ssm:GetParameter"
        ],
        "Resource": "arn:aws:ssm:'${AWS_DEFAULT_REGION}':'${aws_account_id}':parameter/ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD"
      }
    ]
  }'

Create the CloudWatch Log Group

aws logs create-log-group \
  --log-group-name /anchore/ecs-inventory \
  --region ${AWS_DEFAULT_REGION}

Register the Task Definition

cat << EOF > task-definition.json
{
  "family": "anchore-ecs-inventory",
  "cpu": "512",
  "memory": "1024",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "executionRoleArn": "arn:aws:iam::${aws_account_id}:role/AnchoreECSInventoryExecutionRole",
  "taskRoleArn": "arn:aws:iam::${aws_account_id}:role/AnchoreECSInventoryTaskRole",
  "containerDefinitions": [
    {
      "name": "ecs-inventory",
      "image": "docker.io/anchore/ecs-inventory:latest",
      "cpu": 0,
      "essential": true,
      "user": "1000",
      "readonlyRootFilesystem": true,
      "linuxParameters": {
        "capabilities": {
          "drop": ["ALL"]
        }
      },
      "environment": [
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_URL",
          "value": "${ANCHORE_URL}"
        },
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_USER",
          "value": "${ANCHORE_USERNAME}"
        },
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_ACCOUNT",
          "value": "${ANCHORE_ACCOUNT}"
        },
        {
          "name": "ANCHORE_ECS_INVENTORY_REGION",
          "value": "${AWS_DEFAULT_REGION}"
        }
      ],
      "secrets": [
        {
          "name": "ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD",
          "valueFrom": "arn:aws:ssm:${AWS_DEFAULT_REGION}:${aws_account_id}:parameter/ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD"
        }
      ],
      "healthCheck": {
        "command": ["CMD", "/anchore-ecs-inventory", "version"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 10
      },
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-create-group": "true",
          "awslogs-group": "/anchore/ecs-inventory",
          "awslogs-region": "${AWS_DEFAULT_REGION}",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}
EOF

aws ecs register-task-definition \
  --cli-input-json file://task-definition.json

Create the ECS Cluster and Service

Create the ECS cluster and deploy the agent as a Fargate service with a desired count of 1.

aws ecs create-cluster \
  --cluster-name anchore-ecs-inventory-cluster

aws ecs create-service \
  --cluster anchore-ecs-inventory-cluster \
  --service-name anchore-ecs-inventory \
  --task-definition anchore-ecs-inventory \
  --desired-count 1 \
  --launch-type FARGATE \
  --scheduling-strategy REPLICA \
  --network-configuration "awsvpcConfiguration={
    subnets=[${subnet_ids}],
    securityGroups=[${security_group_id}],
    assignPublicIp=ENABLED
  }"

Inventory Multiple Account-Regions from the Fargate Service

The walkthrough above deploys an agent that inventories its own account and region. To have the same service inventory several account-regions, grant the task role permission to assume the target roles and give the agent a configuration file containing an assume-role list.

Grant the Task Role Permission to Assume the Target Roles

Create the target roles in each account being inventoried, following Allow the Agent to Assume a Role, then attach the assume-role policy to AnchoreECSInventoryTaskRole:

aws iam put-role-policy \
  --role-name AnchoreECSInventoryTaskRole \
  --policy-name AnchoreECSInventoryAssumeRole \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AnchoreEcsInventoryAssumeRole",
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": [
          "arn:aws:iam::123456789012:role/anchore-ecs-inventory",
          "arn:aws:iam::999999999999:role/anchore-ecs-inventory"
        ]
      }
    ]
  }'

The task role no longer needs the ECSReadOnly policy in the agent’s own account, because the ECS calls are made with the assumed credentials instead. Keep it only if you also add an assume-role entry for a role in the agent’s account.

Write the Configuration File

Create the configuration file, including the assume-role list:

log:
  level: "info"
  file: ""

anchore:
  url: $ANCHORE_ECS_INVENTORY_ANCHORE_URL
  user: $ANCHORE_ECS_INVENTORY_ANCHORE_USER
  password: $ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD
  account: $ANCHORE_ECS_INVENTORY_ANCHORE_ACCOUNT
  http:
    insecure: false
    timeout-seconds: 10

polling-interval-seconds: 300
quiet: false

assume-role:
  - role-arn: arn:aws:iam::123456789012:role/anchore-ecs-inventory
    region: us-east-1
  - role-arn: arn:aws:iam::999999999999:role/anchore-ecs-inventory
    region: eu-west-1
    external-id: <external-id>

Choose How to Supply the Configuration File

Amazon ECS has no direct equivalent of a Kubernetes ConfigMap. The secrets parameter (Systems Manager Parameter Store and Secrets Manager) and the environmentFiles parameter (a .env object in Amazon S3) both inject values as environment variables, and the assume-role list cannot be set that way — it is read only from a configuration file. The file therefore has to reach the container’s filesystem, and three approaches do that on Fargate:

ApproachWhat it requiresChange the configuration without rebuilding the image
Copy the file from S3 with a setup containerAn S3 object, s3:GetObject on the task role, and a second container in the task definitionYes — upload the new object and redeploy the service
Mount an S3 Files volumeAn S3 file system with a mount target in the task’s VPCYes — update the object in the bucket
Build a derived imageA Dockerfile and a registry the task can pull fromNo — rebuild and push the image for every change

The agent reads its configuration from /etc/xdg/anchore-ecs-inventory/config.yaml inside the container, so each approach places the file at that path. Copying the file from S3 is the closest equivalent to a ConfigMap and is the approach to reach for first: it adds no new infrastructure and keeps the released Anchore image unmodified.

Copy the Configuration File from S3

Upload the configuration file to a bucket the task can read:

aws s3 cp config.yaml s3://<your_config_bucket>/anchore-ecs-inventory/config.yaml

Allow the task role to read it:

aws iam put-role-policy \
  --role-name AnchoreECSInventoryTaskRole \
  --policy-name AnchoreECSInventoryConfigRead \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AnchoreEcsInventoryConfigRead",
        "Effect": "Allow",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::<your_config_bucket>/anchore-ecs-inventory/config.yaml"
      }
    ]
  }'

Then add a shared volume and a setup container to the task definition. The setup container copies the file from S3 into the volume and exits; the dependsOn condition holds the agent back until that copy has succeeded:

"volumes": [
  {
    "name": "agent-config"
  }
],
"containerDefinitions": [
  {
    "name": "config-init",
    "image": "public.ecr.aws/aws-cli/aws-cli:latest",
    "essential": false,
    "command": [
      "s3",
      "cp",
      "s3://<your_config_bucket>/anchore-ecs-inventory/config.yaml",
      "/config/config.yaml"
    ],
    "mountPoints": [
      {
        "sourceVolume": "agent-config",
        "containerPath": "/config"
      }
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/anchore/ecs-inventory",
        "awslogs-region": "<region>",
        "awslogs-stream-prefix": "config-init"
      }
    }
  },
  {
    "name": "ecs-inventory",
    "image": "docker.io/anchore/ecs-inventory:v1.5.0",
    "essential": true,
    "dependsOn": [
      {
        "containerName": "config-init",
        "condition": "SUCCESS"
      }
    ],
    "mountPoints": [
      {
        "sourceVolume": "agent-config",
        "containerPath": "/etc/xdg/anchore-ecs-inventory",
        "readOnly": true
      }
    ]
  }
]

The remaining keys on the ecs-inventory container — environment, secrets, healthCheck, logConfiguration, and the security settings — stay as they are in Register the Task Definition, except that ANCHORE_ECS_INVENTORY_REGION should be removed because it is ignored when assume-role entries are configured.

To roll out a configuration change, upload the new object and run aws ecs update-service --force-new-deployment. The setup container fetches the current object each time a task starts.

Mount an S3 Files Volume

Amazon ECS can also mount an S3 file system directly as a volume, which keeps the file in S3 and synchronizes changes without a setup container. This requires an S3 file system with a mount target reachable from the task’s VPC, so it suits deployments that already use S3 Files; if you are adding it only to deliver this one file, the setup container above is lighter.

"volumes": [
  {
    "name": "agent-config",
    "s3filesVolumeConfiguration": {
      "fileSystemArn": "arn:aws:s3files:<region>:<aws_account_id>:file-system/<file-system-id>",
      "rootDirectory": "/anchore-ecs-inventory"
    }
  }
]

Mount it on the agent container at /etc/xdg/anchore-ecs-inventory with "readOnly": true, exactly as in the setup container example above, and drop the config-init container and its dependsOn entry. Transit encryption and a task IAM role are mandatory for S3 Files volumes and are enforced automatically; the task role also needs permission to connect to the file system. S3 Files volumes are supported on Fargate and Amazon ECS Managed Instances, but not on the EC2 launch type. See Configuring S3 Files for Amazon ECS for the file system prerequisites and IAM policies.

Build a Derived Image with the Configuration File

Where a rebuild per configuration change is acceptable — for example when the role list is managed in version control and rolled out through a pipeline — bake the file into an image built from the released one:

FROM docker.io/anchore/ecs-inventory:v1.5.0
COPY config.yaml /etc/xdg/anchore-ecs-inventory/config.yaml

Then build the image and push it to a registry your ECS tasks can pull from, such as Amazon ECR:

docker build -t ${aws_account_id}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/anchore-ecs-inventory:v1.5.0 .

aws ecr get-login-password --region ${AWS_DEFAULT_REGION} \
  | docker login --username AWS --password-stdin \
    ${aws_account_id}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com

docker push ${aws_account_id}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/anchore-ecs-inventory:v1.5.0

Point image in the task definition at the derived image, and remove the ANCHORE_ECS_INVENTORY_REGION environment variable because it is ignored when assume-role entries are configured.

Deploy the Updated Service

Register the new task definition revision and update the service to use it:

aws ecs register-task-definition \
  --cli-input-json file://task-definition.json

aws ecs update-service \
  --cluster anchore-ecs-inventory-cluster \
  --service anchore-ecs-inventory \
  --task-definition anchore-ecs-inventory \
  --force-new-deployment

Check the service’s CloudWatch logs after the deployment. The agent logs Startup validation passed with the number of passes it will run, and exits if any configured role cannot be assumed.


Deploying via Helm on Kubernetes

You can install the chart via the Anchore Enterprise repository:

helm repo add anchore https://charts.anchore.io
helm install <release-name> -f <values.yaml> anchore/ecs-inventory

A basic values file can be found here.

Assign the IAM Role to the Agent

Create the ECS read policy and attach it to an IAM role, then follow the AWS instructions found here to assign that role to a Kubernetes service account in the cluster where the Anchore ECS Inventory Agent will be running. Then configure the following in your values.yaml to ensure the agent has access to the ECS service API:

serviceAccountName: "service_account_name"

Using existing secrets

For those users unable to use IAM roles (e.g. the ECS Inventory Agent is not running on Kubernetes or ECS), the (ecsInventory.useExistingSecret and ecsInventory.existingSecretName) or ecsInventory.injectSecretsViaEnv keys allows you to create your own secret and provide it in the values file or place the required secret into the pod via different means such as injecting the secrets into the pod using hashicorp vault. For example:

  • Create a secret in kubernetes:

    apiVersion: v1
    kind: Secret
    metadata:
      name: ecs-inventory-secrets
    type: Opaque
    stringData:
      ANCHORE_ECS_INVENTORY_ANCHORE_PASSWORD: foobar
      AWS_ACCESS_KEY_ID: someKeyId
      AWS_SECRET_ACCESS_KEY: someSecretAccessKey
    
  • Provide it to the helm chart via the values file:

    ecsInventory:
        useExistingSecret: true
        existingSecretName: "ecs-inventory-secrets"
    

Configure Assume-Role on Kubernetes

The agent reads its configuration file from /etc/xdg/anchore-ecs-inventory/config.yaml in the container image. Write the file as shown in Write the Configuration File, then place it at that path using either approach below:

  • Mount your own ConfigMap. Deploy the agent with your own manifests rather than the chart, mounting a ConfigMap that holds the complete configuration file at /etc/xdg/anchore-ecs-inventory/config.yaml. This keeps configuration changes to a kubectl apply and a pod restart.
  • Bake the configuration into a derived image. Build an image from the released one with your configuration file copied over the default, as described in Build a Derived Image with the Configuration File, then set image in your values file to the derived image. The chart deploys this image unmodified, so no custom manifests are needed.

The service account attached to the agent still needs permission to assume each target role; it does not need ECS read permissions in its own account unless you also add an assume-role entry for a role there.


Usage

To verify that you are tracking Amazon ECS inventory in your Anchore Enterprise deployment you can access inventory results with the command anchorectl inventory list and look for results where the TYPE is ecs.

Auto analyze new inventory

It is possible to create a subscription to watch for new Amazon ECS inventory that is reported to Anchore Enterprise and automatically schedule those images for analysis. The subscription_key can be set to any part of an Amazon ECS ClusterARN. For example setting the subscription_key to the:

  • full ClusterARN arn:aws:ecs:us-east-1:012345678910:cluster/telemetry will create a subscription that only watches this cluster
  • partial ClusterARN arn:aws:ecs:eu-west-2:988505687240 will result in a subscription that watches every cluster within the account 988505687240
  • All ECS clusters arn:aws:ecs effectively auto-subscribes all ECS runtime agents.

anchorectl inventory watch activate <SUBSCRIPTION_KEY>

The least-privilege role with permission to add a subscription is image-analyzer.

UI

The UI will visually indicate when images are actively found in ECS Runtime Inventory.

ECS Runtime linkage via UI

General Runtime Management

See Data Management

5 - ServiceNow

This documentation serves as a comprehensive reference for integrating Anchore Enterprise with ServiceNow’s Container Vulnerability Response (CVR) module. This integration enables organizations to “hydrate” ServiceNow with Anchore security data, allowing teams to utilize their established vulnerability grouping, reporting, and remediation workflows for containerized assets.

Overview and Release Information

Anchore provides a dedicated plugin that bridges the gap between Anchore Enterprise scans and ServiceNow Security Operations (SecOps).

Availability: Accessible via GitHub for licensed Anchore customers.
Version Support: Anchore Enterprise v5.x and later.
Source Code: https://github.com/anchore/servicenow (private)

Anchore SNOW Integrations

Anchore currently focuses on the Security Operations (SecOps) side of the ServiceNow platform:

  • ServiceNow Container Vulnerability Response (CVR)
    Vulnerabilities identified in container images are pulled into ServiceNow CVR via the Anchore CVR plugin.

  • ServiceNow IT Service Management (ITSM)
    Anchore does not currently provide a native SNOW ITSM integration for automatic ticket generation.


Installation and Setup

Prerequisites

Ensure the following ServiceNow applications are active before installation:

  • Vulnerability Response
  • Vulnerability Response and Configuration Compliance for Containers
  • Vulnerability Response Integration with NVD

Deployment Steps

The integration is installed directly from GitHub into the ServiceNow Studio application:

  1. Select Import from Source Control
  2. URL: https://github.com/anchore/servicenow/
  3. Branch: snow_import (fully bundled application)
  4. Credentials:
    • Create a GitHub Personal Access Token
    • In ServiceNow, navigate to Connections & Credentials > Credentials
    • Create a new Basic Auth Credential
      • Username: GitHub username
      • Password: Personal Access Token

Architecture: Anchore ServiceNow Integration

Hydrates ServiceNow’s Container Vulnerability Response module with Anchore Enterprise scan results.

Reference: https://docs.servicenow.com/en-US/bundle/vancouver-security-management/page/product/vulnerability-response/concept/vuln_integrations.html


Theory of Operation: Integration Overview

The integration relies on JavaScript-based scripts to move data from the Anchore Enterprise API into ServiceNow tables.

1. Integration Script (Data Retrieval)

The Integration Script handles the initial handshake and data preparation:

  • Queries ServiceNow for images with a known image_digest
  • Compares them against Anchore Enterprise API summary (/v2/summaries/image-tags)
  • Processes images in batches (default: 50)
  • Retrieves full vulnerability and ancestry data (including base image layers)
  • Uses image digest for matching
  • Bundles merged vulnerability and metadata into a JSON document
  • Attaches JSON to a ServiceNow Import Set

2. Processor Script (Data Ingestion)

Once the JSON attachment is created, the Processor Script automatically ingests the data:

  • Iterates through JSON per image digest
  • Retrieves vulnerability IDs and package metadata
  • Uses CMDB Lookup Rule to link findings to cmdb_ci_docker_image
  • Creates Container Vulnerable Items (CVITs)
  • Updates first found and last found timestamps based on Anchore detectedAt data

Detailed Data Flow

Data Import into ServiceNow

  • Anchore Enterprise data imported into ServiceNow Vulnerability Response
  • Vulnerability data merged with image metadata
  • Images matched by digest

Vulnerability Item Creation

  • Vulnerability data compared against existing ServiceNow records
  • If a match is found, a Vulnerable Item (VI) is created or updated

Configuration Settings

The following settings are configured within the Anchore Integration module in ServiceNow:

SettingDescription
Anchore Enterprise APIFull URL (including port) of the Anchore Enterprise instance
MID ServerMID server used for communication between ServiceNow and Anchore Enterprise
SNOW Image ListEncoded Glide Query to filter images from cmdb_ci_docker_image
Add Missing Repos?If TRUE, creates new repositories in CMDB if absent
Batch SizeNumber of images processed per Import Set (default: 50)

Performance Tuning

Batch Size Optimization

Adjust the batch size so that:

  • REST API processing time
  • Import queue processing time

remain balanced (recommended ~5 minutes per batch).

MID Server Considerations

Because requests are synchronous:

  • MID server load directly impacts speed
  • High-scale deployments (10,000+ images) may require:
    • Increasing mid.eccq.max_payload_size
    • Disabling glide.http.outbound.max_timeout.enabled

ServiceNow Integration Documentation

ServiceNow Vulnerability Response Container Integration Implementation Guide (JavaScript):

https://docs.servicenow.com/en-US/bundle/vancouver-security-management/page/product/vulnerability-response/concept/vuln_integrations.html

6 - DefectDojo

DefectDojo

DefectDojo is an open source application vulnerability management platform that streamlines the handling of security findings from various tools, including seamless integration with Anchore Enterprise.

Anchore Enterprise vulnerability and policy reports, whether obtained through the UI or using anchorectl, can be seamlessly parsed and imported into DefectDojo for centralized vulnerability management.

Importing Anchore Enterprise analysis Data into DefectDojo

Data from Anchore Enterprise can be ingested into DefectDojo in a number of ways:

  • The upload of vulnerability or policy reports obtained via the Anchore Enterprise GUI
  • The upload of vulnerability or policy reports obtained via anchorectl
  • By utilizing the Anchore Enterprise API to automatical pull vulnerability and policy reports from Anchore Enterprise
  • By using the Anchore Enterprise connector

Downloaded reports from Anchore Enterprise GUI or anchorectl can be uploaded to DefectDojo by selecting the appropriate parser during the import process. For more details on available DefectDojo and Anchore parsers, see: DefectDojo Integration.

Downloading Vulnerability report from Anchore Enterprise GUI

To download vulnerability report data from Anchore Enterprise GUI

Click on the “Images” icon Image list

Select the image tag for which you want to download the vulnerability data. Image tags

Now navigate to the “Vulnerabilities” section, Click on “Vulnerability Report” to download the report. Vulnerability list

Download the report in JSON format, then proceed to import it into DefectDojo.

Downloading Vulnerability and Policy report via anchorectl

To download vulnerability report using anchorectl run the following:

anchorectl image vulnerabilities <Image sha> -o json > <filename.json> 

To download policy report using anchorectl run the following:

anchorectl image check <name:tag> -o json > <filename.json> 

The filename.json download can then be uploaded into DefectDojo

Exporting vulnerability report data using Anchore Enterprise API

Automation workflows can be created using the Anchore Enterprise API to pull vulnerability data and submit it to DefectDojo via its API.

To retrieve vulnerability data for an image using the Anchore Enterprise API, use the following curl command:

curl -X 'GET' \
  'https://myanchore.com/v2/images/<Imagesha>/vuln/all?force_refresh=false&include_vuln_description=false&vendor_only=true' \
  -H 'accept: application/json'

For more details on how to automate this process using DefectDojo API, see: DefectDojo API usage.

7 - Data Stream

Overview

The Anchore Data Stream provides a mechanism to stream security data from Anchore Enterprise to external systems for further processing, analysis, and long-term storage. As image vulnerability scans and policy evaluations occur within Anchore Enterprise, the data is captured and written to files. These files are monitored by a sidecar service (such as Fluent Bit). The sidecar service reads the data from the files and forwards the events to external destinations like Splunk, Elasticsearch, or other SIEM platforms.

This feature enables you to integrate:

  • Real-time Security Monitoring: Stream vulnerability discoveries and policy violations as they occur
  • Centralized Log Management: Aggregate Anchore Enterprise security data with other infrastructure logs
  • Custom Dashboards: Build security dashboards in your preferred analytics platform
  • Compliance Reporting: Maintain audit trails of security events for compliance requirements
  • Alerting Integration: Trigger alerts based on critical vulnerability discoveries or policy failures

Architecture

The data streaming pipeline consists of three components:

flowchart LR
    RW["Anchore Enterprise<br/>(Reports Worker)"]
    DEF["Data Event Files"]
    FB["Fluent Bit Sidecar"]
    DEST["External Destination<br/>(e.g., Splunk)"]

    RW --> DEF
    DEF --> FB
    FB --> DEST
  1. Reports Worker: Writes security data to NDJSON (newline-delimited JSON) files
  2. Data Event Files: Rotating log files stored on a shared volume, with automatic cleanup of processed files
  3. Fluent Bit: A lightweight log forwarder that tails the data event files and forwards them to your destination

Data Event Types

The following system data events are streamed:

Data Event TypeDescription
Image Vulnerability Scan ResultsChanges to the vulnerability scan results including CVE IDs, severity, fix availability, and affected packages
Image Policy Evaluation FindingsChanges to the policy evaluation results including pass/fail status, triggered gates, and findings

Getting Started

To set up the Data Event Stream integration:

  1. Configure the Data Stream in Anchore Enterprise
  2. Deploy Fluent Bit as a sidecar container
  3. Configure your destination (e.g., Splunk)

7.1 - Event Stream Configuration

Overview

The Data Event Stream feature is configured through the Anchore Enterprise configuration file or Helm values. This page covers the configuration options for enabling event streaming and customizing its behavior.

Prerequisites

Before enabling the event stream:

  1. Ensure you have a valid license with the Data Stream entitlement
  2. Plan your shared volume strategy for the data event files. Maximum file size and count will impact storage requirements.
  3. Determine your destination system (Splunk, Elasticsearch, etc.)

Configuration

Ports

ComponentPortPurpose
Fluent Bit Health2020Health checks and metrics endpoint
(example) Splunk HEC8088HTTP Event Collector ingestion

Shard Data Files

PathDescription
/var/log/anchore/events/Default directory for data event files
/var/log/anchore/events/events.json.*Rotating data event files (timestamped)
/var/log/anchore/events/offsets.dbFluent Bit file position tracking database

Helm Values (Kubernetes)

To enable event streaming in a Kubernetes deployment using Helm, add the following to your values.yaml:

anchoreConfig:
  reports_worker:
    event_stream:
      enabled: true
      max_file_size_mb: 100
      max_file_count: 5
    cycle_timers:
      event_stream_health: 60

Config File (Docker Compose / Standalone)

For Docker Compose or standalone deployments, add the following to your config.yaml:

services:
  reports_worker:
    event_stream:
      enabled: true
      max_file_size_mb: 100
      max_file_count: 5
    cycle_timers:
      event_stream_health: 60

Volume Configuration

The Reports Worker and the Fluent Bit sidecar must have read/write access to the data event directory.

Kubernetes

Create a shared volume between the Reports Worker and Fluent Bit:

# In your Helm values or deployment manifest
volumes:
  - name: anchore-events
    emptyDir: {}

# Reports Worker container
volumeMounts:
  - name: anchore-events
    mountPath: /var/log/anchore

# Fluent Bit container
volumeMounts:
  - name: anchore-events
    mountPath: /var/log/anchore

Docker Compose

Use a named volume shared between containers:

volumes:
  anchore-events:

services:
  reports-worker:
    volumes:
      - anchore-events:/var/log/anchore

  fluent-bit:
    volumes:
      - anchore-events:/var/log/anchore:ro

File Rotation

Data event files are rotated based on the max_file_size_mb setting. When a file reaches the maximum size, a new file is created with a timestamp suffix:

events.json.20240115T103045Z
events.json.20240115T114532Z
events.json.20240115T125018Z

The max_file_count setting determines how many files are retained. Older files are deleted after they have been processed by Fluent Bit (tracked via the position database).

Health Monitoring

The data stream health watcher runs at the interval specified by event_stream_health and performs the following tasks:

  1. Cleanup: Removes event files that have been fully processed by Fluent Bit
  2. Emitter Resume Detection: Detects when the data_stream has been suspended and allows it to resumes processing when possible

Viewing Integration Status

The system event notification system provides events related to the data stream health. You can view these events via the API or UI. Filter on event type system.event_stream.suspend and system.event_stream.resume to see suspension and resumption events.

Verification

After enabling event streaming, verify it is working:

Step 1: Analyze an Image

Analyze a new image to generate vulnerability and policy events:

Step 2: Check Data Event Files

You should see one or more event files with the pattern events.json.*.

Examples below:

# Kubernetes
kubectl exec -it <reports-worker-pod> -- ls -la /var/log/anchore/events/
Defaulted container "enterprise-reportsworker" out of: enterprise-reportsworker, fluent-bit (init)
total 51680
drwxrwsrwx. 2 root    anchore      104 Jan 10 18:17 .
drwxrwxr-x. 1 anchore root         113 Jan 10 15:15 ..
-rw-r--r--. 1 anchore anchore 35440931 Jan 10 18:41 events.json.20260110T181643Z
-rw-r--r--. 1 anchore anchore     8192 Jan 10 18:02 offsets.db
-rw-r--r--. 1 anchore anchore    32768 Jan 10 18:41 offsets.db-shm
-rw-r--r--. 1 anchore anchore  4120032 Jan 10 18:41 offsets.db-wal


# Docker
docker exec <reports-worker-container> ls -la /var/log/anchore/events/
total 5092
drwxr-xr-x 2 root    root    4096 Jan 10 18:29 .
drwxrwxr-x 3 anchore root    4096 Jan 10 15:21 ..
-rw-r--r-- 1 root    root 5163541 Jan 10 15:26 events.json.20260110T152612Z
-rw-r--r-- 1 root    root    8192 Jan 10 17:53 offsets.db
-rw-r--r-- 1 root    root   32768 Jan 10 18:29 offsets.db-shm
-rw-r--r-- 1 root    root       0 Jan 10 18:29 offsets.db-wal
****

Troubleshooting

No Event Files Created

  1. Verify enabled: true is set in the configuration
  2. Check that the Reports Worker has write permissions to the directory
  3. Ensure the event_stream_health cycle timer is configured
  4. Check Reports Worker logs for errors

Events Not Being Processed

  1. Verify Fluent Bit is running and can read the event files
  2. Check the position database (offsets.db) exists and is being updated
  3. Review Fluent Bit logs for connection or parsing errors

Data Stream is Suspended

If the data stream becomes suspended due to unprocessed files accumulating, consider:

  1. Increase max_file_size_mb to buffer more date and allow fluent bit to catch up
  2. Increase max_file_count to retain more files during high-volume periods
  3. Ensure Fluent Bit is keeping up with event production

Next Steps

7.2 - Fluent Bit Integration

Overview

Fluent Bit is a lightweight, high-performance log processor and forwarder that serves as the bridge between Anchore Enterprise Enterprise event files and your destination system. This guide covers deploying Fluent Bit as a sidecar container to forward events to external systems.

Prerequisites

  • Data event streaming enabled in Anchore Enterprise
  • Shared volume configured between Reports Worker and Fluent Bit
  • Network access from Fluent Bit to your destination system

Architecture

Fluent Bit runs as a sidecar container alongside the Reports Worker, sharing a volume for event files:

┌──────────────────────────────────────────────────────────────────────┐
│                      Kubernetes Pod                                  │
│  ┌─────────────────┐                   ┌─────────────────────────┐   │
│  │  Reports Worker │                   │      Fluent Bit         │   │
│  │                 │                   │                         │   │
│  │   Data Event    │                   │  Tail Input Plugin      │   │
│  │     Emitter     │                   │         │               │   │
│  └────────┬────────┘                   │         ▼               │   │
│           │                            │  JSON Parser            │   │
│           │ writes                     │         │               │   │
│           ▼                            │         ▼               │   │
│  ┌──────────────────────────┐          │  Output Plugin ─────────┼───┼──► Splunk/Elastic/etc
│  │ /var/log/anchore/events/ │◄─────────┤  (HTTP/HEC)             │   │
│  │                          │  reads   │                         │   │
│  └──────────────────────────┘          └─────────────────────────┘   │
│       Shared Volume                                                  │
└──────────────────────────────────────────────────────────────────────┘

Deployment

Kubernetes (Helm)

Add a Fluent Bit sidecar to your Anchore Enterprise deployment by modifying your Helm values:

    reportsWorker:
      extraVolumes:
        - name: anchore-events
          emptyDir: {}
        - name: fluent-bit-config
          configMap:
            name: fluent-bit-config
            defaultMode: 0644
        # A LUA script can be added for ETL but the script is not provided
        #- name: fluent-bit-lua-helpers
        #  configMap:
        #    name: fluent-bit-lua-helpers
        #    defaultMode: 0644
      extraVolumeMounts:
        - name: anchore-events
          mountPath: /var/log/anchore/events
      initContainers:
        - name: fluent-bit
          image: fluent/fluent-bit:latest
          imagePullPolicy: IfNotPresent
          restartPolicy: Always
          ports:
            - containerPort: 2020
              name: metrics
              protocol: TCP
          volumeMounts:
            - name: fluent-bit-config
              mountPath: /fluent-bit/etc/fluent-bit.conf
              subPath: fluent-bit.conf
              readOnly: true
            - name: fluent-bit-config
              mountPath: /fluent-bit/etc/parsers.conf
              subPath: parsers.conf
              readOnly: true
            # A LUA script can be added for ETL but the script is not provided
            #- name: fluent-bit-lua-helpers
            #  mountPath: /fluent-bit/etc/anchore_helpers.lua
            #  subPath: anchore_helpers.lua
            - name: anchore-events
              mountPath: /var/log/anchore/events

Create a ConfigMap for Fluent Bit configuration:

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush           1
        Daemon          Off
        Log_Level       info
        Parsers_File    parsers.conf
        HTTP_Server     On
        HTTP_Listen     0.0.0.0
        HTTP_Port       2020

    [INPUT]
        Name            tail
        Path            /var/log/anchore/events/events.json.*
        Tag             anchore.events
        Parser          json
        DB              /var/log/anchore/events/offsets.db
        Mem_Buf_Limit   64MB
        Buffer_Chunk_Size 32MB
        Buffer_Max_Size 64MB
        Skip_Long_Lines Off
        Refresh_Interval 10
        Rotate_Wait     5
        Read_from_Head  On

    [FILTER]
        Name            modify
        Match           anchore.events
        Add             anchore_service reports_worker
    
    # A LUA script can be added for ETL but the script is not provided
    #[FILTER]
    #    Name    lua
    #    Match   anchore.events
    #    Script  /fluent-bit/etc/anchore_helpers.lua
    #    Call    split_and_wrap

    [OUTPUT]
        Name            splunk
        Match           anchore.events
        Host            ${SPLUNK_HEC_HOST}
        Port            ${SPLUNK_HEC_PORT}
        TLS             On
        TLS.Verify      On
        Splunk_Token    ${SPLUNK_HEC_TOKEN}
        Splunk_Send_Raw Off
        Event_Host      anchore-enterprise
        Event_Sourcetype anchore:events
        Retry_Limit     5

  parsers.conf: |
    [PARSER]
        Name        json
        Format      json
        Time_Key    timestamp
        Time_Format %Y-%m-%dT%H:%M:%S.%LZ
        Time_Keep   On

Docker Compose

Add Fluent Bit to your Docker Compose configuration:

services:
  fluent-bit:
    image: fluent/fluent-bit:latest
    restart: unless-stopped
    volumes:
      - ./fluent-bit/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
      - ./fluent-bit/parsers.conf:/fluent-bit/etc/parsers.conf:ro
      - anchore-events:/var/log/anchore:rw
    environment:
      SPLUNK_HEC_HOST: "${SPLUNK_HEC_HOST:-splunk}"
      SPLUNK_HEC_PORT: "${SPLUNK_HEC_PORT:-8088}"
      SPLUNK_HEC_TOKEN: "${SPLUNK_HEC_TOKEN}"
    ports:
      - "2020:2020"
    depends_on:
      - reports-worker
    networks:
      - anchore-network

volumes:
  anchore-events:

Create the configuration files in a fluent-bit/ directory:

fluent-bit/fluent-bit.conf:

[SERVICE]
    Flush           1
    Daemon          Off
    Log_Level       info
    Parsers_File    parsers.conf
    HTTP_Server     On
    HTTP_Listen     0.0.0.0
    HTTP_Port       2020

[INPUT]
    Name            tail
    Path            /var/log/anchore/events/events.json.*
    Tag             anchore.events
    Parser          json
    DB              /var/log/anchore/events/offsets.db
    Mem_Buf_Limit   64MB
    Buffer_Chunk_Size 32MB
    Buffer_Max_Size 64MB
    Skip_Long_Lines Off
    Refresh_Interval 10
    Rotate_Wait     5
    Read_from_Head  On

[FILTER]
    Name            modify
    Match           anchore.events
    Add             anchore_service reports_worker

[OUTPUT]
    Name            splunk
    Match           anchore.events
    Host            ${SPLUNK_HEC_HOST}
    Port            ${SPLUNK_HEC_PORT}
    TLS             On
    TLS.Verify      On
    Splunk_Token    ${SPLUNK_HEC_TOKEN}
    Splunk_Send_Raw Off
    Event_Host      anchore-enterprise
    Event_Sourcetype anchore:events
    Retry_Limit     5

fluent-bit/parsers.conf:

[PARSER]
    Name        json
    Format      json
    Time_Key    timestamp
    Time_Format %Y-%m-%dT%H:%M:%S.%LZ
    Time_Keep   On

Configuration Reference

Input Configuration

The tail input plugin monitors event files and tracks read positions:

ParameterValueDescription
NametailUse the tail input plugin
Path/var/log/anchore/events/events.json.*Pattern matching event files
Taganchore.eventsTag for routing to outputs
ParserjsonParse each line as JSON
DB/var/log/anchore/events/offsets.dbSQLite database for position tracking
Mem_Buf_Limit64MBMemory buffer limit
Buffer_Chunk_Size32MBBuffer chunk size for reading
Buffer_Max_Size64MBMaximum buffer size per file
Read_from_HeadOnRead from beginning for new files
Refresh_Interval10Seconds between file checks
Rotate_Wait5Seconds to wait before processing rotated files

Buffer Sizing

Vulnerability reports can be large (10-100+ KB per event). The buffer settings should accommodate your largest expected events:

Event TypeTypical SizeRecommended Buffer
Vulnerability Report (few CVEs)10-50 KB32 MB
Vulnerability Report (many CVEs)100-500 KB64 MB
Vulnerability Report (large image)500 KB - 2 MB128 MB
Policy Evaluation5-20 KB32 MB

For large images with many vulnerabilities, increase the buffer settings:

[INPUT]
    ...
    Mem_Buf_Limit   128MB
    Buffer_Chunk_Size 64MB
    Buffer_Max_Size 128MB

Position Tracking

Fluent Bit uses an SQLite database to track which events have been read and forwarded. This ensures:

  • Events are not re-sent after Fluent Bit restarts
  • Each file is tracked independently by inode
  • Progress is persistent across container restarts

The position database is stored at the path specified by DB and should be on the same volume as the event files.

Output Plugins

Fluent Bit supports multiple output destinations. Common options include:

Splunk

See the Splunk Integration guide for detailed configuration.

[OUTPUT]
    Name            splunk
    Match           anchore.events
    Host            ${SPLUNK_HEC_HOST}
    Port            ${SPLUNK_HEC_PORT}
    TLS             On
    TLS.Verify      On
    Splunk_Token    ${SPLUNK_HEC_TOKEN}

Elasticsearch

[OUTPUT]
    Name            es
    Match           anchore.events
    Host            elasticsearch.example.com
    Port            9200
    Index           anchore-events
    Type            _doc
    TLS             On
    TLS.Verify      On
    HTTP_User       ${ES_USER}
    HTTP_Passwd     ${ES_PASSWORD}

HTTP (Generic Webhook)

[OUTPUT]
    Name            http
    Match           anchore.events
    Host            webhook.example.com
    Port            443
    URI             /api/events
    Format          json
    TLS             On
    TLS.Verify      On
    Header          Authorization Bearer ${API_TOKEN}

Stdout (Debugging)

For troubleshooting, add stdout output to see events in container logs:

[OUTPUT]
    Name            stdout
    Match           anchore.events
    Format          json_lines

Filtering and Transformation

Adding Metadata

Add custom fields to all events:

[FILTER]
    Name            modify
    Match           anchore.events
    Add             environment production
    Add             cluster_name my-cluster
    Add             anchore_service reports_worker

Filtering by Event Type

Route different event types to different outputs:

[FILTER]
    Name            rewrite_tag
    Match           anchore.events
    Rule            $event ^(image\.vulnerability_report)$ vuln.$1 false
    Rule            $event ^(tag\.policy_evaluation)$ policy.$1 false

[OUTPUT]
    Name            splunk
    Match           vuln.*
    Host            ${SPLUNK_HEC_HOST}
    Splunk_Token    ${VULN_TOKEN}
    Event_Index     vulnerabilities

[OUTPUT]
    Name            splunk
    Match           policy.*
    Host            ${SPLUNK_HEC_HOST}
    Splunk_Token    ${POLICY_TOKEN}
    Event_Index     policy_evaluations

Troubleshooting

No Events Forwarded

  1. Check event files exist:

    ls -la /var/log/anchore/events/
    
  2. Verify Fluent Bit can read files:

    docker logs <fluent-bit-container> 2>&1 | grep -i "tail"
    
  3. Check position database:

    ls -la /var/log/anchore/events/offsets.db
    
  4. Enable debug logging:

    [SERVICE]
        Log_Level   debug
    

Connection Errors

  1. Verify network connectivity:

    # From inside the Fluent Bit container
    curl -k https://${SPLUNK_HEC_HOST}:${SPLUNK_HEC_PORT}/services/collector/health
    
  2. Check TLS settings: If using self-signed certificates, you may need TLS.Verify Off (not recommended for production)

  3. Verify credentials: Test HEC token directly:

    curl -k -X POST "https://${SPLUNK_HEC_HOST}:${SPLUNK_HEC_PORT}/services/collector/event" \
      -H "Authorization: Splunk ${SPLUNK_HEC_TOKEN}" \
      -d '{"event": "test"}'
    

Buffer Overflow

If you see buffer full errors:

  1. Increase buffer limits:

    Mem_Buf_Limit   128MB
    Buffer_Max_Size 128MB
    
  2. Check destination throughput - events may be produced faster than they can be forwarded

  3. Consider adding backpressure handling with storage.type filesystem

Re-sending All Events

To reset position tracking and re-send all events:

# Stop Fluent Bit
# Delete the position database
rm /var/log/anchore/events/offsets.db
# Restart Fluent Bit

Next Steps

7.3 - Splunk Integration

Overview

This guide covers integrating Anchore Enterprise data streaming with Splunk using the HTTP Event Collector (HEC). Once configured, vulnerability reports and policy evaluations will flow into Splunk for search, alerting, and dashboard visualization.

Prerequisites

Architecture

┌─────────────────────┐       ┌─────────────────────┐       ┌─────────────────────┐
│  Anchore Enterprise │       │     Fluent Bit      │       │       Splunk        │
│                     │       │                     │       │                     │
│  Reports Worker     │──────►│  Tail + JSON Parse  │──────►│  HTTP Event         │
│  Event Files        │ NDJSON│  Splunk Output      │ HTTPS │  Collector (HEC)    │
│                     │       │                     │       │                     │
└─────────────────────┘       └─────────────────────┘       └─────────────────────┘
                                                            ┌─────────────────────┐
                                                            │  Splunk Index       │
                                                            │  - Search           │
                                                            │  - Dashboards       │
                                                            │  - Alerts           │
                                                            └─────────────────────┘

Splunk Configuration

Step 1: Enable HTTP Event Collector

Enable HEC globally in Splunk:

Via Splunk Web UI:

  1. Navigate to Settings > Data Inputs > HTTP Event Collector
  2. Click Global Settings
  3. Set All Tokens to Enabled
  4. Configure Default Source Type to anchore:events
  5. Click Save

Via REST API:

curl -k -u admin:<password> -X POST \
  https://<splunk-host>:8089/servicesNS/nobody/splunk_httpinput/data/inputs/http/http/enable

Step 2: Create HEC Token

Create a dedicated HEC token for Anchore Enterprise events:

Via Splunk Web UI:

  1. Navigate to Settings > Data Inputs > HTTP Event Collector
  2. Click New Token
  3. Configure:
    • Name: anchore_events
    • Source type: anchore:events
    • Index: main (or create a dedicated index)
  4. Click Submit
  5. Copy the generated token value

Via REST API:

curl -k -u admin:<password> -X POST \
  https://<splunk-host>:8089/servicesNS/nobody/splunk_httpinput/data/inputs/http \
  -d "name=anchore_events" \
  -d "sourcetype=anchore:events" \
  -d "index=main"

The response will include the token value.

For better data management, create a dedicated index for Anchore Enterprise events:

Via Splunk Web UI:

  1. Navigate to Settings > Indexes
  2. Click New Index
  3. Configure:
    • Index Name: anchore_events
    • Max Size: Based on your retention needs
  4. Click Save
  5. Update your HEC token to use this index

Via REST API:

curl -k -u admin:<password> -X POST \
  https://<splunk-host>:8089/services/data/indexes \
  -d "name=anchore_events" \
  -d "datatype=event"

Fluent Bit Configuration

Configure Fluent Bit to forward events to Splunk HEC:

[OUTPUT]
    Name            splunk
    Match           anchore.events
    Host            ${SPLUNK_HEC_HOST}
    Port            ${SPLUNK_HEC_PORT}
    TLS             On
    TLS.Verify      On
    Splunk_Token    ${SPLUNK_HEC_TOKEN}
    Splunk_Send_Raw Off
    Event_Host      anchore-enterprise
    Event_Sourcetype anchore:events
    Event_Index     anchore_events
    Retry_Limit     5

Configuration Options

ParameterDescriptionExample
HostSplunk HEC hostnamesplunk.example.com
PortSplunk HEC port8088
TLSEnable TLS encryptionOn
TLS.VerifyVerify TLS certificatesOn
Splunk_TokenHEC authentication tokenyour-token-here
Splunk_Send_RawSend raw JSON eventsOff
Event_HostHost field value in Splunkanchore-enterprise
Event_SourcetypeSourcetype for eventsanchore:events
Event_IndexTarget Splunk indexanchore_events
Retry_LimitNumber of retry attempts5

Environment Variables

Set these environment variables for Fluent Bit:

VariableDescriptionExample
SPLUNK_HEC_HOSTSplunk HEC hostnamesplunk.example.com
SPLUNK_HEC_PORTSplunk HEC port8088
SPLUNK_HEC_TOKENHEC authentication tokenyour-hec-token

TLS Configuration

For production deployments, always enable TLS verification:

[OUTPUT]
    Name            splunk
    ...
    TLS             On
    TLS.Verify      On
    TLS.CA_File     /path/to/ca-bundle.crt

If using self-signed certificates (not recommended for production):

[OUTPUT]
    Name            splunk
    ...
    TLS             On
    TLS.Verify      Off

Verification

Step 1: Test HEC Connectivity

Test the HEC endpoint directly:

curl -k -X POST "https://<splunk-host>:8088/services/collector/event" \
  -H "Authorization: Splunk <your-token>" \
  -d '{"event": "test event from anchore"}'

Expected response:

{"text":"Success","code":0}

Step 2: Check Fluent Bit Logs

Verify Fluent Bit is connecting to Splunk:

# Kubernetes
kubectl logs <fluent-bit-pod> | grep -i splunk

# Docker
docker logs <fluent-bit-container> 2>&1 | grep -i splunk

Look for:

  • [output:splunk:splunk.0] worker #0 started
  • No connection errors

Step 3: Search for Events in Splunk

Run a search in Splunk to verify events are arriving:

index=anchore_events sourcetype="anchore:events"

Or search for specific event types:

index=anchore_events event="image.vulnerability_report"
index=anchore_events event="tag.policy_evaluation"

Event Schema

Vulnerability Report Event

{
  "event": "image.vulnerability_report",
  "timestamp": "2024-01-15T10:30:45.123Z",
  "account_name": "admin",
  "resource_id": "sha256:abc123...",
  "payload": {
    "image_digest": "sha256:abc123...",
    "total_added": 15,
    "total_removed": 3,
    "added": [
      {
        "vulnerability_id": "CVE-2024-1234",
        "severity": "Critical",
        "package_name": "openssl",
        "package_version": "1.1.1k",
        "fixed_in": "1.1.1l",
        "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-1234"
      }
    ],
    "removed": []
  }
}

Policy Evaluation Event

{
  "event": "tag.policy_evaluation",
  "timestamp": "2024-01-15T10:31:00.456Z",
  "account_name": "admin",
  "resource_id": "docker.io/library/alpine:latest",
  "payload": {
    "result": "fail",
    "policy_id": "default",
    "image_digest": "sha256:abc123...",
    "findings": [
      {
        "gate": "vulnerabilities",
        "trigger": "package",
        "action": "stop",
        "message": "Critical vulnerability found: CVE-2024-1234"
      }
    ]
  }
}

Splunk Searches

Basic Searches

All Anchore Enterprise Events:

index=anchore_events sourcetype="anchore:events"

Vulnerability Reports Only:

index=anchore_events event="image.vulnerability_report"

Policy Evaluations Only:

index=anchore_events event="tag.policy_evaluation"

Failed Policy Evaluations:

index=anchore_events event="tag.policy_evaluation" payload.result="fail"

Vulnerability Analysis

Critical Vulnerabilities:

index=anchore_events event="image.vulnerability_report"
| spath path=payload.added{} output=vulns
| mvexpand vulns
| spath input=vulns
| where severity="Critical"
| table _time, account_name, resource_id, vulnerability_id, package_name, fixed_in

Top 10 Most Common CVEs:

index=anchore_events event="image.vulnerability_report"
| spath path=payload.added{} output=vulns
| mvexpand vulns
| spath input=vulns
| stats count by vulnerability_id
| sort -count
| head 10

Vulnerabilities by Severity:

index=anchore_events event="image.vulnerability_report"
| spath path=payload.added{} output=vulns
| mvexpand vulns
| spath input=vulns
| stats count by severity
| sort -count

Images with Most Vulnerabilities:

index=anchore_events event="image.vulnerability_report"
| stats sum(payload.total_added) as total_vulns by resource_id
| sort -total_vulns
| head 10

Policy Analysis

Policy Violations by Gate:

index=anchore_events event="tag.policy_evaluation" payload.result="fail"
| spath path=payload.findings{} output=findings
| mvexpand findings
| spath input=findings
| stats count by gate
| sort -count

Recent Policy Failures:

index=anchore_events event="tag.policy_evaluation" payload.result="fail"
| table _time, account_name, resource_id, payload.policy_id
| sort -_time
| head 20

Dashboards

Creating a Vulnerability Dashboard

Create a new dashboard in Splunk with the following panels:

Panel 1: Vulnerability Count Over Time

index=anchore_events event="image.vulnerability_report"
| timechart sum(payload.total_added) as "New Vulnerabilities"

Panel 2: Severity Distribution

index=anchore_events event="image.vulnerability_report"
| spath path=payload.added{} output=vulns
| mvexpand vulns
| spath input=vulns
| stats count by severity

Panel 3: Top Vulnerable Images

index=anchore_events event="image.vulnerability_report"
| stats sum(payload.total_added) as vulns by resource_id
| sort -vulns
| head 10

Creating a Policy Compliance Dashboard

Panel 1: Pass/Fail Ratio

index=anchore_events event="tag.policy_evaluation"
| stats count by payload.result

Panel 2: Policy Compliance Over Time

index=anchore_events event="tag.policy_evaluation"
| timechart count by payload.result

Panel 3: Recent Failures

index=anchore_events event="tag.policy_evaluation" payload.result="fail"
| table _time, account_name, resource_id, payload.policy_id
| sort -_time

Alerting

Critical Vulnerability Alert

Create an alert for new critical vulnerabilities:

Search:

index=anchore_events event="image.vulnerability_report"
| spath path=payload.added{} output=vulns
| mvexpand vulns
| spath input=vulns
| where severity="Critical"
| stats count as critical_count by resource_id
| where critical_count > 0

Alert Settings:

  • Trigger: Number of results > 0
  • Throttle: 1 hour per resource_id
  • Action: Email, Slack, or PagerDuty

Policy Failure Alert

Create an alert for policy failures:

Search:

index=anchore_events event="tag.policy_evaluation" payload.result="fail"
| stats count by resource_id, payload.policy_id

Alert Settings:

  • Trigger: Number of results > 0
  • Throttle: Based on your requirements
  • Action: Your preferred notification method

Troubleshooting

No Events in Splunk

  1. Verify HEC is enabled:

    curl -k "https://<splunk-host>:8089/services/data/inputs/http?output_mode=json" \
      -u admin:<password>
    
  2. Test HEC endpoint:

    curl -k -X POST "https://<splunk-host>:8088/services/collector/event" \
      -H "Authorization: Splunk <token>" \
      -d '{"event": "test"}'
    
  3. Check Fluent Bit logs for errors:

    docker logs <fluent-bit-container> 2>&1 | tail -50
    
  4. Verify network connectivity:

    # From Fluent Bit container
    curl -k https://<splunk-host>:8088/services/collector/health
    

Authentication Errors

If you see 401 Unauthorized errors:

  1. Verify the HEC token is correct
  2. Check the token is enabled in Splunk
  3. Ensure the token has permission to write to the target index

TLS Errors

If you see certificate errors:

  1. Verify the CA certificate is correct
  2. Check certificate chain is complete
  3. For testing only: Set TLS.Verify Off (not recommended for production)

Missing Fields

If fields are not appearing in Splunk:

  1. Verify the sourcetype is set correctly
  2. Check field extractions in Splunk
  3. Use spath command to extract JSON fields in searches

Performance Tuning

High Volume Environments

For high-volume deployments:

  1. Increase Fluent Bit workers:

    [SERVICE]
        Workers     4
    
  2. Enable compression:

    [OUTPUT]
        Name            splunk
        ...
        compress        gzip
    
  3. Batch events:

    [OUTPUT]
        Name            splunk
        ...
        Batch_Size      2048
    

Splunk Indexer Optimization

  1. Create a dedicated index for Anchore Enterprise events
  2. Configure appropriate retention policies
  3. Consider using indexed extractions for frequently searched fields

Next Steps