Securing Docker containers UID/GID?

Please don’t just set the UID/GID as environment, as it bypasses mechanisms introduced by the image maintainer (case 2) or docker (case 3).

Case 1)
Build image without USER instruction:

cat <<EOF  | docker build -t test:root -
FROM alpine:3.16.3
CMD ["id"]
EOF

Run container without --user argument:

$ docker run -ti --rm test:root
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

The user is root with stripped down capabilities in the isolated container. If someone mounts the /etc folder inside the folder, that person could modify files in the container, but the same person would not be able to mount a remote share into the container. Restrictions on file access could be mitigated by using Isolate containers with a user namespace | Docker Documentation, which effectively maps an unprivileged host user as root user inside the container.

Case 3)
Build image with USER instruction:

cat <<EOF  | docker build -t test:me -
FROM alpine:3.16.3
RUN adduser -u 783 -S me -G users 
USER me
ENTRYPOINT ["id"]
EOF

Run container without --user argument:

$  docker run -ti --rm test:me
uid=783(me) gid=100(users) groups=100(users)

Run container with --user argument to override the user from the USER instruction:

$  docker run -ti --rm --user 1000:1000 test:me
uid=1000 gid=1000

Actually the --user argument seem to not care about the USER instruction from the image at all. Thus, the argument could be applied to an image of case 1 as well.

Case 2) adds a user, but does not use the USER instruction. Instead, an entry point script expect variables following the naming convention *_UID and *_GID (where * is an arbitrary string), chowns the files in volumes and exec’s the main process using that UID/GID. Those images will fail if the --user argument is passed. You can check any linuxserver.io image to see how it’s used and implemented.

While home users prefer type 2 images (after all it’s convinient to have the files chowned and sometimes even chmodded for you), enterprise users usualy use type 3 images.

If you meet a type1 image, the service inside the container either really requires root permissions (and then usually need --cap_add to add required capabilities as well) OR is a good candidate for refactoring to one of the other types.