Image Signature Verification with Kyverno

Page content

In this post I will show you how you can use Kyverno and Cosign for Image Signature Verification in a Kubernetes cluster.

Parts of the K8S Security Lab series

Container Runetime Security
Advanced Kernel Security
Network Security
Secure Kubernetes Install
User Security
Image Security
  • Part1: Image security Admission Controller
  • Part2: Image security Admission Controller V2
  • Part3: Image security Admission Controller V3
  • Part4: Continuous Image security
  • Part5: trivy-operator 1.0
  • Part6: trivy-operator 2.1: Trivy-operator is now an Admisssion controller too!!!
  • Part7: trivy-operator 2.2: Patch release for Admisssion controller
  • Part8: trivy-operator 2.3: Patch release for Admisssion controller
  • Part8: trivy-operator 2.4: Patch release for Admisssion controller
  • Part8: trivy-operator 2.5: Patch release for Admisssion controller
  • Part9_ Image Signature Verification with Connaisseur
  • Part10: Image Signature Verification with Connaisseur 2.0
  • Part11: Image Signature Verification with Kyverno
  • Part12: How to use imagePullSecrets cluster-wide??
  • Part13: Automatically change registry in pod definition
  • Part14: ArgoCD auto image updater
    Pod Security
    Secret Security
    Monitoring and Observability
    Backup

    Wat is Cosign?

    Cosign is a new open-source tool to manage the process of signing and verifying container images. Developed by Googele in collaboration with Linux Foundation’s sigstore project. The motivation for cosign is “to make signatures invisible infrastructure.” With Images signed by Cosign you didn’t neet to change your infrastructure to store the public signing key, like Notary. (With Notary you need a Notary server connected to your registry to store the keys) With Cosign, the signatures directly appear as tags of the image linked to the associated image via the digest:

    Notice how the signature tag below corresponds to the sha256 digest of the image tag ‘latest’ above.

    Key Management options:

    • fixed, text-based keys generated using cosign generate-key-pair
    • cloud KMS-based keys generated using cosign generate-key-pair -kms
    • keys generated on hardware tokens using the PIV interface using cosign piv-tool
    • Kubernetes-secret based keys generated using cosign generate-key-pair -k8s

    Installing Cosign

    It’s a golang project, so it’s fairly easy to get started, there’s a single binary available from their release pag and it has been signed by them.

    Generate key pair with:

    $ cosign generate-key-pair
    Enter password for private key:
    Enter again:
    Private key written to cosign.key
    Public key written to cosign.pub
    

    Sign an image with cosign:

    docker pull alpine:edge
    docker tag alpine:edge devopstales/testimage:unsigned
    docker push devopstales/testimage:unsigned
    
    
    docker pull alpine:latest
    docker tag alpine:latest devopstales/testimage:cosign
    docker push devopstales/testimage:cosign
    
    
    cosign sign -key ~/data/cosign.key devopstales/testimage:cosign
    Enter password for private key: 
    Pushing signature to: index.docker.io/devopstales/testimage:sha256-4661fb57f7890b9145907a1fe2555091d333ff3d28db86c3bb906f6a2be93c87.sig
    

    Verify a container against a public key:

    $ cosign verify -key ~/data/cosign.pub devopstales/testimage:cosign
    
    Verification for devopstales/testimage:cosign --
    The following checks were performed on each of these signatures:
      - The cosign claims were validated
      - The signatures were verified against the specified public key
      - Any certificates were verified against the Fulcio roots.
    {"critical":{"identity":{"docker-reference":"index.docker.io/devopstales/testimage"},"image":{"docker-manifest-digest":"sha256:4661fb57f7890b9145907a1fe2555091d333ff3d28db86c3bb906f6a2be93c87"},"type":"cosign container image signature"},"optional":null}
    

    Image Signature Verification tools

    In a previous post I used Connaisseur to Image Signature Verification. I could youse Connaisseur with Cosign too but with the new release of Kyverno we didn’t need to deploy a separate tool for Image Signature Verification. We can use Kyverno’s verifyImages rule.

    It validate signatures for matching images using Cosign and mutates image references with the digest returned by Cosign. Using an image digest guarantees immutability of images and hence improves security.

    Install the latest version of Kyverno:

    kubectl create -f https://raw.githubusercontent.com/kyverno/kyverno/main/definitions/release/install.yaml
    

    Patch the Kyverno webhook, to allow time for calling the OCI registry:

    kubectl patch mutatingwebhookconfigurations kyverno-resource-mutating-webhook-cfg \
    --type json \
    -p='[{"op": "replace", "path": "/webhooks/0/failurePolicy", "value": "Ignore"},{"op": "replace", "path": "/webhooks/0/timeoutSeconds", "value": 15}]'
    

    Here is a policy that verifies all images from a specific repository:

    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
      name: check-image
    spec:
      validationFailureAction: enforce
      background: false
      rules:
        - name: check-image
          match:
            resources:
              kinds:
                - Pod
          verifyImages:
          - image: "docker.io/devopstales/testimage:*"
            key: |-
              -----BEGIN PUBLIC KEY-----
              MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL53O1V5FP2Vaa60BTwRjrOxhuu5C
              iB/mODf/V2eiGw+WbA689ZZRjWwXCf+4jwzfRSrik0YvTCMqvl3BDaPG2A==
              -----END PUBLIC KEY-----          
    
    kubectl create deployment signed-my \
    --image=devopstales/testimage:cosign
    

    Try running an unsigned image that matches the configured rule:

    kubectl create deployment unsigned-my \
    --image=docker.io/devopstales/testimage:unsigned
    

    This will be blocked:

    error: failed to create deployment: admission webhook "mutate.kyverno.svc" denied the request: 
    
    resource Deployment/kyverno-system/unsigned-my was blocked due to the following policies
    
    verify-image:
      autogen-verify-image: 'image verification failed for docker.io/devopstales/testimage:unsigned:
        failed to verify image: fetching signatures: getting signature manifest: GET https://index.docker.io/v2/devopstales/testimage/manifests/sha256-0119f88f395766eb52f9b817c3d23576bf31935dc8e94abe14bae9a083ce4639.sig:
        MANIFEST_UNKNOWN: manifest unknown; map[Tag:sha256-0119f88f395766eb52f9b817c3d23576bf31935dc8e94abe14bae9a083ce4639.sig]'