Guest post originally published on the Magalix blog by Mohamed Ahmed

In this article, we’re going to demonstrate how you can enforce the most fine-grained security policies using OPA. Notice that this article is part of a series in which we’re building upon the knowledge that you gained in the introducing OPA as a code & integrating OPA with Kubernetes. If you haven’t already done so, please go through the published articles in this series.

You may be already familiar with Pod Security Policy where you can apply very specific security controls over pods. For example, using Linux kernel capabilities, using host namespace, network, port, or filesystem, in addition to many others. Using OPA you can also impose similar controls on pods and in this lab, we’ll create an OPA policy that disallows creating privileged containers in pods. A privileged container has a higher access level to the host than non-privileged containers.

Enforce Pod Security Policies In Kubernetes Using OPA

Why Use OPA And Not The Native Pod Security Policy?

There’s nothing wrong with using Pod Security Policy to enforce our security policy. However, PSP – by definition – can only be applied to pods. They cannot handle other Kubernetes resources like Ingresses, Deployments, Services, etc. The power of OPA comes from its ability to be applied to any Kubernetes resource. OPA is deployed to Kubernetes as an admission controller, which intercepts the API calls sent to the API server and validates, and/or mutates them. Accordingly, you can have one unified OPA policy that applies to different components of the system, and not just pods. For example, a policy that forces users to use the company’s domain in their ingresses and also ensures that they only pull images from the company’s image repository. Please note that we’re using OPA that’s deployed using kube-mgmt and not the OPA Gatekeeper.

The Policy Code In Rego

In this article, we assume that you’re already familiar with OPA and the Rego language. We also assume that you have a running Kubernetes cluster that has OPA and the kube-mgmt container deployed. Please refer to our previous article for installation instructions. Our no-priv-pod.rego file should look as follows:

package kubernetes.admission
deny[msg] {
  c := input_containers[_]
  c.securityContext.privileged
  msg := sprintf("Privileged container is not allowed: %v, securityContext: %v", [c.name, c.securityContext])
}
input_containers[c] {
  c := input.request.object.spec.containers[_]
}
input_containers[c] {
  c := input.request.object.spec.initContainers[_]
}

Let’s briefly go through this file:

Deploying The Policy

OPA expects to find its policies in ConfigMaps in the opa namespace. To apply our code to a ConfigMap, we run the following command:

kubectl create configmap no-priv-pods --from-file=no-priv-pod.rego

The kube-mgmt sidecar container is continuously watching the API server for ConfigMaps in the opa namespace so that you can deploy policies by just creating the ConfigMap.

Exercising The Policy

Let’s ensure that our policy is working by attempting to deploy a privileged container:

kubectl -n default apply -f - <<EOT
apiVersion: v1
kind: Pod
metadata:
 name: nginx-privileged
 labels:
  app: nginx-privileged
spec:
 containers:
 - name: nginx
  image: nginx
  securityContext:
   privileged: true #false
EOT
Error from server (Privileged container is not allowed: nginx, securityContext: {"privileged": true}): error when creating "STDIN": admission webhook "validating-webhook.openpolicyagent.org" denied the request: Privileged container is not allowed: nginx, securityContext: {"privileged": true}

Notice that we intentionally deployed the pod to the default namespace because our admission webhook will ignore any resources created in the opa namespace or the kube-system.

TL;DR