Comparing four popular Kubernetes policy engines

Intro

Kubernetes is a complex system that requires careful management and control to ensure security and compliance. As clusters grow and evolve, it becomes increasingly challenging to maintain consistency and enforce policies across all deployments. This is where Kubernetes policy engines come into play, providing a way to define, enforce, and audit policies across the entire cluster. PaC-k8s In this post, we’ll compare four popular Kubernetes policy engines: OPA Gatekeeper, Kyverno, Kubewarden, and jsPolicy. We’ll look at their features, strengths, and weaknesses, and provide examples of policies for enforcing runAsNonRoot for Pods.

Tools comparison

Technical aspects comparison table

Tool OPA Gatekeeper Kyverno Kubewarden jsPolicy
Engine Language GO GO GO GO
Admission Controller Yes Yes Yes Yes
Mutating Webhook Yes Yes Yes Yes
Validation Rule Language Rego YAML/JSON Any compiled to WebAssembly JavaScript/TypeScript
Support for Custom Resources Yes Yes Yes Yes
Extensibility Highly Extensible Highly Extensible Highly Extensible Limited Extensibility
Community Large and Active Growing Small and Active Small and Active
Integration Native Integration with Kustomize and Helm Native Integration with Kustomize and Helm Native Integration with Open Policy Agent Native Integration with Kubectl and Kubernetes API
Ease of Installation Moderate Easy Moderate Easy
Package Management Not Available Not Available Not Available npm

Example of policies
OPA Gatekeeper
OPA Gatekeeper is a powerful and flexible policy engine based on the Rego language. It uses Kubernetes Admission Control to enforce policies at the pre-admission stage, ensuring that any new objects created in the cluster meet the defined policy requirements. OPA Gatekeeper supports custom policies written in Rego, making it highly extensible and customizable.

However, OPA Gatekeeper has some limitations. Its customization options are somewhat limited, and it can be difficult to use for non-developers or those unfamiliar with the Rego language. Additionally, it can be challenging to debug policies due to the complex nature of the Rego language.

Here’s an example of an OPA Gatekeeper policy for enforcing runAsNonRoot for Pods:

package k8srequiredlabels

violation[{"msg": msg}] {
    pod := input.review.object
    pod.spec.securityContext.runAsNonRoot == false
    msg := "Pod must not run as root."
}

Kyverno
Kyverno is a Kubernetes policy engine that uses YAML/JSON-based policies to enforce policies on resources in the cluster. Kyverno supports both pre-admission and post-admission enforcement, making it a flexible and powerful tool for managing policies. It also provides support for custom resources, allowing users to define their policies on custom resources.

Kyverno’s main strength lies in its simplicity and ease of use. It has an intuitive policy language, making it accessible to non-developers, and provides a rich set of validation rules and transformations. However, Kyverno’s customizability is limited, and its performance can be impacted when handling large volumes of resources.

Here’s an example of a Kyverno policy for enforcing runAsNonRoot for Pods:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-non-root-pods
spec:
  validationFailureAction: audit
  background: false
  rules:
  - name: runasnonroot
    match:
      resources:
        kinds:
        - Pod
    validate:
      message: "Pod must not run as root"
      pattern:
        spec:
          securityContext:
            runAsNonRoot: true

Kubewarden
Kubewarden is a policy engine that uses WebAssembly modules to enforce policies on Kubernetes resources. It supports pre-admission and post-admission enforcement, making it a flexible and powerful tool for managing policies. Kubewarden’s use of WebAssembly allows it to support policies written in any language that can be compiled to WebAssembly.

Kubewarden’s main strength is its flexibility and extensibility. It provides a rich set of APIs for building and deploying custom policies, making it highly customizable. However, its performance can be impacted when handling large volumes of resources, and its use of WebAssembly can be challenging for non-developers or those unfamiliar with the technology. Here’s an example of a Kubewarden policy for enforcing runAsNonRoot for Pods using Rust:

use kubewarden_policy_sdk::admission::AdmissionRequest;
use kubewarden_policy_sdk::policy::Policy;
use kubewarden_policy_sdk::request::ValidationResponse;

fn validate(request: &AdmissionRequest) -> ValidationResponse {
    let pod = &request.object;
    match pod.get("spec") {
        Some(spec) => match spec.get("securityContext") {
            Some(security_context) => match security_context.get("runAsNonRoot") {
                Some(run_as_non_root) => {
                    if let Some(false) = run_as_non_root.as_bool() {
                        return ValidationResponse::invalid("Pod must not run as root");
                    }
                }
                _ => {}
            },
            _ => {}
        },
        _ => {}
    }
    ValidationResponse::valid()
}

kubewarden::policy! {
    "name": "run-as-non-root",
    "resource": "pods",
    "description": "Enforce runAsNonRoot for Pods",
    "validate": validate
}

jsPolicy
jsPolicy is a policy engine that uses JavaScript to enforce policies on Kubernetes resources. It supports pre-admission and post-admission enforcement, making it a flexible and powerful tool for managing policies. jsPolicy’s use of JavaScript allows it to support policies written in any language that can be compiled to JavaScript.

jsPolicy’s main strength is its simplicity and ease of use. It has an intuitive policy language, making it accessible to non-developers, and provides a rich set of validation rules and transformations. However, its customizability is limited, and its performance can be impacted when handling large volumes of resources.

Here’s an example of a jsPolicy policy for enforcing runAsNonRoot for Pods:

function mutate(request) {
  const pod = request.object;
  if (pod.spec.securityContext && pod.spec.securityContext.runAsNonRoot === false) {
    throw new Error("Pod must not run as root");
  }
}

module.exports = {
  mutate
};

The final part:

Here’s an example pod manifest that violates the policies we’ve defined, and one that satisfies the policies:

  • Pod manifest violating the policies
apiVersion: v1
kind: Pod
metadata:
  name: root-pod
spec:
  containers:
  - name: nginx
    image: nginx
    securityContext:
      runAsNonRoot: false

In this manifest, the runAsNonRoot field in the pod’s securityContext is set to false, which violates the policy that requires the field to be set to true.

  • Pod manifest satisfying the policies
apiVersion: v1
kind: Pod
metadata:
  name: non-root-pod
spec:
  containers:
  - name: nginx
    image: nginx
    securityContext:
      runAsNonRoot: true

In this manifest, the runAsNonRoot field in the pod’s securityContext is set to true, which satisfies the policy that requires the field to be set to true.

In conclusion, Kubernetes policy engines are essential for ensuring security and compliance in Kubernetes clusters. Each of the policy engines we’ve covered has its strengths and weaknesses, and the choice of which one to use ultimately depends on your specific needs and preferences. We hope this post has helped you understand the differences between these popular policy engines and provided useful examples of how to enforce policies using each one.


References: