Learning Kubernetes - Episode 37 - Introduction and Explanation of Pod Security Context

Learning Kubernetes - Episode 37 - Introduction and Explanation of Pod Security Context

In this episode, we'll discuss Kubernetes Pod Security Context for controlling Pod and container security settings. We'll learn how to run Pods as non-root users, set capabilities, enforce read-only filesystems, and best practices for Pod security.

Arman Dwi Pangestu
Arman Dwi PangestuApril 12, 2026
0 views
7 min read

Introduction

Note

If you want to read the previous episode, you can click the Episode 36 thumbnail below

Episode 36Episode 36

In the previous episode, we explored NetworkPolicy, which controls network traffic between Pods and external networks. Now we'll dive into Pod Security Context, which controls security settings for Pods and containers.

Note: Here I'll be using a Kubernetes Cluster installed through K3s.

Pod Security Context defines privilege and access control settings for a Pod or container. It controls aspects like user ID, group ID, filesystem permissions, and Linux capabilities. By properly configuring security context, you can significantly reduce the attack surface of your applications.

Understanding Pod Security Context

Pod Security Context is a set of security-related settings that apply to Pods and containers. It operates at the Linux kernel level and controls how containers interact with the host system.

Why Pod Security Context Matters

1. Principle of Least Privilege

Run containers with minimal required permissions. Don't run as root unless absolutely necessary.

2. Prevent Privilege Escalation

Restrict containers from gaining additional privileges.

3. Enforce Read-Only Filesystems

Prevent containers from modifying the filesystem.

4. Control Linux Capabilities

Grant only necessary Linux capabilities instead of all root capabilities.

5. Compliance and Security Standards

Meet security compliance requirements like PCI-DSS, HIPAA, and SOC 2.

Pod-Level vs Container-Level Security Context

Security context can be applied at two levels:

Pod-Level - Applies to all containers in the Pod Container-Level - Applies to a specific container (overrides Pod-level settings)

KubernetesPod and Container Security Context
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      runAsUser: 2000
      allowPrivilegeEscalation: false

Running as Non-Root User

Pod-Level runAsUser

KubernetesPod-Level runAsUser
apiVersion: v1
kind: Pod
metadata:
  name: non-root-pod
spec:
  securityContext:
    runAsUser: 1000
  containers:
  - name: app
    image: myapp:latest

This runs all containers in the Pod as user ID 1000.

Container-Level runAsUser

KubernetesContainer-Level runAsUser
apiVersion: v1
kind: Pod
metadata:
  name: mixed-users
spec:
  containers:
  - name: app1
    image: app1:latest
    securityContext:
      runAsUser: 1000
  - name: app2
    image: app2:latest
    securityContext:
      runAsUser: 2000

Different containers run as different users.

runAsNonRoot

KubernetesEnforce Non-Root
apiVersion: v1
kind: Pod
metadata:
  name: enforce-non-root
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
  containers:
  - name: app
    image: myapp:latest

The runAsNonRoot: true setting prevents the container from running as root. If the image tries to run as root, the Pod will fail to start.

Group Settings

runAsGroup

KubernetesSet Primary Group
apiVersion: v1
kind: Pod
metadata:
  name: group-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
  containers:
  - name: app
    image: myapp:latest

This sets the primary group ID to 3000.

fsGroup

KubernetesSet Filesystem Group
apiVersion: v1
kind: Pod
metadata:
  name: fsgroup-demo
spec:
  securityContext:
    runAsUser: 1000
    fsGroup: 2000
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    emptyDir: {}

The fsGroup setting changes the group ownership of mounted volumes. All processes in the container are also part of this group.

supplementalGroups

KubernetesSupplemental Groups
apiVersion: v1
kind: Pod
metadata:
  name: supplemental-groups
spec:
  securityContext:
    runAsUser: 1000
    supplementalGroups: [4000, 5000]
  containers:
  - name: app
    image: myapp:latest

The container process is a member of groups 4000 and 5000 in addition to the primary group.

Filesystem Permissions

readOnlyRootFilesystem

KubernetesRead-Only Root Filesystem
apiVersion: v1
kind: Pod
metadata:
  name: readonly-fs
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: var-log
      mountPath: /var/log
  volumes:
  - name: tmp
    emptyDir: {}
  - name: var-log
    emptyDir: {}

This makes the root filesystem read-only. The container can only write to mounted volumes.

Linux Capabilities

Linux capabilities break down root privileges into smaller units. Instead of granting all root capabilities, you can grant only what's needed.

Drop Capabilities

