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.
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.
Disables the containers to start in privileged mode, this increases the isolation of the running containers as well restrict many privileges on the host.
Disables the ability to start processes in the host namespace. Disabling this prevents the possibility of leaking information such as environment variables.
Disables the ability to communicate with other processes on the host
This defines the volumes the pods can use.
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
Requires the pod to be submitted with a non-zero (non-root) runAsUser or have a USER definition in the Dockerfile.
Enforces that no child process of a container can get more privileges than its parent.
This allows any seLinuxOptions to be specified.