Image Signing and Attestation with Trivy, Kyverno, and Cosign

Image Signing and Attestation with Trivy, Kyverno, and Cosign


In this tutorial, we are combining three amazing security tools: Trivy, Cosign and Kyverno to sign, attest container images and enforce those through Policies in our Kubernetes cluster.

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 


This tutorial will use the following GitHub repository:

Next, ensure that 

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 
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

Scan the container Image with Trivy for vulnerabilities & generate an attestation 

trivy image --ignore-unfixed  --format cosign-vuln --output vuln.json 

 Create an attestation:  

cosign attest --key cosign.key --type vuln --predicate vuln.json 

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 --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:  

And on their documentation:  

# Add the Kyverno Helm repository to your repository list helm 
repo add 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 

kind: ClusterPolicy 
  name: check-vulnerabilities 
  validationFailureAction: Enforce 
  background: false 
  webhookTimeoutSeconds: 30 
  failurePolicy: Fail 
    - name: checking-vulnerability-scan-not-older-than-one-hour 
        - resources: 
              - Pod 
      - imageReferences: 
        - "*" 
        - type: 
          - all: 
            - key: "{{ time_since('','{{ metadata.scanFinishedOn }}', '') }}" 
              operator: LessThanOrEquals 
              value: "1h" 
          - count: 1 
            - keys: 
                publicKeys: |- 
                  -----BEGIN PUBLIC KEY----- 
                  -----END PUBLIC KEY----- 

Replace the lines below the `publicKeys` part with your public key i.e. what is in the 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 

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= 

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!