Customizing EC data

I’ll work through real-world example.

Cluster auth

Visit this url, authenticate and click "Display Token".

Copy the oc login command and paste it into a terminal.

$ oc login --token=.... --server=....
Logged into "https://api.stone-prd-rh01.pg1f.p1.openshiftapps.com:6443" as "simonbaird" using the token provided.

You have access to 134 projects, the list has been suppressed. You can list all projects with ' projects'

Using project "default".

Extracting the public key

kubectl get -n openshift-pipelines secret public-key -o json | jq -r '.data."cosign.pub" | @base64d' > cosign.pub

Extracting the default ECP config

kubectl get -n enterprise-contract-service enterprisecontractpolicy default -o yaml | yq .spec > policy.yml

Running EC locally

Install ec as per these instructions then do the following.

Replace the image ref with your image ref.

TODO: Explain how to find the image ref.

export IMAGE=quay.io/redhat-appstudio/user-workload:rrqte-devfile-sample-python-basic6-agrl
ec validate image --image $IMAGE --public-key cosign.pub --policy "$(yq -ojson -I0 policy.yml)" | yq -P

In the above example we downloaded the public key and the policy, but since we have cluster access, it’s not actually required. EC can find the public key and the policy configuration in the cluster. For example we can do this, which should give the same result:

ec validate image --image $IMAGE --policy enterprise-contract-service/default | yq -P

For this example I’ll continue using the local key and policy, but the concepts process would be the same if we used the cluster records directly.

For reference, here is the ec output I’m getting currently, with the default policy:

success: true
components:
  - name: Unnamed
    containerImage: quay.io/redhat-appstudio/user-workload@sha256:de1c78cd8321ec999187dfc95ed5470b4a5b2bf5121a2482dba7b5965868253d
    violations: []
    warnings: []
    success: true
    successCount: 31
    signatures: ...
key: ...

And here is the default policy:

configuration:
  collections:
    - minimal
description: |
  Default EnterpriseContractPolicy. If a different policy is desired, this resource can serve as a starting point.
publicKey: k8s://openshift-pipelines/public-key
sources:
  - data:
      - oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest
      - github.com/release-engineering/rhtap-ec-policy//data
    name: Default Policies
    policy:
      - oci::quay.io/enterprise-contract/ec-release-policy:latest@sha256:16703532b485c4edd3cbe47f62d44a51be4b7390b663e86eb5a7372ba9ecae52

Notice that the EC is currently passing. There are no violations and the top level success value is true.

Modify the policy so all rules are applied

Notice the configuration currenly specifies the minimal collection. This means there are a subset of the rules being applied. You can see the list of rules in the documentation here.

You can also use ec to produce a list of the rules like this:

ec inspect policy --source quay.io/enterprise-contract/ec-release-policy --collection minimal --output text

By default EC applies all the rules found in the policy source. So we can just remove the collection configuration to make this happen.

Let’s do that and re-run the EC locally.

Edit the policy.yml file:

configuration: {}
sources:
  - data:
      - oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest
      - github.com/release-engineering/rhtap-ec-policy//data
    policy:
      - oci::quay.io/enterprise-contract/ec-release-policy:latest@sha256:16703532b485c4edd3cbe47f62d44a51be4b7390b663e86eb5a7372ba9ecae52
To make these examples tidier I removed some description and name fields that won’t be consequential. You can leave them alone or modify them if you wish.

Running the EC with this new policy configuration produces some violations:

ec validate image --image $IMAGE --public-key cosign.pub --policy "$(yq -ojson -I0 policy.yml)" | yq -P .components.[].violations
- msg: Build task was not invoked with hermetic parameter
  metadata:
    code: hermetic_build_task.build_task_not_hermetic
    effective_on: "2022-01-01T00:00:00Z"
- msg: Required task "prefetch-dependencies" is missing
  metadata:
    code: tasks.missing_required_task
    effective_on: "2022-01-01T00:00:00Z"
- msg: Required task "sast-snyk-check" is missing
  metadata:
    code: tasks.missing_required_task
    effective_on: "2022-01-01T00:00:00Z"
- msg: Step 0 in task 'show-summary' has disallowed image ref 'quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:9f0cdc00b1b1a3c17411e50653253b9f6bb5329ea4fb82ad983790a6dbf2d9ad'
  metadata:
    code: step_image_registries.disallowed_task_step_image
    effective_on: "2022-01-01T00:00:00Z"
