Kubernetes SecurityContext Explained With Examples
β Bibin Wilson
This blog explores why non-root pods are essential and look deep into Kubernetes SecurityContext parameters that enable you to enforce non-root user configurations at the pod and container levels.
- why non-root pods
- Pod level and container level SecurityContext
- How Kubernetes treats container images with and without non-root users.
Here is the detailed guide about 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 blog, 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 correctly set or the image defaults to root.
In this case, if you enable runAsNonRoot: true
it 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.
Conclusion
SecurityContext is a broad topic that has 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 into 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.
If you have any doubts about this blog, drop it in the comments!