Containers

Using Gatekeeper as a drop-in Pod Security Policy replacement in Amazon EKS

Jason Umiker

If you are managing a Kubernetes cluster or you are a security professional responsible for one then you likely have heard that you should be implementing Kubernetes Pod Security Policies (PSPs) on the cluster in order to improve your security posture. Using PSPs can help to block pods from being disruptive to their worker nodes or the wider cluster. However, PSPs are not the only way to achieve this. As a matter of fact, there is a newer and arguably better way: Gatekeeper, part of the Open Policy Agent (OPA) open source project in the Cloud Native Computing Foundation (CNCF).

What is a Pod Security Policy?

PSPs are a feature of Kubernetes that has been in beta since version 1.13. They allow you to restrict pods from specifying security sensitive properties in the context of the pod specification such as restricting capabilities or applying an SELinux context.

Let’s have a look at a concrete example from the Kubernetes docs on a ‘restricted’ policy, showing how to add many of the various restrictions possible within a single PSP:

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'runtime/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
  privileged: false
  # Required to prevent escalations to root.
  allowPrivilegeEscalation: false
  # This is redundant with non-root + disallow privilege escalation,
  # but we can provide it for defense in depth.
  requiredDropCapabilities:
    - ALL
  # Allow core volume types.
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    # Assume that persistentVolumes set up by the cluster admin are safe to use.
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    # Require the container to run without root privileges.
    rule: 'MustRunAsNonRoot'
  seLinux:
    # This policy assumes the nodes are using AppArmor rather than SELinux.
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  readOnlyRootFilesystem: false

What are challenges around PSPs?

While PSPs can help to strengthen runtime security in the context of Kubernetes, they are not without challenges. Let us now have a closer look at that.

  1. They’re a bit cumbersome: The way PSPs work is that the first policy in alphabetical order that you have access to via Kubernetes RBAC is the one that applies. Some people choose to force that ordering by pre-pending numbers – such as 1-fullyopen, 4-lessrestrictive, 9-mostrestrictive, similar to how you may treat firewall rules. Common requests such as “I want to apply this PSP to that namespace” can be surprisingly tricky to do.
  2. They may not make it to General Availability (GA) in their current form: They have been in beta in Kubernetes for some time and there is an ongoing discussion if they’ll ever come out of beta. And even if they do they might look different than they do today.
  3. They don’t cover all security concerns: There are things that are arguably important to security, such as ensuring that the images come from a trusted registry or that pods have resource limits set (to avoid noisy-neighbor availability issues), not enforceable via PSPs.
  4. They are not user extensible: Following on from the previous point, you can not extend PSPs to cover any missing areas for your use case by yourself in a straightforward manner. Getting even simple additions or changes made to them involves getting pull requests merged into upstream Kubernetes, which is a more significant undertaking than most end-users are up for.

So, where does this leave us?

Open Policy Agent (OPA) to the rescue!

I hinted at earlier to an alternative to using PSPs for this use-case: OPA. So, what is OPA and how can you use it to enforce PSP-equivalent runtime policies?

What is OPA?

OPA is a general purpose policy engine that allows us to define and enforce policies. It is focused just on doing this one thing and doing it well. OPA is a CNCF Incubating project and supports enforcing policies in a variety of areas, including microservices, Kubernetes, CI/CD pipeline, service mesh and API gateways. When it comes to using OPA in Kubernetes, though, you do it via Gatekeeper and we will now discuss why in the following.

What is Gatekeeper?

Gatekeeper is an OPA sub-project that provides first-class integration between OPA and Kubernetes. It includes a Kubernetes Admission Controller that will intercept and inspect any Kubernetes resource being added or updated on the cluster and ensure it meets the required policies defined in OPA’s rule language called Rego, providing you with a preventative control.

Gatekeeper was preceded by an earlier way to integrate OPA with Kubernetes called kube-mgmt. Gatekeeper is a more recent, Kubernetes-native initiative and has matured to the point where it is now the recommended way to integrate OPA with your Kubernetes clusters.

With Gatekeeper you can not only enforce the same things that you can with PSPs but you can also easily make custom policies on nearly any other parameter of nearly any spec allowed in to your cluster(s) as well.

It introduces two new Custom Resource Definitions (CRDs) to declaratively manage your policies and how they are enforced within the cluster by the associated admission controller:

  • A CRD for declaring the policies (aka “constraint templates”)
  • A CRD for declaring how and when to enforce those policies (aka “constraints”)

It also introduced new audit functionality to allow administrators to see what resources are currently violating any given policy for those things admitted before the policy was enforced.

How easy is this to do?

Installing Gatekeeper is as easy as running one command as a cluster administrator:

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml

Note that Gatekeeper makes policy templates for everything that you can do with PSPs today available in their library.

Applying those policy templates and then enforcing them for each policy is as simple as using the following two kubectl commands:

kubectl apply -f pod-security-policy/allow-privilege-escalation/template.yaml
kubectl apply -f pod-security-policy/allow-privilege-escalation/constraint.yaml

You can then verify that the policies are working by trying to deploy a pod that violates one of policies. Thankfully they also provide an example.yaml for each policy to use to test that it works. Here is the whole process of blocking privileged pods and testing it end-to-end in less than 30 seconds:

If you want to exclude a particular namespace, or inversely specify which namespaces it should apply to, this is configured using the match field of the constraint.

Exploring Gatekeeper beyond PSPs

Gatekeeper provides a demo with more examples beyond what you can do with PSPs including but not limited to:

  • Limiting what container registries that images can come from.
  • Enforcing an owner label exists so it is clear who to call or bill for the service.
  • Requiring that each pod has resource limits defined (if they are not set, they can consume the whole node).

Additionally, the raspbernetes/k8s-security-policies repository has examples of policies that can be used with Gatekeeper to enforce the CIS Benchmark for Kubernetes. These are not only starting points for your policy library themselves but also great and varied examples to use to learn how to create your own custom policies.

Conclusion

Gatekeeper can today already do everything that PSPs can do and more. Perhaps most importantly Gatekeeper allows you to define and iterate on your own corporate policies to suit your own needs. It also does not involve RBAC and avoids the first policy in alphabetical order that you have access to situation but instead is scoped to either the whole cluster, a list of namespaces to enforce against, or a list of namespaces to exclude.

Gatekeeper is really emerging as the standard way to have preventative controls of what is allowed within Kubernetes clusters including Amazon EKS. It is easy to get started, so give it a try today and share your experiences.

Marc Chéné

Jason Umiker

Jason is a Senior Specialist Solutions Architect for containers based in Sydney, Australia. He has many years experience at AWS and helping our customers navigate the wider container ecosystem.

Michael Hausenblas

Michael Hausenblas

Michael is an Open Source Product Developer Advocate in the AWS container service team. He covers observability, service meshes, container security, Kubernetes, and Arm-based systems. Before AWS, Michael worked at Red Hat, Mesosphere, MapR and as a PostDoc in applied research. Reach him on Twitter via @mhausenblas (DMs are open).