- msg: Test "sanity-label-check" did not complete successfully
  metadata:
    code: test.test_result_failures
    effective_on: "2022-01-01T00:00:00Z"

Modify the policy to skip some rules that are currently failing

Let’s say you are aware of the reasons for the violations and want to skip them. You can do this using the exclude configuration field.

Modify the policy.yaml file:

configuration:
  exclude:
    - hermetic_build_task.build_task_not_hermetic
    - tasks.missing_required_task:prefetch-dependencies
    - tasks.missing_required_task:sast-snyk-check
    - test.test_result_failures:sanity-label-check
sources:
  - data:
      - oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest
      - github.com/release-engineering/rhtap-ec-policy//data
    policy:
      - oci::quay.io/enterprise-contract/ec-release-policy:latest@sha256:16703532b485c4edd3cbe47f62d44a51be4b7390b663e86eb5a7372ba9ecae52

Re-run ec:

ec validate image --image $IMAGE --public-key cosign.pub --policy "$(yq -ojson -I0 policy.yml)" | yq -P .components.[].violations
- msg: Step 0 in task 'show-summary' has disallowed image ref 'quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:9f0cdc00b1b1a3c17411e50653253b9f6bb5329ea4fb82ad983790a6dbf2d9ad'
  metadata:
    code: step_image_registries.disallowed_task_step_image
    effective_on: "2022-01-01T00:00:00Z"

There’s one violation left. We could skip that one too, but I want to use that violation as example of how to provide custom data.

Modify the in-cluster policy

The steps above make modifications to a local copy of the policy. It is also possible to create the policy in the cluster.

The default policy is a good place to start, fetch its full form:

kubectl get -n enterprise-contract-service enterprisecontractpolicy default -o yaml > local-policy.yml

Next, use an editor to modify the policy as needed. Remove the .metadata.namespace attribute. We will provide a namespace later.

At the beginning of this document, you logged in on a member cluster. This is, currently, required in order to access the default EnterpriseContractPolicy and the public key. However, resources on the cluster cannot be created this way. They must be created via the proxy. To do so, obtain a token from here by clicking on the "Proxy login command" link on the top right. Paste that in your terminal.

Then, create a resource on the cluster for the policy you have modified locally:

kubectl create -f local-policy.yml

If you have more than one namespace, choose one by specifying the -n flag for the command above.

Now, log back in to the member cluster as mentioned in Cluster auth.

Finally, use the new policy. If the policy was created on the namespace alice-tenant and was named alice-policy, the policy can be used as:

ec validate image --image $IMAGE --policy alice-tenant/alice-policy | yq -P

Adding a custom data source

The violation message tells us that the task is using a disallowed image ref.

Rather than skip the test, let’s imagine you want to modify the behavior of the step_image_registries.disallowed_task_step_image rule to allow images from the quay.io/openshift-release-dev.

This is the kind of thing that a Konflux user might want to do based on what security policies they decide are appopriate for their situation.

First, let’s take a look at how that rule works. The documentation is here.

Clicking through to the code we can see a reference to allowed_step_image_registry_prefixes. This refers to some data that comes from the data source.

There should be an easier way for users to know what data is related to a rule. Perhaps just mentioning it in the annotation description is a good starting point.

The data source in the policy config from github.com/release-engineering/rhtap-ec-policy//data includes all the data from here. The one from oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest is dynamically updated when Tasks/Pipelines are modified in the redhat-appstudio/build-definitions repository. You can view the combined data sources like this:

ec inspect policy-data \
  --source github.com/release-engineering/rhtap-ec-policy//data \
  --source oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest
This would be less confusing if we had the ec inspect data command, which is planned in HACBS-1732.

Looking at the rule_data.yml file we can see that allowed_step_image_registry_prefixes is a list of strings which define the policy about which specify defines the policy enforced by the step_image_registries.disallowed_task_step_image rule.

Creating the data source

The easiest way to create a custom data source is to use a git repo in GitHub. For this example I did it like this:

mkdir ec-data-demos
cd ec-data-demos/
git init .
git remote add origin git@github.com:simonbaird/ec-data-demos.git # Repo created in GitHub already
mkdir step_registry_prefixes
vi step_registry_prefixes/data.yml # Use content from below
git add step_registry_prefixes/data.yml
git commit -m "Add custom step registry prefixes"
git push origin main

