Kubernetes Pod Security Policies

What is a Pod Security Policy?

A Pod Security Policy controls the security-related aspects of a pod specification. These policies help you secure and regulate how pods can interact with other resources in your cluster. By implementing Pod Security Policies you can enforce a security baseline for your Kubernetes architecture. Some examples you can enforce are: preventing people from running privileged containers, not allowing host networking and ports, disallowing host path volumes, not allowing containers to run as root, and much more. Once enabled, Pod Security Policies will do this in an automatic and transparent manner. Every pod specification is checked if it matches these requirements. If these requirements are not met, the pod will be prevented from getting started or scheduled.

By default, Kubernetes allows creating privileged pod containers that are able to compromise the security of your application or other applications running on the same cluster. In order to overcome this lack of security you can enable Pod Security Policies by using the Kubernetes API object PodSecurityPolicy. If you haven’t any policies configured yet, you’ll need to put some Pod Security Policies in place to provide the baseline security for your cluster. The default configuration is often not restrictive enough, especially for multi tenant clusters.

The PodSecurityPolicy Object

Below is an example of a Pod Security Policy:

---
apiVersion: networking.k8s.io/v1
kind: PodSecurityPolicy
metadata:
  name: restricted-psp
spec:
  privileged: false 

In Kubernetes it is possible to run “privileged mode” containers inside a pod. As a result, the process inside the container has unrestricted access to the resources on the host system. While there are some use-cases where this can be useful, in general it's a bad practice and elevates the security risk. If you have containers that need to run in privileged mode, you can still schedule them, but with a different role, specific for this container.

Role Binding

Upon creation of the PodSecurityPolicy policy object it doesn’t do anything. You need to authorize the requesting user or service account to use this policy. This is done by using the “use” verb on the policy. These rules are put in a ClusterRole, as shown below.

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: restricted-psp-role
rules:
- apiGroups:
  - policy
  resources:
  - podsecuritypolicies
  resourceNames:
  - restricted-psp
  verbs:
  - use

After the creation of this Cluster Role we still need to bind it to a User or a Service Account using a Cluster Role Binding. In the example below we bind the newly created “restricted-psp-role” to the group system:serviceaccounts, which authorizes all service accounts to use this restricted-psp-role.

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: restricted-psp-role-binding
roleRef:
  kind: ClusterRole
  name: restricted-psp-role
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group 
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

After applying the above cluster role binding, the policy will be in effect for the whole cluster. If you rather want to do it on a namespace level, you can use RoleBinding instead. It’s best practice to have a more restrictive Pod Security Policy at the cluster level, and less restrictive if needed for specific service accounts. This way you are sure this policy is enforced cluster-wide, and exceptions can be put in place.

Real-world policy example

Since the above example was deliberately kept small for educational purposes, we will now provide a real-life example on how to drastically improve your Kuberentes security baseline.

---
apiVersion: networking.k8s.io/v1
kind: PodSecurityPolicy
metadata:
  name: restricted-psp
spec:
  privileged: false
  hostPID: false
  hostIPC: false
  hostNetwork: false
  volumes: 
    - 'configMap' 
    - 'downwardAPI' 
    - 'emptyDir'
    - 'persistentVolumeClaim'
    - 'secret'
    - 'projected'
  readOnlyRootFilesystem: true
  runAsUser: 
    rule: 'MustRunAsNonRoot'
  allowPrivilegeEscalation: false
  seLinux: 
    rule: 'RunAsAny'

Kubernetes Pod Security Policies Security This is the 3rd part of IN4IT’s “Kubernetes Security blog series”. In this blog we are going to cover what Pod Security Policies are. How can they help you secure your Kubernetes cluster and how to implement them.

What is a Pod Security Policy? A Pod Security Policy controls the security-related aspects of a pod specification. These policies help you secure and regulate how pods can interact with other resources in your cluster. By implementing Pod Security Policies you can enforce a security baseline for your Kubernetes architecture. Some examples you can enforce are: preventing people from running privileged containers, not allowing host networking and ports, disallowing host path volumes, not allowing containers to run as root, and much more. Once enabled, Pod Security Policies will do this in an automatic and transparent manner. Every pod specification is checked if it matches these requirements. If these requirements are not met, the pod will be prevented from getting started or scheduled.

