Use admission controllers to enforce security policies - Security Practices for Multi-Tenant SaaS Applications using Amazon EKS

Use admission controllers to enforce security policies

Admission controllers are a powerful feature in Kubernetes. These controllers intercept requests to create new objects or mutate existing objects in a cluster, and take one or more actions. Admission controllers can modify a request to conform to a designated policy (a “mutating webhook”), or they can reject a request altogether (a “validating webhook”).

AWS recommends that customers running multi-tenant clusters implement one or both of the following security policy enforcement mechanisms.

Pod Security Policies (PSPs)

Every EKS cluster comes with a built-in admission controller capable of enforcing Pod Security Policies (PSPs). These policies are ordinary Kubernetes objects that a cluster administrator can create. For details, see Pod Security Policies.

Here is an example of a policy that forbids running privileged Pods:

apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: DisallowPrivilegedPods spec: privileged: false # The rest fills in some required fields. seLinux: rule: RunAsAny supplementalGroups: rule: RunAsAny runAsUser: rule: RunAsAny fsGroup: rule: RunAsAny volumes: - '*'

A more complex policy can be found in Appendix: Strict pod security policy for an untrusted tenant. This policy does the following:

  • Disallows privileged pods

  • Disallows privilege escalation

  • Requires all capabilities be dropped

  • Forbids host volumes from being mounted

  • Forbids using host networking, Inter-Process Communication (IPC) with the host, and using host process IDs (PIDs)

  • Forbids running as root

  • Requires a default Seccomp profile

By default, EKS provides an unrestricted Pod Security Policy. AWS recommends removing the default cluster role binding of the eks.privileged policy to all authenticated users. You can do this by editing the eks:podsecuritypolicy:authenticated cluster role binding to remove the system:authenticated group from the subject list. If you have created an alternative administrator group for your cluster, you can replace the system:authenticated group with your administrator group instead:

apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: eks:podsecuritypolicy:authenticated annotations: kubernetes.io/description: 'Allow all authenticated users to create privileged pods.' labels: kubernetes.io/cluster-service: "true" eks.amazonaws.com/component: pod-security-policy roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: eks:podsecuritypolicy:privileged subjects: - kind: Group apiGroup: rbac.authorization.k8s.io # Replace this with your administrator group name name: system:authenticated
Warning

Be careful when making these or other changes to your cluster. They may prevent you from creating new pods until replacement policies, appropriate roles, and role bindings are created.

Open Policy Agent (OPA)

Open Policy Agent (OPA) is a powerful, open-source general-purpose policy agent. At its core, OPA evaluates configurations against a set of rules you define, using a domain-specific language called Rego. Although OPA is flexible enough to work with just about any kind of structured data, it is most frequently used to enforce policies inside Kubernetes clusters.

OPA is capable of providing much more extensive policy management than a Pod Security Policy. PSPs are limited to Pods, while OPA can manage nearly any kind of Kubernetes object. And while PSPs are only able to apply a limited set of policies to a pod, OPA can apply powerful validators such as pattern matchers to any field in an object. For example, with OPA, you can also require that all container images be pulled from a trusted image repository.

The following is an example of a Rego policy that prohibits the creation of privileged containers:

package kubernetes.admission deny[message] { # match only if a Pod is being created input.request.kind.kind == "Pod" # examine each container container := input.request.object.spec.containers[_] # match if privileged is set container.securityContext.privileged message := sprintf("Container %v runs in privileged mode.", [container.name]) }

OPA is rapidly evolving. Customers can choose from several different implementations to run in their EKS clusters. Kube-mgmt is the original implementation and is still widely used. Gatekeeper is the newest implementation and has a powerful template-based configuration model.