The contents of step_registry_prefixes looks like this:

rule_data_custom:
  allowed_step_image_registry_prefixes:
    - trusted-registry.example.io/secure-task-runners/

See the file in GitHub here.

That’s not a real registry prefix of course, but let’s start with that and see what results EC produces.

Configuring EC to use the data source

Let’s modify the policy.yml file to add an extra data source:

configuration:
  exclude:
    - hermetic_build_task.build_task_not_hermetic
    - tasks.missing_required_task:prefetch-dependencies
    - tasks.missing_required_task:sast-snyk-check
    - test.test_result_failures:sanity-label-check
sources:
  - data:
      - oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest
      - github.com/release-engineering/rhtap-ec-policy//data
      - git::https://github.com/simonbaird/ec-data-demos//step_registry_prefixes
    policy:
      - oci::quay.io/enterprise-contract/ec-release-policy:latest
Actually we could have left out the oci:: and git::https:// because ec knows that github.com is for git and quay.io is for container images.

Now let’s run ec again:

ec validate image --image $IMAGE --public-key cosign.pub --policy "$(yq -ojson -I0 policy.yml)" | yq -P '.components.[].violations.[].msg'
Step 0 in task 'build-container' has disallowed image ref 'quay.io/redhat-appstudio/buildah@sha256:381e9bfedd59701477621da93892106873a6951b196105d3d2d85c3f6d7b569b'
Step 0 in task 'clair-scan' has disallowed image ref 'quay.io/redhat-appstudio/clair-in-ci@sha256:fd6affa3ae32625609b96642129c489bed4cee0a9426cb0e203bd5949eba98d8'
Step 0 in task 'clamav-scan' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 0 in task 'clone-repository' has disallowed image ref 'registry.redhat.io/openshift-pipelines/pipelines-git-init-rhel8@sha256:2fa0b06d52b04f377c696412e19307a9eff27383f81d87aae0b4f71672a1cd0b'
Step 0 in task 'deprecated-base-image-check' has disallowed image ref 'registry.access.redhat.com/ubi8/ubi-minimal@sha256:0214a28336e387c66493c61bb394e86a18f3bea8dbc46de74a26f173ff553c89'
Step 0 in task 'init' has disallowed image ref 'registry.redhat.io/openshift4/ose-tools-rhel8@sha256:253d042ecfad7b64593112a4aa3f528d39cb5fe738852e44f009db87964cf051'
Step 0 in task 'sanity-inspect-image' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 0 in task 'sanity-label-check' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 0 in task 'sanity-optional-label-check' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 0 in task 'sbom-json-check' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 0 in task 'show-summary' has disallowed image ref 'quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:9f0cdc00b1b1a3c17411e50653253b9f6bb5329ea4fb82ad983790a6dbf2d9ad'
Step 1 in task 'build-container' has disallowed image ref 'quay.io/redhat-appstudio/syft@sha256:09afc449976230f66848c19bb5ccf344eb0eeb4ed50747e33b53aff49462c319'
Step 1 in task 'clair-scan' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 1 in task 'clamav-scan' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 1 in task 'deprecated-base-image-check' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 2 in task 'build-container' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-jvm-build-request-processor@sha256:b198cf4b33dab59ce8ac25afd4e1001390db29ca2dec83dc8a1e21b0359ce743'
Step 2 in task 'clair-scan' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 2 in task 'clamav-scan' has disallowed image ref 'quay.io/redhat-appstudio/hacbs-test@sha256:2cbe93facff681d03ca71d2bf9edab99549906ac9c275979457cd0bca4311ba7'
Step 3 in task 'build-container' has disallowed image ref 'registry.redhat.io/ubi9/python-39@sha256:a4833397a08024b156f7bccbf220b3012c206ba902767978de640128132f30c7'
Step 4 in task 'build-container' has disallowed image ref 'registry.access.redhat.com/ubi9/buildah@sha256:c8b1d312815452964885680fc5bc8d99b3bfe9b6961228c71a09c72ca8e915eb'
Step 5 in task 'build-container' has disallowed image ref 'quay.io/redhat-appstudio/cosign@sha256:18b3716a6225727877475e1ab4f2493915e72cffd2ce431e9901d2ed2e4b2c0b'

