{{announcement.body}}
{{announcement.title}}

Kubernetes Pod Security Policies with Open Policy Agent (OPA)

DZone 's Guide to

Kubernetes Pod Security Policies with Open Policy Agent (OPA)

Read this blog post to understand how Open Policy Agent (OPA) can be used to implement Kubernetes Pod Security Policies (PSP).

· Security Zone ·
Free Resource

Kubernetes is the most popular container orchestration platform in today's cloud-native ecosystem. Consequently, Kubernetes is also an area of increased interest and attention.

In this blog post, first I will discuss the Pod Security Policy admission controller. Then we will see how Open Policy Agent can implement Pod Security Policies. In fact, during Kubernetes SIG Auth at Kubecon + CloudNaticeCon North America 2019, Open Policy Agent/Gatekeeper was touched upon as a potential alternative to Pod Security Policy.

Firstly, a brief look at containers, security, and admission controllers.

Overview of containers and security

Containers are lightweight, portable, and easy to manage. There are no separate physical/virtual machines for containers running on the same host. In other words, containers share the resources, hardware, and the OS kernel of the host where they run. Therefore, it becomes very important that operators have appropriate security around what processes can run inside containers, what privileges these processes have, whether a container will allow escalation of privileges, what images are used, and so on.

A Pod is the basic execution unit of a Kubernetes application — the smallest and simplest unit in the Kubernetes object model that you create or deploy. It is a group of one or more containers with shared storage/network, and a specification for how to run the containers. Therefore, when enforcing security policies on containers, we inspect and apply security policies on Pod specifications. So, how are these policies enforced? Using Admission Controllers.

What are Admission Controllers?

Admission Controllers are part of the kube-apiserver. They intercept requests to the Kubernetes API server before configuration is stored in cluster settings (etcd). An admission controller can be validating (one which validates the incoming request) or mutating (one which modifies the incoming request) or both. Refer Kubernetes Documentation for a quick glance at various admission controllers.

Open Policy Agent As an Admission Controller

Open Policy Agent (OPA) is an open-source, general-purpose policy engine that makes it possible to write policy as code. OPA provides a high-level declarative language, Rego, to enable policy as code. Using OPA, we can enforce policies across microservices, CI/CD pipelines, API gateways and so on. One of the most important use-case for OPA is Kubernetes policy enforcement as an Admission Controller.

OPA as an admission controller allows you to enforce policies such as non-root user, requiring specific labels for resources, ensuring all pods specify resource requests and limits and so on. Basically, OPA allows you to write any custom policy as code using Rego language.

The policies are written in Rego and loaded into OPA running as an admission controller on your Kubernetes cluster. OPA will evaluate any resource create/update/delete request to Kubernetes API server against the Rego policies. If the request satisfies all the policies, the request is allowed. But even if a single policy fails, the request is denied.

Admission control flow

Read more about OPA, Rego and use as admission controller in OPA documentation.

Now, let's go into details of Pod Security Policies.

Pod Security Policy

Pod Security Policy (PSP) is a cluster-level resource that is implemented as an Admission Controller. PSP allows users to translate the security requirements into specific policies governing pod specs. At first, when a PodSecurityPolicy resource is created, it does nothing. And in order to use it, the requesting user or target pod's service account must be authorized to use the policy by allowing the "use" verb. You can refer to Enabling Pod Security Policies on Kubernetes documentation.

Note that the PSP admission controller acts as both validating and mutating the admission controller. For some of the parameters, the PSP admission controller uses default values to mutate the incoming request. Further, the order is always to first mutate and then validate.

What Parameters Can We Control Using PSP?

The table below gives a brief summary of various parameters and fields used in PSP. A detailed explanation is available in Kubernetes documentation here.

Field Kubernetes API Reference

(Kind – Version – Group )

Control Aspect
privileged SecurityContext v1 core Running containers in privileged mode
hostPID, hostIPC PodSpec v1 core Usage of host namespaces
hostNetwork, hostPorts PodSpec v1 core Usage of host networking and ports
volumes PodSpec v1 core Usage of volume types
FSGroup PodSecurityContext v1 core Allocating an FSGroup that owns the pod’s volumes
hostPath Volume v1 core Usage of the host filesystem
readOnlyRootFilesystem SecurityContext v1 core Requiring the use of a read only root file system
flexVolume / driver FlexVolumeSource v1 core White list of FlexVolume drivers
runAsUser SecurityContext v1 core

PodSecurityContext v1 core

The user IDs of the container
runAsGroup SecurityContext v1 core

PodSecurityContext v1 core

The group IDs of the container
runAsNonRoot SecurityContext v1 core Usage of root user for container
allowPrivilegeEscalation SecurityContext v1 core Restricting escalation to root privileges
capabilities SecurityContext v1 core Using Linux capabilities
seLinuxOptions SecurityContext v1 core

PodSecurityContext v1 core

The SELinux context of the container
procMount SecurityContext v1 core The Allowed Proc Mount types for the container
AppArmor annotations The AppArmor profile used by containers
Seccomp annotations The Seccomp profile used by containers
sysctl PodSecurityContext v1 core The sysctl profile used by containers

So, Can We Implement PSP With OPA?

I mentioned earlier that Rego language allows us to write any custom policy as code. That means, we can write the Pod Security Policies described above using Rego and enforce with OPA as an admission controller.

