How to Migrate from Kubernetes Pod Security Policies (PSPs) to Kyverno

How to Migrate from Kubernetes Pod Security Policies (PSPs) to Kyverno

Since its genesis, Kubernetes has been the go-to container orchestration solution for enterprises in need of scalable containerized applications implemented on microservices architecture. It essentially deals with the Pod as the most basic unit, which may hold one or more containers within them. Since any application deployed within Kubernetes is executed through one or more Pods, it is of high importance for the user to ensure that they are secure from misconfigurations and security breaches. Thus, Pod security is not just a major concern, but a necessity for Kubernetes clusters, and even more so for business-critical applications.

To fulfill this need, Kubernetes introduced PodSecurityPolicy in its v1.3 release. However, PodSecurityPolicy has been officially deprecated by Kubernetes in the v1.21 release and has been entirely removed in the v1.25 release, which was a step taken due to some major issues encountered by users throughout the years of its use which could not be addressed without introducing breaking changes (more details could be found on an official post made by Kubernetes).

The Purpose of PodSecurityPolicy

PodSecurityPolicy (PSP) is an admission controller that is built within Kubernetes. It serves the purpose of controlling security-sensitive aspects of the Kubernetes Pod specification. For example, if your use case demands that the Pods must be restricted from accessing the host system’s resources, devices, and kernel capabilities, you would want to avoid running Pods in your cluster in a privileged mode. To accomplish this, the corresponding PSP definition would be as shown below:

Snippet 1: Sample PSP Definition to Restrict Privileged Pods


apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restrict-privileged-pods
spec:
  privileged: false
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'

But the removal of PSP in Kubernetes v1.25 release does not mean that it’s an end for Pod security at all. It will be replaced by a Pod Security Admission (PSA) controller, which utilizes the Kubernetes admission control webhooks. The PSA controller, however, comes with some serious drawbacks, such as the inability to mutate the incoming resource, no enforcement of Pod controllers, and unconfigurable messages, just to name a few. You can find a list of all the pros and cons of PSA and their detailed explanation in this session conducted by the Kyverno maintainers Shuting Zhao (Staff Engineer, Nirmata) and Chip Zoller (Technical Product Manager, Nirmata).

Kyverno for Kubernetes Native Policy Management

Created by Nirmata, later donated to the Cloud Native Computing Foundation (CNCF), and currently a CNCF Incubating project – Kyverno is a policy engine that is specifically designed for Kubernetes. While having significant advantages over the PSA controller, it utilizes the Kubernetes admission control webhooks to inspect the newly created or modified resources that attempt to make their way into the cluster. It does so by performing checks on the definitions of resources in question, based on the Kyverno policies that have been installed within the cluster. The policies themselves can be scoped to either the entire cluster using the ClusterPolicy custom resource or a particular namespace within the cluster using the Policy custom resource.

A sample Kyverno policy with a block-ephemeral-containers rule definition to strictly block the usage of ephemeralContainers within a Pod is shown below:

Snippet 2: Sample Kyverno Policy to Block Ephemeral Containers


apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: block-ephemeral-containers
spec:
  validationFailureAction: enforce
  rules:
  - name: block-ephemeral-containers
    match:
      any:
      - resources:
          kinds:
            - Pod
    validate:
      message: "Ephemeral containers are not permitted."
      pattern:
        spec:
          X(ephemeralContainers): "null"

However, resource validation is just one of the four capabilities of Kyverno. The other three include resource mutation (modifying the incoming resource), resource generation (generating or creating new resources on the basis of incoming resources), and supply chain security guarantees. But to stick to the scope of this article, only the validation of resources is explained throughout.

For steps on installing Kyverno, please visit the official Kyverno Installation documentation, and to explore all the available sample policies created by the Kyverno community (191 at the moment, and counting), please visit the official Kyverno Policies webpage.

Writing Kyverno Policies

