One of the advantages of automated CI/CD pipelines is that they’re a great place to implement regular security controls and checks. Using GitHub Actions, it’s easy to improve the security of your containers by automating vulnerability scanning and digital signing of container images on a regular basis. In this post, we’ll go over how to set up and secure a CI/CD pipeline using GitHub Actions, Cosign, and Trivy.
Adding an action to an existing repository
In a GitHub repository, you can add new workflows from the Actions tab.
GitHub provides a set of pre-packaged actions. We’re going to use one of them, publish docker container
, as a starting point. When you click configure
, GitHub automatically adds a new action to the repository. For this action, the repository that we’re using as a base must have a Dockerfile present.
When I first looked at this action, I got a pleasant surprise, which is that Cosign is present by default! Cosign is part of the Sigstore project. It provides an easy-to-use digital signing process that can be easily integrated into other parts of your environment — for example, allowing only signed images to run in a Kubernetes cluster. GitHub added Cosign as a default option to this workflow in December last year, so hopefully, over time we’ll see increasing adoption. For the signing of artifacts to become a widely used feature, it needs adoption, so steps like this are really helpful.
One thing that’s different about using Cosign, as it’s set up in this action, is that no user key management is needed for signing, which has often been a barrier to adoption. Instead, Cosign gets a short-lived key matching the identity of the GitHub repository and stores the signature on a transparency log, so it can be verified later. From the user’s perspective, this makes the process much simpler.
Adding Trivy to our action
Individual GitHub actions can carry out a number of steps, so it makes sense to add our vulnerability scanning at the same time as we’re building our Docker image. Here we can leverage Trivy’s GitHub Action to add vulnerability scanning and use GitHub code scanning to view the results. Code scanning is free for all public repositories, but might not be available on private repos.
To make this work, there are a couple of places where we need to add some instructions. The first is in the permissions
block at the start of the build. To upload our results to the code scanning page, we need the security-events: write
permission. Once we’ve got that, our permissions block should look like this:
Now to add the actions themselves. The example from Trivy’s GitHub repository assumes we’re using Docker Hub, so we’ll need to change it a bit. The image-ref
has a set of environment variables that should match the output of the previous build action.
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: {% raw %} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} {% endraw %}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: 'trivy-results.sarif'
With that information added, our action should work automatically, and we’ll get the information about noted vulnerabilities in the GUI:
Downloading and checking our image
Now that we’ve got this CI/CD pipeline working, we can download our image from GitHub Container Registry and verify the signature. For this example, I’m using one of my repositories, alpine-containertools
.
docker pull ghcr.io/raesene/alpine-containertools:main
Once the image is pulled, we can verify the signature. At the moment, the keyless signing approach that this GitHub action uses is experimental, so we need to pass a flag to Cosign to verify the image.
COSIGN_EXPERIMENTAL=1 cosign verify ghcr.io/raesene/alpine-containertools:main
This will verify and report on which key signed the image and confirm a good signature.
Conclusion
Using a combination of GitHub Actions, Cosign, and Trivy, it’s relatively easy to start improving your software supply chain security. While signing and scanning images aren’t the whole story, they provide a good starting point, giving visibility into potential issues and allowing users of your container images to verify that the images haven’t been modified by unauthorized parties after signing.