Understanding Kubernetes Pod Security: runAsNonRoot and runAsUser
Table of Contents
Security is a prime concern when deploying applications in a Kubernetes cluster. One of the security aspects in Kubernetes is controlling who can run what and as whom within a Pod. Kubernetes provides two important fields in the Security Context to achieve this: runAsNonRoot and runAsUser. While they might seem similar at first glance, they serve different purposes. This blog post aims to demystify these settings and help you make the right choice for your applications.
nginx images
I have to wonder what percentage of containers are just nginx instances that are there to test something out. Nginx is an easy image to deploy because you can just do:
kubectl run nginx --image=nginx
And you have a running nginx instance.
However, that default nginx image will run as root (if your cluster allows that).
$ k exec -it nginx -- cat /proc/1/status | grep "Name\|Uid"
Name: nginx
Uid: 0 0 0 0
There is an nginx unprivileged image that will run as a non-root user; it runs as user 101. I would definitely recommend using this image if you are just testing something out. It’s a few more letters to type, but it’s worth it.
kubectl run nginx-unprivileged --image=nginxinc/nginx-unprivileged
Inspecting the images…
$ docker image inspect nginx | jq '.[0].Config.User'
""
$ docker image inspect nginxinc/nginx-unprivileged | jq '.[0].Config.User'
"101"
Here’s the nginx user in the unprivileged image:
$ k exec -it runasnonroot-and-runasuser -- grep nginx /etc/passwd
nginx:x:101:101:nginx user:/nonexistent:/bin/false
Differences Between runAsNonRoot and runAsUser
runAsNonRoot | runAsUser | |
---|---|---|
Purpose | Ensure container doesn’t run as root | Specify the exact UID for container |
Settings | true/false | Numeric UID |
Flexibility | Less flexible | More flexible |
Specificity | General: just not root | Very specific: exact UID |
Root Allowed | No | Yes, if specified |
runAsNonRoot
The runAsNonRoot field specifies that the container must not run as the root user. Setting this to true enforces that the container should be executed as a non-root user. If the container image specifies a user as root or numerically as 0, the container won’t start. It’s a way to ensure that your application doesn’t unintentionally run with more permissions than it needs.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: non-root-pod
spec:
securityContext:
runAsNonRoot: true
containers:
- name: my-container
image: nginxinc/nginx-unprivileged
EOF
Check the id of the user running the container:
$ kubectl exec -it non-root-pod -- id
uid=101(nginx) gid=101(nginx) groups=101(nginx)
But if we run the plain nginx image, it will fail:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: non-root-pod-plain
spec:
securityContext:
runAsNonRoot: true
containers:
- name: my-container
image: nginx
EOF
Note that the container is not running:
$ k get pods
NAME READY STATUS RESTARTS AGE
non-root-pod 1/1 Running 0 112s
non-root-pod-plain 0/1 CreateContainerConfigError 0 5s
And the reason is:
$ k describe pod non-root-pod-plain | grep Error
Reason: CreateContainerConfigError
Warning Failed 1s (x5 over 42s) kubelet Error: container has runAsNonRoot and image will run as root (pod: "non-root-pod-plain_runasnonroot(c5764bbb-c1cf-47b1-9606-3a3a49ebf666)", container: my-container)
runAsUser
On the other hand, runAsUser specifies which UID (User ID) the container process should run as. Unlike runAsNonRoot, this allows you to specify the exact UID of the user, including root if you explicitly set it to 0.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: specific-user-pod-nginx-priviliged
spec:
securityContext:
runAsUser: 1001
containers:
- name: my-container
image: nginxinc/nginx-unprivileged
EOF
Check the id of the user running the container:
$ kubectl exec -it specific-user-pod-nginx-priviliged -- id
uid=1001 gid=0(root) groups=0(root)
But if we run the plain nginx image, it will fail:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: specific-user-pod-nginx-plain
spec:
securityContext:
runAsUser: 1001
containers:
- name: my-container
image: nginx
EOF
Note that the container is not running:
$ k get pod specific-user-pod-nginx-plain
NAME READY STATUS RESTARTS AGE
specific-user-pod-nginx-plain 0/1 CrashLoopBackOff 1 (10s ago) 16s
Both runAsNonRoot and runAsUser
You can also use both runAsNonRoot and runAsUser together. In this case, runAsUser specifies the UID to use, and runAsNonRoot ensures that UID is not root.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: runasnonroot-and-runasuser
spec:
securityContext:
runAsUser: 1001
runAsNonRoot: true
containers:
- name: my-container
image: nginxinc/nginx-unprivileged
EOF
Check the id of the user running the container:
$ k exec -it runasnonroot-and-runasuser -- id
uid=1001 gid=0(root) groups=0(root)
$ k get pods runasnonroot-and-runasuser
NAME READY STATUS RESTARTS AGE
runasnonroot-and-runasuser 1/1 Running 0 22s
And nginx is indeed running as user 1001:
$ k exec -it runasnonroot-and-runasuser -- cat /proc/1/status | grep "Name\|Uid"
Name: nginx
Uid: 1001 1001 1001 1001
NOTE: PID 1 is the first process that runs in any operating system or containerized environment. When a container starts, it launches a single process with a PID (Process ID) of 1 within the isolated namespace of that container.
nginx plain as non-root with runAsUser
Will it blend?
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: nginx-plain-runasnonroot-and-runasuser
spec:
securityContext:
runAsUser: 1001
runAsNonRoot: true
containers:
- name: my-container
image: nginx
EOF
No.
$ k get pod nginx-plain-runasnonroot-and-runasuser
NAME READY STATUS RESTARTS AGE
nginx-plain-runasnonroot-and-runasuser 0/1 CrashLoopBackOff 1 (7s ago) 12s
Use Cases
-
runAsNonRoot: Use this setting when you want a general assurance that none of the containers in the Pod are running as root, without caring which user they run as.
-
runAsUser: Use this when you need more control over the exact user that runs the container process, such as for compliance with internal security policies that require specific UIDs for different types of applications. As well, some images require a specific UID to run properly.
Conclusion
While runAsNonRoot and runAsUser both provide ways to control the user running a container, they serve different needs. runAsNonRoot is a more generalized setting to prevent root access, while runAsUser gives you fine-grained control over the user ID. Knowing when to use each can improve the security posture of your Kubernetes applications.
That said, both runAsUser and runAsNonRoot can co-exist. When they do, runAsUser specifies which UID to use, and runAsNonRoot ensures that UID is not root.
In normal, production systems one would never run a container as root so the image would, based on your organizations policies and image build process, have a user setup and the Kubernetes manifest would have runAsNonRoot set to true.