Docker images run with root privileges by default. root user in the container is the same root (uid:0) as on the host machine. If a user manages to break out of an application running as root in a container, they may be able to gain access to the host machine with the same root user.

Running containers using non-root user provides an additional layer of security against processes escaping the container due to a container engine vulnerability and thereby achieving escalated permissions on the host node.

Starting from GoCD release v19.6.0, GoCD containers run as non-root user, by default. The Dockerized GoCD application will run as go:root (uid:1000, gid:0) user instead of running as root:root (uid:0, gid:0).

What are the changes made to GoCD Docker Images to support running containers as non-root?

1. Add USER instruction in the Dockerfile:

The USER instruction sets the user name (or UID) to use when running the container and for any instructions that follow it in the Dockerfile.

GoCD Dockerfile adds a USER instruction to change the current user from the root user to go (uid:1000) user. This instruction will make sure that the go (uid:1000) user is used while running the docker container and also for any RUN, CMD and ENTRYPOINT instructions that follow it in the Dockerfile.

2. Add go user to root group:

OpenShift Container Platform (OCP) manages containers life-cycle with security in mind at every stage and every layer of application deployments.

By default, OpenShift prevents root user containers by running containers using an arbitrarily assigned user ID. This arbitrary user ID in the container is always a member of root group on OCP. For an image to support running as an arbitrary user, directories and files that may be written to by processes in the image should be owned by the root group and be read/writable by that group.

Hence, to allow GoCD application to be deployed on OCP, dockerized GoCD applications run with go:root user.

Note : The root group does not have any special permissions (unlike the root user). So, there are no security concerns with this arrangement.

For more information, checkout OCP General Container Image Guidelines.

3. Modify GoCD Directories and File permissions:

The GoCD server stores all configuration, pipeline history database, artifacts, plugins, addons and logs into /godata, whereas, additional information such as secure credentials, ssh keys can additionally be stored at /home/go.

As the existing container user has changed from go:go to go:root, the file permissions of the above mentioned directories and files are changed to go:root.

What are the Breaking Changes in GoCD Docker Image?

1. Creating directories in file-system root:

FROM gocd/gocd-server:v19.5.0

RUN mkdir /.ssh

Moving files or directories into or out of system directories (i.e., directories that are critical to the functioning of the operating system), copying files into system directories requires root privileges. As GoCD containers do not run as root, RUN mkdir /.ssh command would result in mkdir: /.ssh: Permission denied error.

In general, root privileges should not be needed by applications running inside the container. In GoCD's context, application-related files and directories should be created under /home/go.

FROM gocd/gocd-server:v19.6.0

RUN mkdir /home/go/.ssh

Alternatively, if an application requires creating root directories, it can be achieved by switching users in the Dockerfile and executing the root directory creation instruction.

2. Installing packages:

FROM gocd/gocd-server:v19.5.0

RUN apk --update add jq

Root privileges are usually required for installing software because of the need to write to system directories. Packages can be installed by switching users in the Dockerfile.

3. go group migration:

FROM gocd/gocd-server:v19.5.0

RUN mkdir /home/go/creds

RUN chown go:go /home/go/creds

As the go user belongs to root group instead of go group, the above Dockerfile can be changed to:

FROM gocd/gocd-server:v19.6.0

RUN mkdir /home/go/creds

RUN chown go:root /home/go/creds

Note: Ownership of any files/directories owned by go group needs to be changed to root group as there will no longer be a go group on GoCD images.

4. Custom Entrypoint Scripts:

GoCD allows running custom entrypoint scripts to configure GoCD Server before starting. These entrypoint scripts could involve instructions for installing required plugins, configuring basic authentication, etc.

Starting from GoCD release v19.6.0, custom entrypoint scripts will run as go user (instead of root user). We recommend users to not include scripts requiring root privileges as part of custom entrypoint scripts.

Any pre-configuration instructions requiring root privileges should be included in a Dockerfile.

5. Shared Volume Permissions:

GoCD stores all configuration, pipeline history database, artifacts, plugins, and logs into /godata. Whereas, secure credentials like SSH private keys among other things, can be mounted at /home/go.

For GoCD to persist these configurations across container restarts, /godata and /home/go directories can be volume mounted inside the container.

Prior to GoCD v19.6.0, GoCD containers are run as root user, because of which, the container user has permissions to the volume mounted folders/files. Starting from GoCD release v19.6.0, the GoCD container user will be running as go(uid:1000) user. As a result, the container non-root user will not have permissions to the volume mounted folder/files (by default).

In the Docker context, container's users are mapped with host's users. Similarly, the permissions for host mounted volumes comes from the host OS. To allow go(uid:1000) within the container to have permissions to the mounted volume folders/files requires changing the mounted volume ownership to go:root.

Multiple USER instructions

The USER instruction in the Dockerfile changes the user to the specified user for the subsequent instructions in the Dockerfile. Multiple USER instructions can be used to switch the user context from root to non-root user.

Example:

FROM gocd/gocd-server:v19.6.0

USER root

RUN mkdir /creds
RUN chown -r go:root /creds
RUN apk --update add jq

USER go

In the above example, the following instruction block is executed as root user because of the preceding USER root instruction. And finally, the user is switched back to go using USER go instruction.

RUN mkdir /creds
RUN chown -r go:root /creds
RUN apk --update add jq

Note: The USER go instruction after the instruction requiring root privileges is required to change the user back to go. Failing to add USER go instruction at the end would make container to execute as root user.

How to verify the container user?

To verify whether the user running inside the container is root or non-root, we can execute following command(s):

1. Verify the UID of container user is non-zero:

$ docker container run gocd/gocd-server:v19.6.0 id
> uid=1000(go) gid=0(root) groups=0(root)

2. Verify the container user does have access to root directory:

$ docker container run gocd/gocd-server:v19.6.0 ls /root/
> ls: can't open '/root/': Permission denied

But, what if I want to run containers as root?

Sometimes, the application container requires access to the privileged resources in order to perform its required function(s). It can be achieved by granting certain privileges to the user, or running the application container as root user.

1. Using USER root instruction:

FROM gocd/gocd-server:v19.6.0

USER root

A USER root instruction can be added to the extending image to change the user context from go user to root user.

2. Using --user argument:

docker container run --user root:root gocd/gocd-server:v19.6.0

The container user can be specified using --user option to the docker container run command while starting the container from command line.

Note: Well designed systems adhere to the principle of least privilege. This simply states that an application should only have access to the resources it needs in order to perform its required function.

GoCD containerized processes are application services and therefore don’t require root access. We strongly recommend users to grant required privileges to the go user and avoid running as root.