Let's take a quick look at a Rego policy for implementing a "privileged" pod security policy. You can try out this policy on Rego playground .

Java
 




x
12


 
1
package kubernetes.admission
2
 
           
3
deny[message] {
4
    #applies for Pod resources
5
    input.request.kind.kind == "Pod" 
6
    #loops through all containers in the request
7
    container := input.request.object.spec.containers[_]
8
    #for each container, check privileged field
9
    container.securityContext.privileged
10
    #if all above statements are true, return message
11
    message := sprintf("Container %v runs in privileged mode.", [container.name])
12
}


So, what does this policy do? It will return a message if any container in the input request is running as privileged container.

PSP in action

Let's see this policy in action with a basic minikube based tutorial. First, setup OPA as admission controller by following the tutorial from OPA documentation here. This tutorial loads an ingress validation policy. Instead of that, we will load the privileged policy shown above.

Once OPA is setup as admission controller on minikube, create a file privileged.rego using the policy above. Then, create the policy as a configmap in "opa" namespace.

Shell
 




xxxxxxxxxx
1


 
1
kubectl create configmap privileged-policy --from-file=privileged.rego -n opa


Wait for the policy to be loaded to OPA. You can check that the policy is loaded when an annotation openpolicyagent.org/policy-status: '{"status":"ok"}' appears on the configmap.

Now, let's create a deployment with privileged container using the following manifest :

YAML
 




xxxxxxxxxx
1
13


 
1
apiVersion: v1
2
kind: Pod
3
metadata:
4
  name: nginx
5
  namespace: default
6
  labels:
7
    app: nginx
8
spec:
9
  containers:
10
  - name: nginx
11
    image: nginx:latest
12
    securityContext:
13
      privileged: true


When you try to create this pod, you will notice that the pod is denied by Open Policy Agent.

Plain Text
 




xxxxxxxxxx
1


 
1
Error from server (Container nginx runs in privileged mode.): error when creating "privileged-deploy.yaml": admission webhook "validating-webhook.openpolicyagent.org" denied the request: Container nginx runs in privileged mode.


Likewise, we can write policies for other Pod Security Policy parameters and enforce using OPA.

While in this tutorial, we loaded the policy using configmap for simplicity, thats not the best strategy for production deployments. For production deployments, you can configure OPA to download the policy bundles from an external bundle server periodically. All your policies can be maintained in this bundle server. And OPA will keep itself up-to-date by periodically downloading the policies. Refer Bundle API for more details.

In short, using OPA, we can enforce the Pod Security Policies. And not only that, we can enforce any other custom security / standards based policies using same setup.

What Are the Key Benefits of Using OPA for PSP?

Some key benefits that we get from this approach are:

  • Centralized management of all your policies (PSP and other custom policies) in one admission controller instead of managing those disparately.
  • Shift-Left - Enforce the same policies also in the CI/CD pipeline thus implementing Policy-as-code throughout the stack.
  • Ability to maintain OPA policies in a source control repository like Git. OPA provides HTTP APIs to dynamically manage the policies loaded.
  • Stream the policy decisions to an external logging/monitoring tool of your choice.
  • Customize the denial message as per your setup/implementation.

In addition, we can deploy OPA as a mutating admission controller. That way, you can also implement the mutating behavior of PSP Admission Controller.

If this use case of OPA interests you, it may be worth checking out Styra Declarative Authorization Service (DAS) for Kubernetes. Styra DAS makes it easy to enforce and continually monitor security, compliance and operational policies.

Styra DAS acts as a single control plane over all your clusters. Moreover, using a pre-built policy library for OPA, you can get started within minutes. Also, it provides a rich policy editor for writing custom policies, impact analysis by replaying decisions against changes to the policies, decision logging, and enterprise readiness with SSO, access control.

Recently, Styra has also added the Pod Security Policy pack to the pre-built policy library. So, this takes away all the additional efforts to write the PSP policies yourself. And, you benefit from the continuous enhancements to the policies as per industry standards.

Conclusion

We can implement Pod Security Policies with OPA effectively. Moreover, this allows us to model security policies as code. And, all this in a single OPA admission controller.

Lastly, I hope you liked this blog post. If you have any feedback or queries, please comment here or reach out to me on / @Gaurav on OPA slack / @GGCTwts on Twitter.

This article is originally published on InfraCloud blog

References:

  1. https://kubernetes.io/docs/concepts/policy/pod-security-policy/
  2. https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/
  3. OPA Documentation - https://www.openpolicyagent.org/docs/latest/
  4. Styra WhitePaper - K8sSecurityAdmissionControl.pdf on https://www.styra.com/
  5. How guardrails secure and accelerate K8s deployments - https://blog.styra.com/blog/how-guardrails-can-both-secure-and-accelerate-kubernetes-deployments
  6. KubeCon + CloudNativeCon North America 2019 - SIG Auth Update and Deep Dive - https://www.youtube.com/watch?v=SFtHRmPuhEw&list=PLj6h78yzYM2NDs-iu8WU5fMxINxHXlien&index=129&t=0s
Topics:
admission controller, cloud, cloud native, container, kubernetes, open policy agent, psp, security

Published at DZone with permission of Gaurav Chaware . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}