By default, Kubernetes allows creating privileged pod containers that are able to compromise the security of your application or other applications running on the same cluster. In order to overcome this lack of security you can enable Pod Security Policies by using the Kubernetes API object PodSecurityPolicy. If you haven’t any policies configured yet, you’ll need to put some Pod Security Policies in place to provide the baseline security for your cluster. The default configuration is often not restrictive enough, especially for multi tenant clusters.

The PodSecurityPolicy Object Below is an example of a Pod Security Policy:

---
apiVersion: networking.k8s.io/v1
kind: PodSecurityPolicy
metadata:
  name: restricted-psp
spec:
  privileged: false 

In Kubernetes it is possible to run “privileged mode” containers inside a pod. As a result, the process inside the container has unrestricted access to the resources on the host system. While there are some use-cases where this can be useful, in general it’s a bad practice and elevates the security risk. If you have containers that need to run in privileged mode, you can still schedule them, but with a different role, specific for this container.

Role Binding Upon creation of the PodSecurityPolicy policy object it doesn’t do anything. You need to authorize the requesting user or service account to use this policy. This is done by using the “use” verb on the policy. These rules are put in a ClusterRole, as shown below.

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: restricted-psp-role
rules:
- apiGroups:
  - policy
  resources:
  - podsecuritypolicies
  resourceNames:
  - restricted-psp
  verbs:
  - use

After the creation of this Cluster Role we still need to bind it to a User or a Service Account using a Cluster Role Binding. In the example below we bind the newly created “restricted-psp-role” to the group system:serviceaccounts, which authorizes all service accounts to use this restricted-psp-role.

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: restricted-psp-role-binding
roleRef:
  kind: ClusterRole
  name: restricted-psp-role
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group 
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

After applying the above cluster role binding, the policy will be in effect for the whole cluster. If you rather want to do it on a namespace level, you can use RoleBinding instead. It’s best practice to have a more restrictive Pod Security Policy at the cluster level, and less restrictive if needed for specific service accounts. This way you are sure this policy is enforced cluster-wide, and exceptions can be put in place.

Real-world policy example Since the above example was deliberately kept small for educational purposes, we will now provide a real-life example on how to drastically improve your Kuberentes security baseline.

---
apiVersion: networking.k8s.io/v1
kind: PodSecurityPolicy
metadata:
  name: restricted-psp
spec:
  privileged: false
  hostPID: false
  hostIPC: false
  hostNetwork: false
  volumes: 
    - 'configMap' 
    - 'downwardAPI' 
    - 'emptyDir'
    - 'persistentVolumeClaim'
    - 'secret'
    - 'projected'
  readOnlyRootFilesystem: true
  runAsUser: 
    rule: 'MustRunAsNonRoot'
  allowPrivilegeEscalation: false
  seLinux: 
    rule: 'RunAsAny'

Lets go over this policy line by line to see what it means and how it can help you with securing your cluster.

privileged: false Disables the containers to start in privileged mode, this increases the isolation of the running containers as well restrict many privileges on the host.

hostPID: false Disables the ability to start processes in the host namespace. Disabling this prevents the possibility of leaking information such as environment variables.

hostIPC: false Disables the ability to communicate with other processes on the host

volumes: – ‘configMap’ – ‘downwardAPI’ – ’emptyDir’ – ‘persistentVolumeClaim’ – ‘secret’ – ‘projected’

This defines the volumes the pods can use.

readOnlyRootFilesystem: true Enforces immutable services as it prevents the application from writing to disk. This way malicious alteration of the data in the container is not possible. If the application needs to write temporary data you can still use the emptyDir type Memory

runAsUser: rule: ‘MustRunAsNonRoot’

Requires the pod to be submitted with a non-zero (non-root) runAsUser or have a USER definition in the Dockerfile.

allowPrivilegeEscalation: false Enforces that no child process of a container can get more privileges than its parent.

seLinux: rule: ‘RunAsAny’

This allows any seLinuxOptions to be specified.

Want to learn more on how we can help your organization? Let’s talk!

Edward Viaene
Published on Dec 9, 2020