Achieving Policy as Code with Kyverno in Kubernetes

Kubernetes environments thrive on flexibility and scalability, but these benefits also present challenges in maintaining consistency in governance, security, and compliance. 

Manual policy enforcement can be error-prone and inefficient, so policies must be automated- and Policy as Code is an effective solution.

This approach embeds policies directly into your workflows- transforming policies into versioned, auditable, and executable code that keeps pace with your infrastructure.

Kyverno, a policy engine designed specifically for Kubernetes, simplifies managing policies as code in your cluster management. 

This blog explores how Kyverno enables effective policy management in Kubernetes through real-world examples. We will demonstrate its capabilities to automate governance tasks like enforcing naming conventions, validating resource labels, and more.

Why Use Kyverno for Policy as Code?

Kyverno is tailored for Kubernetes and offers several benefits:

  • Native integration with Kubernetes: Seamlessly integrates with Kubernetes, simplifying policy enforcement directly in the cluster.
  • Simple, YAML-based policy definitions: Policies are defined in YAML, which makes them easy for developers and Kubernetes administrators to write, read, and manage.
  • Seamless application of policies at admission or runtime: Policies are enforced during resource admission to ensure compliance before deploying resources.
  • Support for validation, mutation, and generation policies: Kyverno supports different types of policies that provide flexibility and control over resource management.

To learn more about Kyverno and how it works, refer to Kyverno's official documentation.

Prerequisites

To follow along with the examples in this blog, ensure you have the following:

  • Helm Installed: Install Helm to manage and deploy Kubernetes applications.
  • Kyverno Installed: Install Kyverno using the following command:
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install my-kyverno kyverno/kyverno --version 3.3.4
  • kubectl Access: Ensure you have kubectl installed and configured it to interact with your cluster.
  • Kyverno CLI: To test policies locally, download and install the Kyverno CLI.
  • Namespace Preparation: Create namespaces or use existing ones to test scoped policies.

Common Knowledge for Using Kyverno

1. Types of Kyverno Policies

Kyverno supports three primary policy types:

  • Validation Policies: Ensure that Kubernetes resources meet predefined conditions before they are created or updated.
  • Mutation Policies: Modify resources dynamically during their admission process.
  • Generation Policies: Automatically create additional resources to maintain consistency.

2. Policy Scoping

Policies can be defined at two levels:

  • Cluster Policy: Applicable cluster-wide, targeting resources across all namespaces.
  • Policy: Scoped to a specific namespace, targeting resources within that namespace only.

In addition to resource validation and policy enforcement, Kubernetes provides mechanisms like probes to ensure the health and resilience of your applications.

Learn more about the Role of Probes in Kubernetes.

3. Failure Action

Kyverno policies have two failure actions:

  • Audit: Logs the violation without rejecting the resource.
  • Enforce: Rejects the resource if it does not comply with the policy.

4. Policy Testing

Before applying policies to a live cluster, test them locally using the Kyverno CLI. This avoids potential disruptions caused by misconfigured policies.

kyverno test /path/with/manifests

5. Excluding Resources from Policies

Kyverno allows you to exclude specific resources from being evaluated by policies. This is particularly useful for excluding system-critical resources or third-party components. You can specify exclusions in the policy metadata under the match or exclude fields.

Example: Excluding a Namespace

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: exclude-dev-namespace
spec:
  rules:
    - name: exclude-dev
      match:
        any:
          - resources:
              kinds:
                - Deployment
      exclude:
        any:
          - resources:
              namespaces:
                - dev-namespace
      validate:
        failureAction: Audit
        message: "Resource is excluded from policy enforcement."
        pattern:
          metadata:
            labels:
              excluded: "true"

This policy excludes deployments in the dev-namespace from validation while still applying other cluster-wide rules.

Best Practices for Writing Kyverno Policies

  • Keep Policies Modular: Break policies into smaller, manageable chunks to avoid unintended impacts.
  • Use Descriptive Messages: Make violation messages clear and actionable.
  • Review and Update Regularly: Align policies with evolving security and compliance requirements.

Tip: After implementing and testing your Kyverno policies, tracking their impacts in your Kubernetes environment is essential. Get real-time insights by streamlining ELK Stack deployment on Kubernetes.

Implementation Examples

Use Case 1: Restricting Resource Creation Based on Image Repositories

This policy enforces the use of approved container image registries, preventing resources from being created if unapproved image repositories are used.

