Understanding Pod Security Admission - PSA
Introduction
Running a container inside a pod without restrictions or isolation in a production environment can be dangerous. Fortunately, Kubernetes offers many ways to achieve isolation and governance within a cluster. There are more complex and flexible options (like OPA), and there are also simpler methods that I believe are good starting points, such as Pod Security Admission (PSA), which fits this description.
Pod Security Admission (PSA)
PSA is about standards, and these standards are defined by Pod Security Standards (PSS).
A PSS defines a set of policies ranging from highly restrictive to highly permissive.
The beauty of PSA is that it’s very easy to enable in a cluster. PSA has been enabled by default since Kubernetes version 1.23. To enable it, you simply add a label to a namespace, and all Pods in that namespace must follow the standards in order to execute.
The label is composed of three elements: a prefix, a mode, and a level. The prefix consistently employs the hard-coded value ‘pod-security.kubernetes.io’, followed by a slash. The mode specifies how a violation is handled. Lastly, the level establishes the extent of security standards to be followed.
An example of such a label might appear as follows:
1
2
3
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted
Dig a little deep, we have:
prefix (simple as that):
1
pod-security.kubernetes.io
Mode
Mode | Description |
---|---|
enforce | Policy violations will cause the pod to be rejected. |
audit | Policy violations will trigger the addition of an audit annotation to the event recorded in the audit log, but are otherwise allowed. |
warn | Policy violations will trigger a user-facing warning, but are otherwise allowed. |
Offical doc here
Level
Profile | Description |
---|---|
Privileged | Unrestricted policy, providing the widest possible level of permissions. This policy allows for known privilege escalations. |
Baseline | Minimally restrictive policy which prevents known privilege escalations. Allows the default (minimally specified) Pod configuration. |
Restricted | Heavily restricted policy, following current Pod hardening best practices. |
Priviled: Fully and wide and complety open Baseline: Policy made to make a psa adoption painless Restricted: High secure, less privilege approach
More detail in official documentation
Hands on
Let’s begin by creating a namespace as follows:
1
2
3
4
5
6
7
8
namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: psa
labels:
pod-security.kubernetes.io/enforce: restricted
1
2
3
4
5
6
7
8
9
10
11
12
➜ k8s kubectl apply -f namespace.yaml
namespace/lab-psa created
➜ k8s kubectl describe ns lab-psa
Name: lab-psa
Labels: kubernetes.io/metadata.name=lab-psa
pod-security.kubernetes.io/enforce=restricted
Annotations: <none>
Status: Active
No resource quota.
No LimitRange resource.
And a regular pod:
1
2
3
4
5
6
7
8
9
10
11
12
podv1.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: psa
spec:
containers:
- image: busybox:1.35.0
name: busybox
command: ["sh", "-c", "sleep 1h"]
1
2
➜ k8s kubectl apply -f podv1.yaml
Error from server (Forbidden): error when creating "podv1.yaml": pods "busybox" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "busybox" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "busybox" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "busybox" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "busybox" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
Totally busted!
Let’s fix it
A secure pod’s manifest:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
podv2.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: psa
spec:
containers:
- image: busybox:1.35.0
name: busybox
command: ["sh", "-c", "sleep 1h"]
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
runAsNonRoot: true
runAsUser: 2000
runAsGroup: 3000
seccompProfile:
type: RuntimeDefault
1
2
3
4
5
➜ k8s kubectl apply -f podv2.yaml
pod/busybox created
➜ k8s kubectl get pods -n lab-psa
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 0 8s
There is a healthy and active pod in the ‘lab-psa’ namespace, operating under the strict rules of a PSA.
Conclusion
PSA has been enabled by default since Kubernetes version 1.23. It’s really simple to activate and adopt, allowing for the implementation of governance mechanisms without complex configurations. It can either block or just log any violations.
Sadly, it’s not possible to write custom rules or change any aspect of the PSA implementation, such as messages and so on. However, it can still be a good starting point for implementing security mechanisms in a Kubernetes environment
Any sugests or doubt?
Feel free to reach out to me on social media: twitter ,linkedin and github.
You can also email me directly at rmnobarra@gmail.com.
Support
Did you really enjoy my content? Consider buying me a coffee through my Bitcoins wallets:
Bitcoin Wallet:
bc1quuv5hml9fjkf7azgwkt4xp867pzdwzyga33qmj
Lighting Address:
lnbc1pjue6mkpp5yj737e7fm6efhlj6sns42a875pmkencqmvdshf4ghlnntaet5llsdqqcqzzsxqrrsssp5d9hxl686w839qkwmkm0m30cf5gp4gnzxh68kss393xqzlsg0wr3q9qyyssqp3933zc3fg46nk3vafh63r3lqd0jn2p04w5xrz77h33rrr3xm7ckegg6s2ss64g4rf4jg87zdjzkl5tup7umqpghy2qnk65tqzsnplcpwv6z4c
Bye!