DEV Community

DEVOPS DYNAMO
DEVOPS DYNAMO

Posted on

How to Enforce Allowed Kubernetes Image Registries with Kyverno

If you prefer learning by watching rather than reading, the full lab walkthrough is available in video form on my YouTube channel.

Kubernetes Image Security with Kyverno (Real-World Lab) - Part 1

Controlling which container registries can be used inside a Kubernetes cluster is a core part of supply chain security. If workloads can pull images from any external source, you lose visibility and risk introducing untrusted software. A simple way to lock this down at admission time is to use Kyverno.

This guide walks through building policies that only allow images from approved registries and block everything else. The same approach applies in real production clusters and aligns well with CKS preparation.

Objective

Only permit images from:

registry.company.io

harbor.internal.local

Any Pod pulling from an unapproved registry, including Docker Hub, should be denied during admission.

Confirming the Active Cluster Context

Before you start, verify you're working on the correct cluster:

kubectl config current-context
kubectl get nodes
Enter fullscreen mode Exit fullscreen mode

If the context looks wrong, switch:

kubectl config use-context <context>
kubectl get nodes
Enter fullscreen mode Exit fullscreen mode

Move forward once your nodes show Ready.

Installing Kyverno

Kyverno operates as a set of controllers inside the cluster. If it's not already deployed, install it with Helm.

Check whether it exists:

kubectl get pods -n kyverno
Enter fullscreen mode Exit fullscreen mode

If nothing shows up, add the repo:

helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
Enter fullscreen mode Exit fullscreen mode

Create the namespace if needed:

kubectl create namespace kyverno
Enter fullscreen mode Exit fullscreen mode

Install Kyverno:

helm install kyverno kyverno/kyverno -n kyverno
Enter fullscreen mode Exit fullscreen mode

Verify the controllers are running:

kubectl get pods -n kyverno
Enter fullscreen mode Exit fullscreen mode

Once everything is Running, Kyverno begins intercepting admission requests.

Building the Global Registry Restriction Policy

Create a ClusterPolicy that validates image sources at admission.

File: restrict-registries.yaml

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-image-registries
spec:
  validationFailureAction: enforce
  background: true
  rules:
    - name: validate-registries
      match:
        resources:
          kinds:
            - Pod
      validate:
        message: "Only registry.company.io or harbor.internal.local are allowed."
        pattern:
          spec:
            containers:
              - image: "registry.company.io/* | harbor.internal.local/*"

Enter fullscreen mode Exit fullscreen mode

Apply the policy:

kubectl apply -f restrict-registries.yaml
Enter fullscreen mode Exit fullscreen mode

Check registration:

kubectl get clusterpolicy
Enter fullscreen mode Exit fullscreen mode

You should see restrict-image-registries in the list.

Testing the Policy

You need to validate both failure and success paths.

Test 1: Disallowed registry

kubectl run testbad --image=nginx
Enter fullscreen mode Exit fullscreen mode

This is denied because the image does not match the allowed patterns. The admission error confirms the rule is active.

Test 2: Allowed registry

kubectl run testgood --image=registry.company.io/app:v1
kubectl get pods
Enter fullscreen mode Exit fullscreen mode

Admission succeeds. The Pod may still fail to pull if the registry does not exist, but that is unrelated to the policy.

This proves the global restriction works.

Helpful Verification Commands

kubectl get clusterpolicy
kubectl describe clusterpolicy restrict-image-registries
kubectl get pods
kubectl describe pod testbad
kubectl describe pod testgood
Enter fullscreen mode Exit fullscreen mode

Optional Cleanup

kubectl delete pod testbad testgood --ignore-not-found
Enter fullscreen mode Exit fullscreen mode

Extending the Model for Multi-Environment Clusters

Most real clusters run several environments under the same control plane. A common setup is separate dev and prod namespaces. Each environment may rely on different registries, so policy enforcement must reflect that.

The global policy stays in place. Now add namespace-specific rules.

Creating Environment Namespaces

kubectl create namespace dev
kubectl create namespace prod
kubectl get namespaces
Enter fullscreen mode Exit fullscreen mode

Applying Namespace-Based Registry Policies

Create a second ClusterPolicy:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: registry-per-namespace
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: dev-registry-only
      match:
        resources:
          kinds:
            - Pod
          namespaces:
            - dev
      validate:
        message: "Dev namespace must use registry.dev.company.io."
        pattern:
          spec:
            containers:
              - image: "registry.dev.company.io/*"

    - name: prod-registry-only
      match:
        resources:
          kinds:
            - Pod
          namespaces:
            - prod
      validate:
        message: "Prod namespace must use registry.prod.company.io."
        pattern:
          spec:
            containers:
              - image: "registry.prod.company.io/*"

Enter fullscreen mode Exit fullscreen mode

Apply it:

kubectl apply -f registry-per-namespace.yaml
Enter fullscreen mode Exit fullscreen mode

Testing Namespace Restrictions

Dev namespace

kubectl run bad-dev --image=nginx -n dev
Enter fullscreen mode Exit fullscreen mode

This should be rejected.

kubectl run good-dev --image=registry.dev.company.io/app:1.0 -n dev
Enter fullscreen mode Exit fullscreen mode

This is accepted.

Prod namespace

kubectl run bad-prod --image=nginx -n prod
kubectl run good-prod --image=registry.prod.company.io/web:1.0 -n prod
Enter fullscreen mode Exit fullscreen mode

The nginx Pod fails. The registry-approved one passes admission.

How the Layered Approach Works

The cluster-wide policy limits image usage to internal registries only. The per-namespace policy adds a second filter:

dev uses only dev registry images

prod uses only prod registry images

Both policies apply together. A Pod must satisfy both to be admitted. This structure prevents accidental cross-environment deployments and tightens supply chain controls without complicating developer workflows.

Summary

You installed Kyverno, created a global restriction policy for image registries, and validated correct admission behavior. You then extended the scenario with environment-specific rules, ensuring dev and prod cannot cross-pull container images. This is a core security pattern for Kubernetes and aligns directly with CKS study requirements. Once registry control is established, you can layer in tag policies, digest enforcement, and signing rules.

Top comments (0)