This blog post is just a quick guide on how to scan and verify container images with Trivy, Kyverno and Cosign.
- Trivy: an all in one, cloud native security scanner
- Cosign: Cosign is a command line utility that can sign and verify software artifact, such as container images.
- Kyverno: Kubernetes Native Policy Management
Prerequisites
This tutorial will use the following GitHub repository: https://github.com/Cloud-Native-Security/website
Next, ensure that
- You have Trivy installed
- Have Cosign installed
- Docker installed, running and authenticated
- Kyverno installed
Step one: build and sign a container image
First, we will build the container image for the website repository. Note that you can use one of your applications.
git clone https://github.com/Cloud-Native-Security/website
cd website
docker build -t anaisurlichs/signed-example:0.1 .
Note: Replace the DockerHub “anaisurlichs” handle with your own DockerHub Handle.
Push the container image to your DockerHub Account
We need to push the container image to DockerHub:
docker push anaisurlichs/signed-example:0.1
Sign the container image with Cosign
First, we need to generate a new cosign keypair:
cosign generate-key-pair
The private key generated will be used to sign our artefact – never ever share it with anyone, otherwise they can pretend you signed artefacts that you never touched – and the public key can be shared with people to verify that you are the one who signed specific artefacts.
Once the container image is in your DockerHub account, you can go ahead and use Cosign to sign it. In this case, we are going to use the SHA of the container image tag that you can find on the DockerHub site of the image tag. Alternatively, run the following Docker command to get the SHA for your container image:
docker image ls --digests
cosign sign --key cosign.key docker.io/anaisurlichs/signed-example@sha256:c5911ac313e0be82a740bd726dc290e655800a9588424ba4e0558c705d1287fd
Scan the container Image with Trivy for vulnerabilities & generate an attestation
trivy image --ignore-unfixed --format cosign-vuln --output vuln.json docker.io/anaisurlichs/signed-example@sha256:c5911ac313e0be82a740bd726dc290e655800a9588424ba4e0558c705d1287fd
Create an attestation:
cosign attest --key cosign.key --type vuln --predicate vuln.json docker.io/anaisurlichs/signed-example@sha256:c5911ac313e0be82a740bd726dc290e655800a9588424ba4e0558c705d1287fd
The attestation basically says that you are the one who created a vulnerability scan for this container image.
Verify the Attestation:
cosign verify-attestation --key cosign.pub --type vuln anaisurlichs/signed-example@sha256:c5911ac313e0be82a740bd726dc290e655800a9588424ba4e0558c705d1287fd
Create a Kubernetes cluster and install Kyverno Helm CHart
kind create cluster --name kyverno
Note that you can use any other cluster as well. I am just using KinD here.
Next, install the Kyverno Helm Chart in your cluster as detailed in the following tutorial:
https://anaisurl.com/defining-kubernetes-native-policies-with-kyverno-full-tutorial/
And on their documentation: https://kyverno.io/docs/installation/methods/
# Add the Kyverno Helm repository to your repository list helm
repo add kyverno https://kyverno.github.io/kyverno/
# Scan your Helm repositories to fetch the latest available charts
helm repo update
# Install the Kyverno Helm chart into a new namespace called "kyverno"
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
Install a policy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-vulnerabilities
spec:
validationFailureAction: Enforce
background: false
webhookTimeoutSeconds: 30
failurePolicy: Fail
rules:
- name: checking-vulnerability-scan-not-older-than-one-hour
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "*"
attestations:
- type: https://cosign.sigstore.dev/attestation/vuln/v1
conditions:
- all:
- key: "{{ time_since('','{{ metadata.scanFinishedOn }}', '') }}"
operator: LessThanOrEquals
value: "1h"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
abc
xyz
-----END PUBLIC KEY-----
Replace the lines below the `publicKeys` part with your public key i.e. what is in the cosign.pub file.
Next, apply the policy:
kubectl apply –f policy.yaml
Verify only the signed image can be deployed to your cluster
Next, try to install an unsigned container image to your cluster:
kubectl run app-unsigned --image=docker.io/anaisurlichs/cns-website:0.1.1
As you can see, this does not work.
Lastly, try to install the container image that you have signed and that contains the attestation:
kubectl run app-signed --image= docker.io/anaisurlichs/signed-example@sha256:c5911ac313e0be82a740bd726dc290e655800a9588424ba4e0558c705d1287fd
Lastly, clean up your cluster:
kubectl delete pod app-signed
kubectl delete –f policy.yaml
What’s next
Huge shout out to Unni for helping me in the Trivy Slack community to make this tutorial work!!
You can find a list of various Kyverno policies in their documentation, go try them out!
Also, have a look at the other Trivy features.
If you enjoyed this tutorial, make sure to give it a thumbs up on my YouTube channel!