- Daily Devops Tips
- Posts
- Secure Kubernetes Pods With SecurityContext
Secure Kubernetes Pods With SecurityContext
👋 Hi! I’m Bibin Wilson. In each edition, I share practical tips, guides, and the latest trends in DevOps and MLOps to make your day-to-day DevOps tasks more efficient. If someone forwarded this email to you, you can subscribe here to never miss out!
In this edition, we will look at
why non-root pods
Pod level and container level SecurityContext
How Kubernetes treats container images with and without non-root users.
According to the State of Kubernetes Security, 42% of respondents list security as a top concern in container and Kubernetes strategies, highlighting issues like incidents, vulnerabilities, and misconfigurations during the application lifecycle.
In the previous edition, we looked at building containers as non-root users. This is the first step in securing containers. You can also enforce non-root user-based image builds using tools like Trivy integrated into the image build CI pipeline.
Ultimately, these containers end up being deployed in the Kubernetes cluster.
In this edition, we will explore how to run Kubernetes pods as non-root users and how Kubernetes handles the non-root user configuration in the container image.
Why non-root pods?
Let’s first understand why we should use only non-root pods.
First, to minimize security risks. Running containers as non-root users reduces the potential impact of an exploit, as the container processes won't have root-level access to the host system.
Also, it aligns with the principle of least privilege, ensuring that containers operate with only the minimum permissions required to perform their tasks.
Next, compliance. Many organizations and industries have policies or compliance requirements (e.g., PCI DSS) that mandate running containers as non-root users to meet security and regulatory standards.
Default UID Assigned To Pods
By default, Kubernetes assigns UID 0 (root) to containers in a Pod unless you explicitly configure a different user.
Here is an example that shows the user ID of a container image in the pod without any security configuration at the image or pod level.
$ kubectl exec -it flask-app -- sh
# whoami
root
Good To Know: Kubernetes supports cluster-wide configurations to enforce policies that prevent Pods from running as root. This can be achieved through PodSecurity Standards (PSS) or Pod Security Admission (PSA)
SecurityContext in Kubernetes
To run pods as non-root users, first, you need to understand the SecurityContext parameter.
The SecurityContext supports the runAsUser
and runAsGroup
parameters to specify the user and group the container should use.
However, if you don’t want to specify a specific user but still want to enforce the use of a non-root user, you can use the runAsNonRoot: true
parameter.
For example,

Pod and Container Level SecurityContext
You can define the securityContext at both the Pod and Container levels.
Pod-level: This security context applies to all the containers in the pod. It acts as a default for all containers in the Pod.
For example,

Container-level : This security context applies to individual containers. It provides flexibility for individual containers that may have different requirements. It overrides the pod-level settings for that specific container.

Container Image With Non-root User
Now, let's say you created a container image with a non-root user UID 1001.
What happens if you don't specify the securityContext in the pod?
The container will run as the user assigned during the image creation. For example, if the image was created to run as the user appuser
, then the container inside the pod will run as appuser
.
To test this, I deployed a non-root container image devopscube/flask-app-non-root:1.0
, built to run as appuser
, without any security context.
The output confirms that the container is running as appuser
.
$ kubectl exec -it flask-app-non-root -- sh
$ whoami
appuser
So the general recommendation is:
If the image is properly configured with a non-root user → Don't specify
runAsUser
.If you need to enforce a specific UID regardless of the image, due to compliance reasons → Use
runAsNonRoot
orrunAsUser
, depending on the use case.
Note:If you have Pod Admission enabled in a namespace to allow only non-root pods, you need to specify runAsNonRoot: true
. The container will automatically use the user ID configured in the container image.
Container Image With root User
Now, let's say you use a container image that is built to run as root (the default behavior in many images).
This usually happens when the USER
instruction in the Dockerfile is not properly set, or the image defaults to root.
In this case, if you enable runAsNonRoot: true
in the security context, the pod will throw an error.
The error occurs because Kubernetes is configured to enforce the runAsNonRoot
security policy, but the container is running as root, even though no non-root user is defined in the image.
You can mitigate this by adding runAsUser
with a specific user ID.
Now, what if the application inside the image needs root privileges? Let's take an example using the following manifest with the standard nginx image:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1001
containers:
- name: app-container
image: nginx
imagePullPolicy: Always
ports:
- containerPort: 5000
If you deploy this manifest, it will go into a CrashLoopBackOff
state.
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx 0/1 CrashLoopBackOff 4 (10s ago) 47s
This is because the default nginx image is designed to run as the root user initially to bind to privileged ports like 80 (ports below 1024 require root privileges). Once it starts, the nginx process may drop privileges to a lower-privileged user, but it still needs to start as root.
So, if you want to run an nginx image as a non-root user, you need to create a customized image for nginx to work with a non-root user. You can take a look at the Nginx Unprivileged Image to learn more.
Bottom Line: The non-root pod will run without errors only if the process inside the container does not require root privileges.
If it tries to modify system-level configurations or files owned by root, or if it tries to bind to privileged ports (e.g., ports <1024), it will throw errors.
Wrapping Up
SecurityContext is a wide topic with many functionalities.
For example:
capabilities: With this feature, you can add or drop Linux capabilities. Dropping unnecessary capabilities minimizes the container's attack surface.
seccompProfile: This controls which system calls the container can use. A restricted profile blocks dangerous or unnecessary system calls.
AppArmor: It is a Linux kernel security module that provides Mandatory Access Control (MAC) security. It can be incorporated in to pods.
allowPrivilegeEscalation: This option determines if a process can gain more privileges than its parent process.
readOnlyRootFilesystem: Setting this option to
true
ensures that the container's root filesystem is read-only, reducing the risk of attackers modifying system files.
Info: These security features are specified in Kubernetes at the Pod level through the Pod's security context, but they ultimately get translated into container runtime configurations.
The container runtime (like containerd or CRI-O) is responsible for actually implementing and enforcing these security measures.
Covering all the concepts with practical examples in a single edition is beyond the scope, so I will continue exploring SecurityContext-related options with real-world use cases and practical examples in future editions.
Over to you!
Share your feedback in the comments to help me create content that meets your needs.
Reply