Introduction
Image signature verification is an important step if you want to deploy applications securely on Kubernetes clusters. It allows customers to ensure no unexpected or malicious changes affect the images along the entire CI/CD flow.
Images stored in a registry can be signed using several tools like the Notary Server and the Cosign/Sigstore, both sign digitally an specific tag or, as a best practice, push a signature of specific SHA ID
There are several tools to sign and verify images, some of them includes Notary Server and Cosign/Sigstore.
Notary Server 
Notary is an Open Source project to sign, store and verify signatures securely with delegated servers for verification and authentication.
Notary is a project developed by Docker which provides identification and authentication for Docker Images. Notary was designed with the idea to ensure identification, integrity and authentication of the images.
Cosign/Sigstore
Cosign is designed to enable developers to create and push signatures keyless, using created keys or through the KMS system
Image signatures can be verified and integrated by several other tools in Kubernetes like Kyverno, Connaisseur or Rekor through the usage of admission controllers.
In conjunction with Sigstore, provides authentication with GitHub OAuth, Gmail and more, allowing identification and authentication to images
Deploying applications securely
Deploying applications securely challenges developers in a number of ways. First, developers need to be aware of the potential threats posed to their application and the data it stores. This includes both external threats, such as malicious actors trying to gain access to the application, and internal threats, such as employees inadvertently exposing sensitive data.
Kubernetes relies on external tools and sources to ensure the image did not suffer any unexpected or malicious changes during the promotion workflow but it does not have any native system to verify images or signatures.
Trusted images in Kubernetes
The implementation of these solutions will use Kyverno and Cosign to verify images preventing non-trusted images from being deployed in production.
Building the environment where only trusted images can be deployed in production will use this application
- Build and sign images with Cosign
- Implement a Kyverno policies
- Kyverno admission controller validates and enforces or audit actions based on the policies created above.
- Eventing
Building and signing images with Cosign
With Cosign images can be signed and this signature can be used to verify images before being deployed.
This Proof of Concept uses the Github repository kyverno_sigstore_verification to follow up the steps along this article moving along the concepts
Once the image is ready went through all the required security standards, images can be signed using Cosign:
$ cosign sign --key <KEYNAME>.key DOCKERUSER/test:signed
- While both tags and SHA ID It is a good practice to sign the SHA ID instead of tags.
The image is now signed using the provided keys and can be pushed to the repository, signatures and tags can be looked at the repository
The application image are there with both tags and the signatures with the format of sha256-<id>
Policy Management System – Kyverno
Kyverno is a Policy Management Tool using a Kubernetes Admission Controller to ensure and/or audit policy violation,It consists of a set of Custom Resource Definitions (CRDs) and an admission controller defining cluster policies, policies reports and cluster policies reports.
Helm can deploy Kyverno deployed as explained here the deploy should finish soon with the pods running on the namespace kyverno
NAME READY STATUS RESTARTS AGE
kyverno-696d66bc7b-4xx4q 1/1 Running 0 20h
kyverno-696d66bc7b-c8css 1/1 Running 0 20h
kyverno-696d66bc7b-xrr66 1/1 Running 0 20h
kyverno-cleanup-controller-5dc5d7774-jwpjt 1/1 Running 0 20h
Deploy Kyverno policy from the repository
kubectl create -f cpolicy.yaml
Looking at the policy details:
[...]
spec:
background: false
failurePolicy: Fail
rules:
- match:
any:
- resources:
kinds:
- Pod
name: verify-image
verifyImages:
- image: '*'
key: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/IjWWdh7Jd6p72Y/1D+TmVGjIp2cukXhWknErGhBIwnM5p9wc69S8+/bETXbgUfU7Y9N5grQo9x/u+qZDlHoA==
-----END PUBLIC KEY-----
verifyDigest: true
validationFailureAction: Audit
validationFailureActionOverrides:
- action: Enforce
namespaces:
- prod
[...]
The above policy takes effect immediately, so let´s review.
- The policy match with any Pod in any namespace
- The validation happens with the PUBLIC KEY of the pair used to sign the image previously
- There are two more filters:
- validationFailureAction: Audit —> warning by default on every unsigned image deployed on each namespace
- validationFailureActionOverrides: this parameter is used to override the previous validationFailureAction on a specific namespace, in this case any unsigned image on prod namespace will be denied by Kyverno admission controller.
Verify Kyverno and policies
New objects are validated against the Kyverno policies to pass, fail, warn, error or skip triggering a policy report.
Every time a new object is created it passes throughout the policies triggering a policy report.
Let’s see kyverno on action!
error: failed to create deployment: admission webhook "mutate.kyverno.svc-fail" denied the request:
policy Deployment/prod/unsigned for resource violation:
check-image-signature:
autogen-verify-image: |
failed to verify image docker.io/gmontalvoy/test:unsigned: .attestors[0].entries[0].keys: no matching signatures
The admission controller prevented the the unsigned image to be deployed on a production namespace but allows the trusted ones
❯ kc create deployment signed --image=gmontalvoy/test:signed -n prod
deployment.apps/signed created
The Kyverno Admission Controller intercepted the application based on the policy declared above.

Image by Sysdig article: https://sysdig.com/blog/image-scanning-admission-controller/
Audit events
What about the non-production namespaces? The kyverno policy includes an audit policy targeting other namespaces than ´prod´.When an unsigned image is deployed on a non-production namespace targeted by an audit-type policy (validationFailureAction: Audit) it will trigger a policyReport or polr kubernetes objects with the related violation even though the object was allowed to deploy.
❯ kc get polr -A
NAMESPACE NAME PASS FAIL WARN ERROR SKIP AGE
dev cpol-check-image-signature 0 1 0 0 0 19m
prod cpol-check-image-signature 1 0 0 0 0 19m
As shown above, there are two policy reports, one informing about the signed image was deployed on prod as specify by the policy:
results:
- message: image verified
policy: check-image-signature
resources:
- apiVersion: v1
kind: Pod
name: signed-5b6c94c79b-jkwhh
namespace: prod
uid: 69c13f62-f8e0-4be7-8665-c6943fb31639
result: pass
rule: verify-image
scored: true
severity: medium
source: kyverno
In the results section of the policy report, there is the validation performed on a prod namespace.
The results of the non-production reports produce a result showing the resource is violating the trusted image policy but it is just a warning not preventing the image to be deployed on a non-production namespace.
results:
- message: unverified image docker.io/gmontalvoy/test@sha256:6234a29e5fe3235109059262e86d4ffddcc5785ce5abbf73973aa25cc9fb3915
policy: check-image-signature
resources:
- apiVersion: v1
kind: Pod
name: unsigned-85755b54c-flw7x
namespace: dev
uid: a8e4f91e-cbea-4fb5-9bb4-53e0cbd9f28c
result: fail
Conclusion
As more and more institutions and customers are being targeted and attacked, the importance of security fully integrated within the CI/CD workflow is more important than ever before.
Applications in kubernetes needs to be compliant and secure and image verification through signatures, Cosign and Sigstore are projects into the Kubernetes framework ensuring compliance and security standards when it comes to deploy new applications.
While enforcing and auditing policies takes more importance, these events can be pushed to observability tools like Grafana, Prometheus and others providing observability layer to admission controller tools.
References
- Kyverno Docs: https://kyverno.io/docs/
- Admissions Controllers: https://sysdig.com/blog/kubernetes-admission-controllers/
- Cosign / Sigstore: https://docs.sigstore.dev/cosign/overview/