One might get intimidated by the thought of learning something entirely new to accomplish a requirement. It also seems quite tedious for cluster administrators to learn an entirely new language or technology to deliver and maintain a new requirement or behavior in their cluster. And when it concerns the security of the cluster itself, it certainly seems inevitable.

However, that’s not the case for Kyverno, for it is designed in a way that allows one to write policies for Kubernetes in the Kubernetes native fashion itself, that is, through YAML definitions in the form of Kubernetes custom resources. In essence, it makes it extremely simple for someone who knows how to work with Kubernetes, but is new to Kyverno, to understand the expected behavior of a Kyverno policy.

You have already seen a sample Kyverno policy in Snippet 2. A basic explanation for the policy defined there is as follows:

  1. validationFailureAction: enforce indicates that any resource which violates this policy will be blocked. If validationFailureAction is set to audit instead of enforce, then the policy violation is logged within the Kyverno policy report (see the official Kyverno Reporting documentation to learn more about policy reports) but the resource is allowed to be created.
  2. The rules field stores a list of rules that you’d like to define within your policy. In this case, a validate rule is being used which is being matched against any resource with kind: Pod.
  3. The validate field indicates that this rule will trigger a resource validation in case the incoming resources have matching kinds according to the match field. The pattern field inside validate defines the pattern you want to look for when validating
  4. X(ephemeralContainers) tells Kyverno that the key specified within the parentheses must not exist within the resource definition. X() in this case is known as a Negation Anchor in Kyverno (see the official Kyverno Anchors documentation to learn more about Anchors in Kyverno).

In case you’re interested in writing your very first policy or fulfilling a use case through your own Kyverno policy, you can find a very detailed guide in the official Kyverno documentation for writing policies.

Kubernetes Pod Security with Kyverno Policies

The equivalent Kyverno ClusterPolicy for the pod security policy defined in Snippet 1 is shown below.

Snippet 3: Sample Kyverno Policy Definition to Restrict Privileged Pods

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-privileged-pods
spec:
  validationFailureAction: enforce
  rules:
    - name: restrict-privileged-pods
      match:
        any:
        - resources:
            kinds:
              - Pod
      validate:
        message: "Rejected by restrict-privileged-pods rule."
        pattern:
          spec:
            =(initContainers):
              - =(securityContext):
                  =(privileged): false
            =(ephemeralContainers):
              - =(securityContext):
                  =(privileged): false
            containers:
              - =(securityContext):
                  =(privileged): false

With the explanation provided in the Writing Kyverno Policies section for the policy defined in Snippet 2, it must be quite easy to understand Snippet 3. Here, instead of the Negation Anchor, you can observe the usage of Equality Anchor =(). The purpose of Equality Anchor is to validate the existence of the key provided within the parentheses. If the key exists, it proceeds with validating its value. If that key further has a child element that is enclosed within the Equality Anchor, then the child element is further evaluated as a validation pattern.

For example, a simple explanation for
=(securityContext):
=(privileged): false

would be:
“If the key is equal to securityContext, and if the child element of securityContext has a key equal to privileged, then the value for the privileged key should be equal to false

Conclusion

Migrating from Kubernetes pod security policies to Kyverno is just as simple as defining any other Kubernetes resource.

Kubernetes policy management with Kyverno can be further made exponentially simpler and more robust with the help of the Nirmata Policy Manager for Kubernetes. To ensure the governance and security of your Kubernetes cluster, get a free trial today with no credit card required at all!

To connect with the Kyverno community and to get your queries and issues resolved quickly, join Kyverno’s Slack community on the Kubernetes workspace. You can also learn more about Kyverno by visiting this Nirmata page.

Lastly, reach-out to Nirmata if you have any questions on the material covered in this post – thanks for reading.

Kubernetes Policy Comparison: Kyverno vs. OPA/Gatekeeper
Kubernetes Policy, Security and Governance with Nirmata at KubeCon North America 2022
No Comments

Post a Comment