What is Pod Security Policy?
The Pod Security Policy, sometimes called PSP in short, is a Kubernetes resource that allows the enforcement of policy rules during the creation phase of a Pod.
When a PodSecurityPolicy resource is created, it does nothing. In order to use it, the requesting user or target pod’s service account must be authorized to use the policy, by allowing the use verb on the policy.
Most Kubernetes pods are not created directly by users. Instead, they are typically created indirectly as part of a Deployment, ReplicaSet, or other templated controller via the controller manager. Granting the controller access to the policy would grant access for all pods created by that controller, so the preferred method for authorizing policies is to grant access to the pod’s service account
For example, if one wishes to make sure that Pod with root privileges can only be created by the cluster administrator and not by any other user then the following operations will do the trick:
- Define a Pod Security Policy that deny the creation of Pod with root privileges
- Associate the Pod Security Policy with Role or a Cluster Role that is binded to non-admin users
- Define a Pod Security Policy that allows the creation of Pod with root privileges
- Associate the Pod Security Policy with Role or a Cluster Role that is binded to admin users
The below diagram illustrates that place of the Pod Security Policy within the different Kubernetes objects
The Pod Security Policy Object (PodSecurityPolicy) is bound either to a User (User) or to a Service Account (ServiceAccount)
The binding to the User or the Service Account is done Associating the Pod SecurityPolicy to a Role (Role) or the Cluster Role (ClusterRole) that is binded via the Role Binding (RoleBinding) or the Cluster Role Binding (ClusterRoleBinding) to the User or the Service Account.
See the Pod Security Policy documentation for the full details.
Activating the Pod Security Policy
The Pod Security Policy is part of Kubernetes admission control mechanism, so in order to have the Pod Security Policy take effect, the Kubernetes Admission Control needs to be activated.
When the User or the Service Account access the Kubernetes API for creating a Pod, the Admission Control is invoked and checks if the Pod attributes conform with the rules defined in the Pod Security Policy. In case there is a mismatch the API server will fail the operation.
Controlling Container Runtime
One way to control the Kubernetes worker node’s Container Runtime is to gain access to it’s API interface by connecting to a local file system unix socket, which can be done by mounting the socket to another container - In docker for example the default unix socket file is located in /var/run/docker.sock
Docker CLI client uses this socket to execute docker commands by default. You can override these settings as well.
There may be different valid applications that require access to the container runtime . For example logging agents such as fluentd, worker node monitoring agents such as Datadog, or security agents.. An uncommon usage can be the creation of new containers on the worker node directly through the container runtime and not through Kubernetes APIs
This increases attack surface so one should avoid mounting the container runtime unix-socket based API endpoint inside a Pod in order not to compromise the host that is running docker daemon, since Docker by default launches all containers as root.
Example: Protecting Docker Container Runtime API
In the following example we will see how can the Pod Security Policy be used to protect against mounting docker.sock into a container. We will first define a namespace, psp-example, where our example will run, a service account malicious-user, that will represent the user that is trying to create a Pod that can access the docker.sock
kubectl create namespace psp-example
kubectl create serviceaccount -n psp-example malicious-user
Step 1: Define the Pod Security Policy
First let's define the Pod Security Policy that will limit the allowed volumes:
privileged: false # Don't allow privileged pods!
# The rest fills in some required fields.
# This specifies a whitelist of host paths that are allowed to be used by hostPath volumes. An empty list means there is no restriction on host paths used.
- pathPrefix: "/tmp"
Note the use of the allowedHostPaths to limit the allows files on the host.
Next we will create the Policy:
kubectl create -f example_psp.yaml
Step 2: Create a Role and Attach the Pod Security Policy
Now we will create a Role and attach the Pod Security Policy to the Role:
# "namespace" omitted since ClusterRoles are not namespaced
- apiGroups: [""]
verbs: ["get", "list", "watch", "create"]
Next we will create the Role:
kubectl create -f example_role.yaml
Step 3: Bind the Role to the User
Now we will bind the role to a user, a service account in our case:
- kind: ServiceAccount
Next we will run the binding:
kubectl create -f example_role_binding.yaml
Running the Example
Lets define a pod:
- name: docker-socket
- name: pause
- mountPath: /my-docker
Now we enable the Admission control to enable Pod Security Policy. For example the gcloud command for my-cluster would be:
gcloud beta container clusters update my-cluster --enable-pod-security-policy --zone us-east1-d
Next we will run the Pod creation:
kubectl create -f attaching_pod.yaml --as=system:serviceaccount:psp-example:malicious-user -n psp-example'
We will get an error message indicating that the pod creation violates the Pod Security Policy:
Error from server (Forbidden): error when creating "attacking_pod.yaml": pods "attacking-pod" is forbidden: unable to validate against any pod security policy: [spec.volumes: Invalid value: "hostPath": hostPath volumes are not allowed to be used spec.containers.securityContext.allowPrivilegeEscalation: Invalid value: true: Allowing privilege escalation for containers is not allowed]