As you might have predicted, EC is now reporting that all the steps are using disallowed image refs.

Let’s fix that now. Back in the git repo for your custom data source, modify step_registry_prefixes/data.yml to look like this:

rule_data_custom:
  allowed_step_image_registry_prefixes:
    - quay.io/redhat-appstudio/
    - quay.io/openshift-release-dev/
    - registry.access.redhat.com/
    - registry.redhat.io/

Then push it up to GitHub:

git add step_registry_prefixes/data.yml
git commit -m "More useful set of step image prefixes"
git push origin main

Running ec again with the updated data source should now give you as passing results:

ec validate image --image $IMAGE --public-key cosign.pub --policy "$(yq -ojson -I0 policy.yml)" | yq -P
success: true
components:
  - name: Unnamed
    containerImage: quay.io/redhat-appstudio/user-workload@sha256:de1c78cd8321ec999187dfc95ed5470b4a5b2bf5121a2482dba7b5965868253d
    violations: []
    warnings:
      - msg: Pipeline task 'build-container' uses an out of date task bundle 'quay.io/redhat-appstudio-tekton-catalog/task-buildah:0.1@sha256:3ec1cd16f0467534db8e4d1ffcacfb18a5801acc763e8dcd4d92c292e3aa3de6'
        metadata:
          code: attestation_task_bundle.out_of_date_task_bundle
          effective_on: "2022-01-01T00:00:00Z"
      - msg: Required tasks do not exist for pipeline
        metadata:
          code: tasks.missing_required_pipeline_task_warning
          effective_on: "2022-01-01T00:00:00Z"
    success: true
    successCount: 38
    signatures: ...
key: ...

Adding a custom policy source

Now imagine your organization has a need for a custom policy. A policy that doesn’t exactly fit in with the default. The solution is to create a custom policy repo and apply that to your next EC run.

Creating the custom policy source

To create a custom policy, we’ll start by creating a git repo for it. If you don’t have a git repo created already, let’s start by creating one. If you do, start by creating the file policy/myorg.rego with the content below.

mkdir ec-data-demos
cd ec-data-demos/
git init .
git remote add origin git@github.com:joejstuart/ec-data-demos.git # Repo created in GitHub already
mkdir policy/
vi policy/myorg.rego # Use content from below
git policy/myorg.rego
git commit -m "Add custom task policy"
git push origin main
policy/myorg.rego
#
# METADATA
# description: |-
#   Verify "myorg-task" does not exist in a build
#
package policy.myorg

import future.keywords.contains
import future.keywords.if
import future.keywords.in

import data.lib


# METADATA
# title: myorg-task does not exist
# description: |-
#   This policy enforces that a task named "myorg-task" does not exist in a build
# custom:
#   short_name: myorg_task_missing
#   failure_msg: myorg-task missing from build pipeline
deny contains result if {
  count({task | some task in input.predicate.buildConfig.tasks; task.name == "myorg-task"}) == 0
  result := lib.result_helper(rego.metadata.chain(), [])
}

The above policy will throw a violation if a task named myorg-task is not found in the build.

Configuring EC to use the custom policy source

Let’s modify the policy.yml file to add an extra policy source:

policy.yaml
configuration:
  exclude:
    - hermetic_build_task.build_task_not_hermetic
    - tasks.missing_required_task:prefetch-dependencies
    - tasks.missing_required_task:sast-snyk-check
    - test.test_result_failures:sanity-label-check
sources:
  - data:
      - oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest
      - github.com/release-engineering/rhtap-ec-policy//data
    policy:
      - oci::quay.io/enterprise-contract/ec-release-policy:latest
      - git::https://github.com/joejstuart/ec-policy-demo//policy
The policy source oci::quay.io/enterprise-contract/ec-release-policy:latest will give you access to some useful helper methods defined here. For instance, lib.result_helper(rego.metadata.chain(), []) collects information from the custom annotation and uses it in the ec-cli output. This can be helpful debugging violations.

Now let’s run ec again:

ec validate image --image $IMAGE --public-key cosign.pub --policy "$(yq -ojson -I0 policy.yml)" | yq -P '.components.[].violations.[].msg'
ec validate image output
- msg: myorg-task missing from build pipeline
  metadata:
    code: myorg.myorg_task_missing

Now, in the output you can see there is a new violation. Our build does not contain a task named 'myorg-task'.