Mastering Dockerfile USER: The Key to seamless Kubernetes Deployment

Jehoszafat Zimnowoda
Otomi Platform
Published in
3 min readNov 12

--

Linux users and their corresponding file permissions are essential Linux security features. The containers are nothing more than processes contained by Linux namespaces running with permissions inherited from a user. Understanding these concepts allow easily to extend them to higher level abstractions like Dockerfiles, Kubernetes cluster, container runtime, etc.

So, here’s the deal: Linux sees everything as a file, even users and groups! We’re about to create a new “app” user (UID 1000), being a member of the primary group “app” (GID 2000).

groupadd --gid 2000 app 
useradd -u 1000 -g app app

The first command adds the app:x:2000 to the/etc/groups file. The second one adds app:x:1000:2000::/home/app:/bin/sh string to the /etc/passwd file.

Why is all this important, you ask? Well, these files are like secret ingredients in your grandma’s famous recipe. Many programs count on their existence, and when they’re missing, chaos reigns, and nobody knows why.

The Dockerfile USER drama

A USER in a Dockerfile is an instruction that allows to define the default user that container runtime is about to use. It has the following formatUSER <user>:[group] or USER <uid>:[gid]. It is worth noting that group name (or group ID) is optional. If a Dockerfile specifies only user (USER app), then you must make sure that Linux user have a primary group defined in /etc/passwd file. Otherwise the default group falls back to the root!

USER in a multi-stage build

Now, we dive into the world of multi-stage Dockerfile, because this how we do it these days.

FROM golang:1.21-bullseye as builder
RUN apt update && apt -y install apt-transport-https ca-certificates
RUN groupadd --gid 2000 app && useradd -u 1000 -g app app

FROM scratch
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/group /etc/group
COPY --chown=app:app --from=builder /usr/src/app/webhook /app/webhook
USER app
EXPOSE 443
ENTRYPOINT ["/app/webhook"]

The first stage (builder) is using rich in many utilities based image. At this stage binaries are build and user with group created. The second stage (FROM scratch). It’s like teleporting to a minimalist realm. Here, we simply copy the goodies (binaries, /etc/passwd, and /etc/group files) from our previous stage. Don’t forget to use --chown=app:app when copying to ensure that our user has the keys to the kingdom!

User nobody

The nobody user is set in almost every Linux distribution with UID and GID equal to 65534. Since the container namespaces are isolated by default it is considered as a best practices to execute on behalf of this user your containerised application. If opt in using the nobody user you do not have to execute the groupadd and the useradd commands.

The /etc/groups and /etc/passwd entries looks as follows

nogroup:x:65534:
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin

Kubernetes and Pod security context

Kubernetes, our magical orchestrator in the land of distributed computing, comes into play, like a director in charge of a circus of containers.

Kubernetes introduces the security guards at the container party. The securityContext with runAsUser can override what you defined in the Dockerfile. However the mistmatch between the Kubernetes `runAsUser` and Dockerfile USER may cause file permission issues or ambiguous system errors mentioned earlier. Therefore it is the best to define the default non-root USER in Dockerfile and set it in the security context to ensure smooth runtime in Kubernetes. The below example is instructing Kubernetes to run container as the app user and group.

apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
securityContext:
runAsUser: 1000
runAsGroup: 2000
containers:
- name: demo
image: <yourimage>
securityContext:
allowPrivilegeEscalation: false

At the continer section the allowPrivilegeEscalation is set to false. This falg ensures that low level system calls will always perform with the no_new_priv flag set. Having that, the container cannot gain more linux capabilities during the container runtime.

Summary

Some essential takeaways:

  • ensure that user and primary group are defined in /etc/passwd and /etc/groups files,
  • use Pod and Container securityContext to enforce running containers as not root user and group,

So, there you have it — Dockerfile, users and Kubernetes. Enjoy your journey through the Linux funhouse, and may your containers be as harmonious as a jazz ensemble on a moonlit night. 🚀

--

--

Jehoszafat Zimnowoda
Otomi Platform

Passionate about computer networks and distributed system. OSS contributor and occasionally technical writer.