KubernetesDrop Unnecessary Capabilities
apiVersion: v1
kind: Pod
metadata:
  name: drop-caps
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      capabilities:
        drop:
        - ALL

This drops all Linux capabilities. The container runs with minimal privileges.

Add Specific Capabilities

KubernetesAdd Specific Capabilities
apiVersion: v1
kind: Pod
metadata:
  name: add-caps
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE

This drops all capabilities but adds NET_BIND_SERVICE to allow binding to ports below 1024.

Common Capabilities

CapabilityPurpose
NET_BIND_SERVICEBind to ports < 1024
NET_RAWUse raw sockets
SYS_ADMINVarious admin operations
SYS_TIMESet system time
CHOWNChange file ownership
DAC_OVERRIDEBypass file permissions
SETUIDChange user ID
SETGIDChange group ID

Privilege Escalation

allowPrivilegeEscalation

KubernetesPrevent Privilege Escalation
apiVersion: v1
kind: Pod
metadata:
  name: no-priv-escalation
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false

This prevents the container from gaining additional privileges through setuid or setgid binaries.

privileged Mode

KubernetesPrivileged Container
apiVersion: v1
kind: Pod
metadata:
  name: privileged-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      privileged: true

Privileged containers have access to all host devices and capabilities. Use only when absolutely necessary.

SELinux Options

SELinux Context

KubernetesSet SELinux Context
apiVersion: v1
kind: Pod
metadata:
  name: selinux-demo
spec:
  securityContext:
    seLinuxOptions:
      level: "s0:c123,c456"
      type: "spc_t"
  containers:
  - name: app
    image: myapp:latest

This sets the SELinux context for the Pod.

Seccomp Profiles

Seccomp (Secure Computing) restricts which system calls a container can make.

RuntimeDefault Seccomp

KubernetesUse Default Seccomp Profile
apiVersion: v1
kind: Pod
metadata:
  name: seccomp-default
spec:
  securityContext:
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:latest

This uses the container runtime's default seccomp profile.

Custom Seccomp Profile

KubernetesCustom Seccomp Profile
apiVersion: v1
kind: Pod
metadata:
  name: seccomp-custom
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: my-profile.json
  containers:
  - name: app
    image: myapp:latest

This uses a custom seccomp profile stored on the node.

Practical Examples

Secure Web Application

KubernetesSecure Web Application
apiVersion: v1
kind: Pod
metadata:
  name: secure-web
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: web
    image: nginx:latest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: cache
      mountPath: /var/cache/nginx
    - name: run
      mountPath: /var/run
  volumes:
  - name: tmp
    emptyDir: {}
  - name: cache
    emptyDir: {}
  - name: run
    emptyDir: {}

Database Container

KubernetesSecure Database Container
apiVersion: v1
kind: Pod
metadata:
  name: secure-db
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 999
    runAsGroup: 999
    fsGroup: 999
  containers:
  - name: postgres
    image: postgres:15
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL
    volumeMounts:
    - name: data
      mountPath: /var/lib/postgresql/data
  volumes:
  - name: data
    emptyDir: {}

Minimal Privilege Container

KubernetesMinimal Privilege Container
apiVersion: v1
kind: Pod
metadata:
  name: minimal-privilege
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}

Security Context in Deployments

KubernetesDeployment with Security Context
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 2000
      containers:
      - name: app
        image: myapp:latest
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
        volumeMounts:
        - name: tmp
          mountPath: /tmp
      volumes:
      - name: tmp
        emptyDir: {}

Common Mistakes and Pitfalls

Mistake 1: Running as Root

Problem: Container has unnecessary privileges.

KubernetesMistake: Running as Root
# DON'T DO THIS - Runs as root
apiVersion: v1
kind: Pod
metadata:
  name: root-pod
spec:
  containers:
  - name: app
    image: myapp:latest

Solution: Always specify runAsUser:

KubernetesCorrect: Non-Root User
apiVersion: v1
kind: Pod
metadata:
  name: non-root-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
  containers:
  - name: app
    image: myapp:latest

Mistake 2: Allowing Privilege Escalation

Problem: Container can gain additional privileges.

KubernetesMistake: Allow Privilege Escalation
# DON'T DO THIS - Allows privilege escalation
apiVersion: v1
kind: Pod
metadata:
  name: escalation-pod
spec:
  containers:
  - name: app
    image: myapp:latest

Solution: Disable privilege escalation:

KubernetesCorrect: Prevent Privilege Escalation
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false

Mistake 3: Writable Root Filesystem

Problem: Container can modify the filesystem.

KubernetesMistake: Writable Root Filesystem
# DON'T DO THIS - Root filesystem is writable
apiVersion: v1
kind: Pod
metadata:
  name: writable-pod
