Understanding SecurityContext vs PodSecurityPolicy and Implementing PSP in Kubernetes
This article explains the difference between SecurityContext and PodSecurityPolicy, demonstrates how to create service accounts, define a non‑privileged PSP, enable it in a cluster, bind it with RBAC, and apply the policy to Pods, Deployments and system components in Kubernetes.
Many people confuse the SecurityContext field inside a Pod spec with the separate resource type PodSecurityPolicy (PSP); the former is a declaration of security settings for the Pod itself, while the latter is an independent resource that enforces security constraints on Pods.
PSP usage is tightly coupled with RBAC: different operators need isolated accounts with separate authorizations, and each namespace’s ServiceAccounts must be managed through role bindings that reference the appropriate PSP.
First, create a ServiceAccount named common in the default namespace to simulate a user who can create Pods:
$ kubectl create sa common
serviceaccount/common created
$ kubectl create rolebinding common --clusterrole=edit --serviceaccount=default:common
rolebinding.rbac.authorization.k8s.io/common created
$ alias kube-common='kubectl --as=system:serviceaccount:default:common'Next, define a PSP that disallows privileged Pods and save it as psp.noprivileged.yaml :
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: noprivileged
spec:
privileged: false
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'Submit the PSP to the cluster and then create two Pods – one without a privileged flag and one with it:
apiVersion: v1
kind: Pod
metadata:
name: noprivileged
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
---
apiVersion: v1
kind: Pod
metadata:
name: privileged
spec:
containers:
- name: pause
image: k8s.gcr.io/pause
securityContext:
privileged: trueUsing the common user, both Pods are created because the PSP has not yet been enabled in the cluster:
$ kube-common apply -f pod.yaml && kube-common delete -f pod.yaml
pod/noprivileged created
pod/privileged created
pod "noprivileged" deleted
pod "privileged" deletedEnable PSP in the cluster (example for GKE):
$ gcloud beta container clusters update gcp-k8s --enable-pod-security-policy --zone=asia-east1-a
Updating gcp-vlab-k8s...done.After enabling PSP, attempts to create either Pod are rejected:
$ kube-common apply -f pod.yaml && kube-common delete -f pod.yaml
Error from server (Forbidden): pods "noprivileged" is forbidden: unable to validate against any pod security policy: []
Error from server (Forbidden): pods "privileged" is forbidden: unable to validate against any pod security policy: []Cluster administrators can still create privileged Pods directly:
$ kubectl apply -f pod.yaml && kubectl delete -f pod.yaml
pod/noprivileged created
pod/privileged created
pod "noprivileged" deleted
pod "privileged" deletedGrant the common ServiceAccount permission to use the non‑privileged PSP:
$ kubectl create role psp:noprivileged \
--verb=use \
--resource=podsecuritypolicy \
--resource-name=noprivileged
role.rbac.authorization.k8s.io/psp:noprivileged created
$ kubectl create rolebinding common:psp:noprivileged \
--role=psp:noprivileged \
--serviceaccount=default:common
rolebinding.rbac.authorization.k8s.io/common:psp:noprivileged createdNow the common user can create the non‑privileged Pod but the privileged one is blocked, matching expectations:
$ kube-common apply -f pod.yaml ; kube-common delete -f pod.yaml
pod/noprivileged created
Error from server (Forbidden): pods "privileged" is forbidden: ... Privileged containers are not allowed
pod "noprivileged" deleted
Error from server (NotFound): pods "privileged" not foundWhen creating a Deployment, the replica set controller uses the default ServiceAccount, which lacks the PSP binding, so Pods are rejected. Adding serviceAccount: common to the Deployment spec allows the non‑privileged Pods to start:
kind: Deployment
metadata:
name: noprivileged
spec:
replicas: 1
template:
serviceAccount: common
metadata:
labels:
app: pause
version: v1
spec:
containers:
- name: sleep
image: k8s.gcr.io/pauseSystem Pods such as kube-proxy are not affected because GKE pre‑creates role bindings that associate those ServiceAccounts with a privileged PSP ( gce:podsecuritypolicy:privileged ), allowing them to continue running.
The PSP framework can control many aspects, including privileged containers, host namespaces (HostPID, HostNetwork), volume types and filesystem permissions, user/group IDs, escalation allowances, capabilities, sysctls, and SELinux/AppArmor profiles.
The kubectl advise-psp plugin can automatically generate a PSP based on the security settings of a running Pod.
Reference: Kubernetes Pod Security Policy documentation
Cloud Native Technology Community
The Cloud Native Technology Community, part of the CNBPA Cloud Native Technology Practice Alliance, focuses on evangelizing cutting‑edge cloud‑native technologies and practical implementations. It shares in‑depth content, case studies, and event/meetup information on containers, Kubernetes, DevOps, Service Mesh, and other cloud‑native tech, along with updates from the CNBPA alliance.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.