Daniel Kraszewski
Daniel Kraszewski
Head of Engineering

The default user in the Docker image

Dec 07, 20224 min read

This fifth article concludes the series of Docker best practices that deserve more love. We will look closely at the default user in the image and pave the way for uninterrupted usage of Docker platform. Make sure you reviewed the previous articles:

  1. Proper use of cache to speed up and optimize builds
  2. Selecting the appropriate base image
  3. Understanding Docker multi-stage builds
  4. Understanding the context of the build
  5. Using administrator privileges

The default user in the Docker image

Regardless of the operating system, it is always good practice to reasonably use administrator privileges. Whether it's the root user on Unix-like systems or the disabled UAC module on Windows the effect may be the same and lead to increased chances of malicious code breaking through security. The principles are the same when it comes to Docker images, despite the fact that it introduces additional isolation between the host system and containers.

So what is the default user? The answer is: the one that was set in the base image or root if the user remained unchanged. Using root gives us some advantages, such as the possibility to install additional dependencies. Due to the location of some libraries, these operations cannot be performed with limited privileges.

Ultimately, we aim to change the user that will be least privileged, which in Dockerfile is quite simple. This is done with the USER instruction which comes in two variants:

  • USER <user>[:<group>]
  • USER <UID>[:<GID>]

The first variant requires the textual names of the user and group we want to switch to, while the latter allows us to use their numeric identifiers directly. Let’s find out how the build process works when USER instruction in both variants is used.

Textual user name as nginx:

tips@u11d:~$ echo " > FROM alpine:3.16 > USER nginx > " > Dockerfile tips@u11d:~$ sudo docker build . Sending build context to Docker daemon 10.75kB Step 1/2 : FROM alpine:3.16 ---> 9b18e9b68314 Step 2/2 : USER nginx ---> Running in b67f70601e4c Removing intermediate container b67f70601e4c ---> 8410027170c6 Successfully built 8410027170c6 tips@u11d:~$ sudo docker run --rm -it 8410027170c6 docker: Error response from daemon: unable to find user nginx: no matching entries in passwd file.

Numeric user identifier as 1000:

tips@u11d:~$ echo " > FROM alpine:3.16 > USER 1000 > " > Dockerfile tips@u11d:~$ sudo docker build . Sending build context to Docker daemon 9.728kB Step 1/2 : FROM alpine:3.16 ---> 9b18e9b68314 Step 2/2 : USER 1000 ---> Running in 869f2e785e83 Removing intermediate container 869f2e785e83 ---> 7d0dcceafd9f Successfully built 7d0dcceafd9f tips@u11d:~$ sudo docker run --rm -it 7d0dcceafd9f $ id uid=1000 gid=0(root)

As we can see from the examples above, both versions were built without a problem. Unfortunately, only the version with a numeric user ID was successfully executed. This is because the USER instruction does not create a user in the image, but only switches to it. Docker, while running the container, could not replace the user's name with his UID (numeric ID), which caused the error.

Need support in Docker platform?

Whether it's integration, containerization or modernization, uninterrupted can handle it efficiently and seamlessly.

A person sitting and typing on a laptop keyboard

The solution is to manually add the user before using it. In images based on the Alpine distribution, we can do this with the adduser -D -u 1000 username command, where:

  • -D parameter disables the password configuration,
  • -u 1000 sets the numeric user and group ID to the indicated value,
  • username is the name of the new user.
tips@u11d:~$ echo " > FROM alpine:3.16 > RUN adduser -D -u 1000 nginx > USER nginx > " > Dockerfile tips@u11d:~$ sudo docker build . Sending build context to Docker daemon 9.728kB Step 1/3 : FROM alpine:3.16 ---> 9b18e9b68314 Step 2/3 : RUN adduser -D -u 1000 nginx ---> Running in a788a92773f9 Removing intermediate container a788a92773f9 ---> 84aecbe49298 Step 3/3 : USER nginx ---> Running in 4db5f569c0dc Removing intermediate container 4db5f569c0dc ---> 63a098041085 Successfully built 63a098041085 tips@u11d:~$ sudo docker run --rm -it 63a098041085 $ id uid=1000(nginx) gid=1000(nginx)

As a result the user is successfully created and changed in the image. The next step is to adjust file and directory permissions. When copying data to the image with COPY or ADD instructions the ownership still remains root, which is a default behavior. Docker's authors anticipated this situation and added the ability to change ownership on the fly. This saves the extra RUN instruction, which would have to do it separately.

To change the ownership of copied files and directories use the --chown=<user>:<group> parameter of the COPY or ADD instruction.

FROM alpine:3.16 RUN adduser -D -u 1000 nginx COPY --chown=nginx . .

If you have trouble remembering the chown shortcut you can always think of its full version - "change owner".

Conclusion

Summarizing, the security principle of least privilege (POLP) can be applied to the Docker platform and containerization concept. It is always a good practice to prevent uncontrolled files or directories ownership and give only the minimum level of access required. On the other hand it is a modus operandi that ownership as well as the process is executed by the same system user. This is definitely a good design as well as a way to meet the security rules and policies and improve the overall security of the system.

Still feeling unsure or need more information about the Docker platform? You can always go through our Docker series to catch up with things.

If this is not enough and you need professional support, do not hesitate to contact us. We are always happy to help.

RELATED POSTS
Michał Miler
Michał Miler
Senior Software Engineer

Passing Body Data in Strapi Webhooks: Triggering GitHub Workflows Example

Nov 27, 20246 min read
Article image
Tomasz Fidecki
Tomasz Fidecki
Managing Director | Technology

Templating Values in Kustomize: Unlocking the Potential of Dynamic Naming for Kubernetes Resources

Mar 13, 20247 min read
Article image
Tomasz Fidecki
Tomasz Fidecki
Managing Director | Technology

Maximizing Efficiency with Dev Containers: A Developer's Guide

Feb 22, 202419 min read
Article image