spec:
  containers:
  - name: app
    image: myapp:latest

Solution: Make root filesystem read-only:

KubernetesCorrect: Read-Only Root Filesystem
apiVersion: v1
kind: Pod
metadata:
  name: readonly-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}

Mistake 4: Granting All Capabilities

Problem: Container has unnecessary capabilities.

KubernetesMistake: All Capabilities
# DON'T DO THIS - Container has all capabilities
apiVersion: v1
kind: Pod
metadata:
  name: all-caps-pod
spec:
  containers:
  - name: app
    image: myapp:latest

Solution: Drop all capabilities and add only what's needed:

KubernetesCorrect: Minimal Capabilities
apiVersion: v1
kind: Pod
metadata:
  name: minimal-caps-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE

Mistake 5: Using Privileged Containers

Problem: Container has full host access.

KubernetesMistake: Privileged Container
# DON'T DO THIS - Privileged container
apiVersion: v1
kind: Pod
metadata:
  name: privileged-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      privileged: true

Solution: Use specific capabilities instead:

KubernetesCorrect: Specific Capabilities
apiVersion: v1
kind: Pod
metadata:
  name: specific-caps-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      capabilities:
        drop:
        - ALL
        add:
        - NET_ADMIN

Best Practices

1. Always Run as Non-Root

KubernetesNon-Root Best Practice
securityContext:
  runAsNonRoot: true
  runAsUser: 1000

2. Drop All Capabilities

KubernetesDrop All Capabilities
securityContext:
  capabilities:
    drop:
    - ALL

3. Make Root Filesystem Read-Only

KubernetesRead-Only Filesystem
securityContext:
  readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
  mountPath: /tmp

4. Prevent Privilege Escalation

KubernetesPrevent Escalation
securityContext:
  allowPrivilegeEscalation: false

5. Use Security Context at Pod Level

Apply security context at the Pod level so all containers inherit it:

KubernetesPod-Level Security Context
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
  containers:
  - name: app
    image: myapp:latest

6. Combine with Pod Security Policies

Use Pod Security Policies (or Pod Security Standards in newer versions) to enforce security context requirements cluster-wide.

7. Test Security Context

Verify that your security context settings work correctly:

KubernetesTest Security Context
# Check running user
kubectl exec -it pod-name -- id
 
# Check filesystem permissions
kubectl exec -it pod-name -- ls -la /
 
# Check capabilities
kubectl exec -it pod-name -- grep Cap /proc/self/status

Checking Security Context

Describe Pod

Kubernetesbash
kubectl describe pod pod-name
# Look for "Security Context" section

Get Pod YAML

Kubernetesbash
kubectl get pod pod-name -o yaml
# Look for securityContext field

Check Running User

Kubernetesbash
kubectl exec -it pod-name -- id
# Output: uid=1000(user) gid=3000(group) groups=2000(fsgroup)

Check Capabilities

Kubernetesbash
kubectl exec -it pod-name -- grep Cap /proc/self/status
# Shows current capabilities

Security Context Limitations

1. Container Runtime Dependent

Some security context settings depend on the container runtime (Docker, containerd, etc.).

2. Host Kernel Limitations

Security context relies on Linux kernel features. Some settings may not work on all systems.

3. No Application-Level Security

Security context doesn't protect against application-level vulnerabilities.

4. Requires Proper Image Setup

The container image must be designed to run as non-root.

Conclusion

In episode 37, we've explored Pod Security Context in Kubernetes in depth. We've learned how to control security settings for Pods and containers, run as non-root users, manage capabilities, and enforce read-only filesystems.

Key takeaways:

  • Pod Security Context controls security settings for Pods and containers
  • runAsUser - Run container as specific user ID
  • runAsNonRoot - Enforce non-root execution
  • runAsGroup - Set primary group ID
  • fsGroup - Set filesystem group for volumes
  • readOnlyRootFilesystem - Make root filesystem read-only
  • capabilities - Grant or drop Linux capabilities
  • allowPrivilegeEscalation - Prevent privilege escalation
  • privileged - Run in privileged mode (use sparingly)
  • seLinuxOptions - Set SELinux context
  • seccompProfile - Restrict system calls
  • Always run as non-root - Principle of least privilege
  • Drop all capabilities - Then add only what's needed
  • Make filesystem read-only - Prevent modifications
  • Test security settings - Verify they work correctly

By properly configuring Pod Security Context, you significantly reduce the attack surface of your applications and follow security best practices.

Note

If you want to continue to the next episode, you can click the Episode 38 thumbnail below

Episode 38Episode 38

Related Posts