Step 1: Defining the Policy

Save the following policy to a file (e.g., restrict-image-registries.yaml):

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-image-registries
spec:
  rules:
  - name: validate-image-registry
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      failureAction: Enforce
      message: "Images must be pulled from the approved registries: 'docker.io' or 'ghcr.io'."
      pattern:
        spec:
          containers:
          - image: ("docker.io/* | "ghcr.io/*"")

Step 2: Apply the Policy

Run the following command to apply the policy to your cluster:

kubectl apply -f <yaml-file-name-for-policy>

After applying the policy, you should see confirmation of successful policy creation.

Policy Creation Confirmed
Policy Creation Confirmed

Step 3: Test the Policy with a Deployment

Create a deployment YAML file (e.g., approved-registry-deployment.yaml) and include an image from an approved registry (docker.io or ghcr.io).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: approved-registry-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: docker.io/nginx:latest
#        image: nginx:latest            # Uncomment this section to know more about kyverno error
        ports:
        - containerPort: 80

Apply the deployment using the following command:

kubectl apply -f <deployment-file-name>
Observing Policy Enforcement
  • Using an Approved Image: The deployment will succeed because docker.io/nginx:latest is from an approved registry.
  • Using an Unapproved Image: To test policy enforcement, uncomment the image: nginx:latest line in the YAML file, save it and reapply the deployment.
    The deployment will fail, and you will see an error message indicating that the image is not from an approved registry.
 Error when the image was not pulled from docker.io or ghcr.io  
 Error when the image was not pulled from docker.io or ghcr.io  

Flow of the Use Case: Restricting Container Image Registries

  1. Pod/Deployment Creation or Update: When a new Pod is created or updated in the cluster, the Kyverno policy triggers automatically.
  2. Image Validation: Kyverno checks the image registry specified in the containers section of the Pod specification against the defined pattern. It ensures that the image is pulled from an approved registry (either docker.io or ghcr.io).
  3. Policy Enforcement: If the image is not from the approved registries, the creation or update of the Pod is blocked, and an error message is returned.
  4. Successful Validation: If the image is from an approved registry, the policy validation passes, and the Pod is successfully created or updated in the cluster.
  5. Ongoing Enforcement: The Kyverno policy continues to enforce this validation for all future Pod creations or updates, ensuring that only images from the trusted registries are used throughout the cluster.

Use Case 2: Automating Resource Creation

Aside from enforcing policies, Kyverno can generate resources automatically, saving time and ensuring consistency across your Kubernetes clusters.

Step 1: Create the ClusterPolicy for NetworkPolicy Generation

The following ClusterPolicy ensures that a default NetworkPolicy is automatically generated whenever a new Namespace is created.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-network-policy
spec:
  rules:
  - name: generate-default-network-policy
    match:
      any:
      - resources:
          kinds:
          - Namespace
    generate:
      kind: NetworkPolicy
      apiVersion: networking.k8s.io/v1
      name: default-deny-all
      namespace: "{{request.object.metadata.name}}"
      synchronize: true
      data:
        spec:
          podSelector: {}
          policyTypes:
          - Ingress
          - Egress

Apply the policy using the following command:

kubectl apply -f <yaml-file-name-for-policy>
Kyverno ClusterPolicy for Automated NetworkPolicy Creation
Kyverno ClusterPolicy for Automated NetworkPolicy Creation

Flow of the Use Case:

  • Namespace Creation: When a new Namespace is created, the Kyverno policy triggers automatically.
  • NetworkPolicy Generation: Kyverno generates a default NetworkPolicy that denies all inbound (Ingress) and outbound (Egress) traffic for the newly created Namespace.
  • Automatic Enforcement: This policy automatically synchronizes with the Namespace, ensuring that all newly created namespaces have secure default network isolation.

Step 2: Example Namespace Creation

Create a new Namespace to trigger the Kyverno policy and validate that it generates the expected NetworkPolicy. Use the following YAML file:

apiVersion: v1
kind: Namespace
metadata:
  name: dev

Apply the namespace creation:

kubectl apply -f <namespace-file-name>

Step 3: Reviewing the Generated Resource

Once the Namespace is created, Kyverno will automatically generate the NetworkPolicy for that namespace. The generated resource will look like this:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: dev-namespace
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
Auto generation of network policy
Auto generation of network policy

Use Case 3: Conditional Logic with Dynamic Variables

Kyverno supports dynamic variables that allow policies to adapt based on contextual information such as user roles, namespaces, or service accounts.

Step 1: Creating a Role-Based Validation Policy

In this policy, we restrict Pod creation to users with the "admin" role. Save the following YAML as validate-admin-user.yaml:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: validate-admin-user
spec:
  rules:
    - name: restrict-non-admin
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Only admin users can create Pods."
        pattern:
          metadata:
            annotations:
              user-role: "admin"
        condition:
          key: "{{request.userInfo.username}}"
          operator: Equals
          value: "admin"

Step 2: Applying the Policy

To apply the policy, run the following command:

kubectl apply -f validate-admin-user.yaml
Validate User Policy Creation
Validate User Policy Creation

Step 3: Testing the Policy with a Deployment Example

Now, let’s test the policy by creating a Deployment that tries to create a Pod.

Sample Deployment YAML (Without admin Role)

Save the following Deployment YAML as infix-nginx-deployment.yaml. Initially, it does not include the user-role: "admin" annotation.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: infix-nginx
  namespace: default
  labels:
    app: nginx
    team: devops
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: infix-nginx
        image: docker.io/nginx:1.28
        ports:
        - containerPort: 80

 Command to apply the Deployment:

kubectl apply -f infix-nginx-deployment.yaml

You will get the error in the picture below:

Error raised while creating deployment using normal user
Error raised while creating deployment using normal user

Step 4: Add the "admin" Role Annotation for Successful Deployment

Modify the Deployment YAML by adding the user-role: "admin" annotation to bypass the policy enforcement error.

annotations:
   user-role: "admin"

The deployment is created successfully when we add this annotation to the container section.

Flow of the Use Case:

1. Pod/Deployment Creation or Update:

When a new Pod is created or updated in the cluster, the Kyverno policy validate-admin-user is automatically triggered.

2. Validation of User Role:

The Kyverno policy checks the annotations of the Pod's metadata to determine the user role. It ensures the user-role annotation is set to "admin" in the Pod's metadata.

3. Condition Check:

The policy further validates the username of the user creating the Pod ({{request.userInfo.username}}) to ensure it matches the value "admin". This double-check ensures that only users with the role "admin" and who are authenticated as "admin" can create Pods.

4. Policy Enforcement:

If the conditions are not met—either the user-role annotation is missing or not set to "admin", or the username does not match "admin"—the creation or update of the Pod is blocked. An error message is returned to the user:

"Only admin users can create Pods."

5. Successful Validation:

The policy validation passes if the annotation and username match the required values, and the Pod is created or updated successfully.

6. Ongoing Enforcement:

Kyverno will continue to enforce this policy for all future Pod creations or updates, ensuring that only users with the "admin" role can create Pods in the cluster.

Related Topic: Dynamic NFS Provisioning For Persistence Storage in Kubernetes

Conclusion

Kyverno empowers Kubernetes administrators to efficiently define, enforce, and automate policies using simple YAML configurations. Administrators can ensure smooth and effective policy implementation across their clusters by mastering Kyverno policy types, scoping, exclusions, and debugging.

Whether it's enforcing naming conventions, validating labels, restricting container image registries, or automating resource generation, Kyverno, a powerful policy as code, provides a robust approach to Kubernetes security and compliance.

If you're looking to strengthen your Kubernetes policy enforcement, our DevOps services can guide you through a seamless implementation process.

Read More:


Achieving Policy as Code with Kyverno in Kubernetes

Written By Mukesh Awasthi

Jan 27, 2025

An adept DevOps Engineer with expertise in AWS cloud infrastructure, Kubernetes orchestration, monitoring, and security. With a strong focus on automating workflows, he has successfully implemented scalable CI/CD pipelines and optimized cloud-native solutions. Mukesh is committed to maintaining a secure cloud environment while leveraging cutting-edge technologies to ensure system reliability and performance across cloud platforms.

Latest Blogs

ECS Service Connect: A Simple Guide to Connecting Microservices

Managing service-to-service communication between microservices can become complex as your AWS ECS a...


Sameep Sigdel

May 07, 2025

Recent Development In AWS: April 2025

AWS continues to roll out meaningful improvements, and the updates for April 2025 are no exception....


Chandra Rana

May 02, 2025

Outsourced IT Services for Small Business

With limited in-house resources and growing technology demands, small and medium-sized businesses (S...


Adex International

Apr 30, 2025