In the dynamic world of cloud-native applications, Kubernetes has become the de facto standard for orchestrating containers. However, as organizations embrace the agility and scalability of Kubernetes, they also inherit a critical challenge: securing the software supply chain. From source code commit to container deployment, every step presents a potential attack vector. A compromised dependency, an untrusted build environment, or a tampered image can lead to devastating breaches, making robust supply chain security not just a best practice, but a necessity.
This is where SLSA (Supply-chain Levels for Software Artifacts) comes into play. SLSA is a security framework, a set of standards and controls, designed to prevent tampering, improve integrity, and secure packages and infrastructure. It provides a common language and a clear path to enhance the security posture of your software artifacts. By adopting SLSA principles within your Kubernetes ecosystem, you can establish verifiable trust throughout your build and deployment pipelines, ensuring that the code running in your clusters is exactly what you intended, and nothing more.
This guide will walk you through implementing SLSA principles for your Kubernetes deployments. We’ll leverage tools like Sigstore for cryptographically signing artifacts and Kyverno for enforcing policies directly within your Kubernetes clusters. By the end of this tutorial, you’ll have a foundational understanding and practical implementation of how to secure your Kubernetes supply chain, mitigating risks and building greater confidence in your deployments.
TL;DR: Kubernetes Supply Chain Security with SLSA
Secure your Kubernetes supply chain using SLSA principles, Sigstore for artifact signing, and Kyverno for policy enforcement. This guide demonstrates how to sign container images and verify their authenticity before deployment, ensuring only trusted artifacts run in your cluster.
Key Commands:
# Install Sigstore tools (cosign)
go install github.com/sigstore/cosign/cmd/cosign@latest
# Generate a key pair for signing
cosign generate-key-pair
# Sign your container image
cosign sign --key cosign.key <YOUR_IMAGE_NAME>
# Install Kyverno
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
# Apply a Kyverno policy to enforce signed images
kubectl apply -f policy-enforce-signed-images.yaml
This setup creates a robust defense against unauthorized or compromised container images.
Prerequisites
Before diving into the implementation, ensure you have the following tools and knowledge:
- Kubernetes Cluster: A running Kubernetes cluster (v1.20+). This can be a local cluster like Minikube or Kind, or a cloud-managed service like EKS, GKE, or AKS.
kubectl: The Kubernetes command-line tool, configured to connect to your cluster. Refer to the official Kubernetes documentation for installation instructions.helm: The Kubernetes package manager, used for installing Kyverno. See the Helm installation guide.go: Go programming language (v1.18+) is required to installcosign. Download from golang.org.- Container Registry: Access to a container registry (e.g., Docker Hub, Google Container Registry (GCR), Amazon Elastic Container Registry (ECR), Quay.io).
- Basic Understanding of Kubernetes: Familiarity with Deployments, Pods, Namespaces, and Custom Resources.
- Basic Understanding of Container Images: How they are built and pushed to registries.
Step-by-Step Guide: Implementing SLSA with Sigstore and Kyverno
Our journey to a more secure supply chain will involve several key steps:
- Setting up Sigstore (Cosign) for artifact signing.
- Building and signing a sample container image.
- Installing Kyverno in your Kubernetes cluster.
- Creating and applying a Kyverno policy to enforce image signing.
- Testing the enforcement.
1. Set Up Sigstore (Cosign)
Sigstore provides a set of tools and services for cryptographically signing software artifacts. Its flagship tool, Cosign, allows you to sign and verify container images, supply chain metadata, and other artifacts. We’ll start by installing Cosign and generating a key pair for signing.
First, ensure you have Go installed, then install Cosign. We’ll generate an asymmetric key pair. The private key will be used for signing, and the public key will be used for verification. For simplicity in this tutorial, we’ll store these locally. In a production environment, you’d integrate with a KMS (Key Management System) or a hardware security module (HSM).
Install Cosign
# Install cosign using go install
go install github.com/sigstore/cosign/cmd/cosign@latest
# Verify installation
cosign version
Expected Output
# Example output (version may vary)
Cosign:
Version: v2.2.0
GitCommit: b80d0d85a11c8d1bb43b910408546f7c8701e6a0
...
Generate a Key Pair
# Generate a private/public key pair.
# You will be prompted to enter a password for the private key.
# REMEMBER THIS PASSWORD!
cosign generate-key-pair
# List the generated files
ls -l cosign.key cosign.pub
Expected Output
# You will be prompted:
Enter password for private key:
Repeat password for private key:
# After entering the password:
Private key written to cosign.key
Public key written to cosign.pub
# Listing files:
-rw------- 1 user group 1675 Nov 20 10:00 cosign.key
-rw-r--r-- 1 user group 451 Nov 20 10:00 cosign.pub
You now have cosign.key (your private key) and cosign.pub (your public key). Keep cosign.key secure and never share its password.
2. Build and Sign a Sample Container Image
Next, we’ll create a simple container image, push it to a registry, and then sign it using the key pair we just generated. This signed image will then be used in our Kubernetes deployment.
We’ll use a basic Nginx image for demonstration. Replace <YOUR_REGISTRY_PATH> with your actual registry path (e.g., docker.io/yourusername/, gcr.io/your-project/).
Create a Dockerfile
# Dockerfile
FROM nginx:latest
LABEL maintainer="Kubezilla"
RUN echo "This is a signed Nginx image by Kubezilla!" > /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Build and Push the Image
# Define your image name
export IMAGE_NAME="<YOUR_REGISTRY_PATH>/kubezilla-signed-nginx:v1.0.0"
# Build the image
docker build -t ${IMAGE_NAME} .
# Log in to your container registry if needed
# docker login <YOUR_REGISTRY_HOST>
# Push the image to your registry
docker push ${IMAGE_NAME}
Expected Output
# Example output for docker build and push
...
Successfully built <IMAGE_ID>
Successfully tagged <YOUR_REGISTRY_PATH>/kubezilla-signed-nginx:v1.0.0
The push refers to repository [<YOUR_REGISTRY_PATH>/kubezilla-signed-nginx]
...
v1.0.0: Pushed
Sign the Image
Now, sign the image using cosign. You will be prompted for the password you set for cosign.key.
# Sign the image using your private key
cosign sign --key cosign.key ${IMAGE_NAME}
Expected Output
# You will be prompted:
Enter password for private key:
# After entering the password:
Pushing signature to: <YOUR_REGISTRY_PATH>/kubezilla-signed-nginx:sha256-<IMAGE_DIGEST>.sig
Cosign pushes the signature to your container registry alongside the image, but as a separate artifact. This signature is verifiable using the public key.
3. Install Kyverno in Your Kubernetes Cluster
Kyverno is a Kubernetes native policy engine that can validate, mutate, and generate configurations. It allows you to define policies as Kubernetes resources and enforce them automatically. For our purpose, Kyverno will enforce that only images signed with our specific public key are allowed to run in the cluster. We’ll install it using Helm.
Kyverno offers powerful capabilities for security and governance. For more advanced network security policies within Kubernetes, you might also find our guide on Kubernetes Network Policies: Complete Security Hardening Guide useful, which complements Kyverno’s admission control features.
Install Kyverno via Helm
# Add the Kyverno Helm repository
helm repo add kyverno https://kyverno.github.io/kyverno/
# Update your Helm repositories
helm repo update
# Install Kyverno into its own namespace
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
Expected Output
# Example output for Helm installation
Release "kyverno" has been upgraded. Happy Helming!
NAME: kyverno
LAST DEPLOYED: <DATE TIME>
NAMESPACE: kyverno
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Kyverno has been installed.
...
Verify Kyverno Installation
# Check if Kyverno pods are running
kubectl get pods -n kyverno
Expected Output
# Example output
NAME READY STATUS RESTARTS AGE
kyverno-69f67878d-abcde 1/1 Running 0 2m
kyverno-cleanup-crd-abc 1/1 Running 0 2m
4. Create and Apply a Kyverno Policy to Enforce Image Signing
Now we’ll create a Kyverno ClusterPolicy that mandates all container images used in deployments must be signed by our public key. This policy will leverage Sigstore’s verification capabilities.
This policy is a critical component of your supply chain security. It acts as an admission controller, preventing unsigned or improperly signed images from ever being deployed. For an even deeper dive into securing your container supply chain with Sigstore and Kyverno, including advanced features, refer to our dedicated guide: Securing Container Supply Chains with Sigstore and Kyverno.
Create the Kyverno Policy YAML
Replace <YOUR_PUBLIC_KEY_CONTENT> with the actual content of your cosign.pub file. You can get this content by running cat cosign.pub.
# policy-enforce-signed-images.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
annotations:
policies.kyverno.io/category: Software Supply Chain Security
policies.kyverno.io/description: >-
This policy enforces that all container images used in Pods must be signed
by a specific Sigstore public key. It prevents the deployment of
unsigned or untrusted images, enhancing supply chain security.
spec:
validationFailureAction: Enforce # Can be Audit or Enforce
background: false # Set to true to scan existing resources
rules:
- name: check-image-signatures
match:
any:
- resources:
kinds:
- Pod
namespaces:
- default # Apply to the default namespace for this demo
# - dev
# - prod
verifyImages:
- imageReferences:
- "*" # Apply to all images in the specified namespaces
key: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElzV/x04aC3z2z5N8z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0z0
z0z0z0z0z
