When deploying in a truly cloud-native way, we need to ensure that we can trust what we’re deploying at every step of our supply chain. We need to be certain that what we think we are deploying is what we are actually deploying.
We recently made GitHub Artifact Attestations generally available, which allows you to create provenance and integrity guarantees to verify what you have built within GitHub Actions can be traced back to its source code. This gives your software engineers and your end users the confidence that your supply chain is secure. With many organizations facing more regulatory and compliance requirements, this process meets SLSA v1.0 Build Level 2 requirements and allows your teams to make informed decisions around your builds.
With this new feature you can create attestations for any type of artifact, whether it’s an executable, a package (such as npm, NuGet, Maven), container registry (Docker, GitHub Packages, Azure Container Registry), or even a .zip file.
In this blog post, we share steps to configure your GitHub Actions workflow for Artifact Attestations, so you can learn how to attest your packages and verify your builds within your CI/CD workflows in a truly cloud-native way. In the specific use case below, we’re going to verify our builds with the Kubernetes admission controller.
Configuring the GitHub Action in your workflow
To attest the provenance of an artifact generated within a workflow, simply add the attest-build-provenance action
after you have built your artifact in your workflow. The action and documentation of how to use it can be found here.
Let’s walk through an existing workflow that builds and outputs an artifact. Before we begin, ensure that you have access to edit the workflow, and then install the GitHub CLI so that we can verify the attestations we’ll be creating.
Now we will configure the build provenance action and verify it!
- Open your existing workflow (or create a new one, just remember to build your artifact). Add in the action after the artifact is built and supply the path to the artifact that you would like to generate the attestation for.
name: build-attest on: workflow_dispatch: jobs: build: permissions: id-token: write contents: read attestations: write steps: - name: Checkout uses: actions/checkout@v4 - name: Build artifact run: make our-app - name: Artifact Attestation uses: actions/attest-build-provenance@v1 with: subject-path: '${{ github.workspace }}/our-app'
- To further customize your inputs, check out the documentation to look at other inputs and outputs that are easily configurable.
For example, you choose to store the attestation in an OCI image registry, or specify the GitHub token as shown below:
- uses: actions/attest-build-provenance@v1 with: # Path to the artifact serving as the subject of the attestation. Must # specify exactly one of "subject-path" or "subject-digest". May contain a # glob pattern or list of paths (total subject count cannot exceed 2500). subject-path: # SHA256 digest of the subject for the attestation. Must be in the form # "sha256:hex_digest" (e.g. "sha256:abc123..."). Must specify exactly one # of "subject-path" or "subject-digest". subject-digest: # Subject name as it should appear in the attestation. Required unless # "subject-path" is specified, in which case it will be inferred from the # path. subject-name: # Whether to push the attestation to the image registry. Requires that the # "subject-name" parameter specify the fully-qualified image name and that # the "subject-digest" parameter be specified. Defaults to false. push-to-registry: # The GitHub token used to make authenticated API requests. Default is # ${{ github.token }} github-token:
- Trigger your workflow. In this example, we’re using a manual trigger. Once the workflow runs successfully, you can view the attestations in the left-hand pane under your “Actions” tab; select “Attestations.”
-
Once you click on your attestations, you can view the attestation, along with useful information about the build. You can also download the attestation to your machine.
-
After the workflow has run and the attestation has been created, you will want to verify it. To verify the attestation, open a terminal and run the following command:
gh attestation verify PATH/TO/ARTIFACT -o myorganization
Note: in the step above, the exact path to the built artifact is provided. You can easily copy/paste this into the CLI or even automate the verification using the verify output.
-
That’s it! Adding the action is really easy to configure and verifying your package is really simple.
From the above steps, you can see that adding the build provenance action along with verifying the output/attestation is very easy to configure and verify. Let’s now look at how we can also attest and the container images that we use for our cloud-native deployments.
Validating our Kubernetes clusters and images
The ability to validate packages and images in our container registries brings security and traceability to the forefront of our cloud-native supply chain.
Remember, we want to ensure what we have built is actually what has been deployed. Whether you’re a DevOps engineer, platform/infrastructure engineer, or security specialist there are always questions with our deployments: are our images and clusters free from security vulnerabilities? Did we follow the approved process to ensure our images and clusters are production ready? Or even, where is the source of this image and have we done our due diligence in ensuring quality?
For this purpose, GitHub provides a Kubernetes admission controller that can be used to validate incoming deployments and reject images without verifiable attestations. Our admission controller consists of two Helm charts: the first one installs the Sigstore policy controller, which performs the verification step, and the second one loads GitHub’s trust root and a default policy.
Let’s now have a look at how we can ensure that what is running in our Kubernetes clusters is actually what we have built.
- Before installing the controller, you can verify that it’s actually coming from GitHub by running the following command:
gh attestation verify --owner github \ oci://ghcr.io/github/artifact-attestations-helm-charts/policy-controller:v0.10.0-github5
Our output looks like this, confirming that it was created by the repository that was expected:
-
Once that is verified, we can then install the Sigstore policy controller by running the follow command:
helm install policy-controller --atomic \ --create-namespace --namespace artifact-attestations \ oci://ghcr.io/github/artifact-attestations-helm-charts/policy-controller \ --version v0.10.0-github5
- Then, we install the GitHub TrustRoot and a default Cluster Image Policy:
helm install trust-policies --atomic \ --namespace artifact-attestations \ oci://ghcr.io/github/artifact-attestations-helm-charts/trust-policies \ --version v0.5.0 \ --set policy.enabled=true \ --set policy.organization=MY-ORGANIZATION
Note: make sure to set the policy.organization to a specific organization. There is a full set of Cluster Image Policies values that can be customized.
-
Once the above policy has been installed, it will only be enforced when the namespace has been specified. Add the following annotation to enable enforcement in a namespace (each namespace in your cluster can be enforced independently).
metadata: annotations: policy.sigstore.dev/include: true
Or, you can also run the following kubectl command:
kubectl label namespace MYNAMESPACE policy.sigstore.dev/include=true
- We can look more closely at what the Helm chart contains, then allowing us to apply it to the cluster. The attestation data that we are reviewing for acceptance into our cluster will be in JSON format:
-
We’ve now confirmed that the Cluster Image Policy contains exactly what is expected! It will display only the images that have been signed by our organization. We have built, confirmed, and attested our container images and Helm charts!
Good luck getting started on your artifact attestation journey with your cloud-native deployments!
Watch the tutorial on YouTube
Written by