keyless image signing with Cosign and validation with Kyverno

Shi
CI/CD/DevOps
Published in
2 min readFeb 26, 2024

--

I am trying to keyless sign an image in Github action, and then deploy it to k3s and then apply some ClusterPolicy to ensure that only signed image can be deployed.

pod yaml

apiVersion: v1
kind: Pod
metadata:
name: cg
namespace: app
spec:
containers:
- image: ghcr.io/whoissqr/cg-test-keyless-sign:latest
name: cg-test-keyless-sign
resources: {}

policy to validate image is signed

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-image-keyless
spec:
validationFailureAction: Enforce
failurePolicy: Fail
background: false
webhookTimeoutSeconds: 30
rules:
- name: check-image-keyless
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/whoissqr/cg-test-keyless-sign:latest"
attestors:
- entries:
- keyless:
subject: "https://github.com/whoissqr/cg-test-keyless-sign/.github/workflows/main.yml@refs/heads/main"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.dev

Github Action

name: Publish and Sign Container Image

on:
schedule:
- cron: '32 11 * * *'
push:
branches: [ main ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
pull_request:
branches: [ main ]

jobs:
build:

runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install cosign
uses: sigstore/cosign-installer@v3.2.0

- name: Check install!
run: cosign version

- name: Setup Docker buildx
uses: docker/setup-buildx-action@v2

- name: Log into ghcr.io
uses: docker/login-action@master
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push container image
id: push-step
uses: docker/build-push-action@master
with:
push: true
tags: ghcr.io/${{ github.repository }}:latest

- name: Sign the images with GitHub OIDC Token
env:
DIGEST: ${{ steps.push-step.outputs.digest }}
TAGS: ghcr.io/${{ github.repository }}
COSIGN_EXPERIMENTAL: "true"
run: |
echo "dont sign image"
# cosign sign --yes "${TAGS}@${DIGEST}"

- name: (optional) Verify the images
run: |
cosign verify ghcr.io/whoissqr/cg-test-keyless-sign \
--certificate-identity https://github.com/whoissqr/cg-test-keyless-sign/.github/workflows/main.yml@refs/heads/main \
--certificate-oidc-issuer https://token.actions.githubusercontent.com | jq

- name: Create k3s cluster
uses: debianmaster/actions-k3s@master
id: k3s
with:
version: 'latest'

- name: Install Kyverno chart
run: |
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install --atomic kyverno kyverno/kyverno -n kyverno --create-namespace
sleep 10

- name: Apply image attestation policy
run: |
kubectl apply -f ./k3s/policy-check-image-keyless.yaml

- name: Deploy pod to k3s
if: always()
run: |
kubectl create ns app
kubectl apply -f ./k3s/pod.yaml
kubectl -n app wait --for=condition=Ready pod/cg
kubectl get pods -n app

- name: (optional) Install Kyverno CLI
if: always()
uses: kyverno/action-install-cli@v0.2.0
with:
release: 'v1.9.5'

- name: (optional) Dry run policy using Kyverno CLI
if: always()
run: |
kyverno version
# kyverno apply ./k3s/policy-check-image-keyless.yaml --cluster -v 10
kubectl get clusterpolicies -o yaml | kyverno apply - --resource ./k3s/pod.yaml



if we comment out the step `cosign sign — yes “${TAGS}@${DIGEST}”`, we will see the image validation will fail and pod deployment will be rejected and kyverno cli dry run will show the same.

--

--

Shi
CI/CD/DevOps

I am a coder/engineer/application security specialist. I like to play around with language and tools; I have strong interest in efficiency improvement.