Root Container

This demo builds a standard docker image from the demo application just using a standard Dockerfile. For details on the demo application see hello spring boot application.

Java Base Images

Standard Dockerfile

FROM openjdk:11.0.9-jre-slim-buster
COPY step2-hello-root-1.0.0-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT java -jar /app.jar

Build the Docker Image

To build the docker image just run

./gradlew clean build docker

To push the docker image to a docker registry please specify your target registry in gradle.properties before performing the next command:

./gradlew clean build dockerPush

Runs with Root by default

When using defaults for building a container image the container will run using the root user by default.

You can prove this by using these commands:

docker container run --rm --detach --name hello-root \
-p 8080:8080 andifalk/hello-root:latest-arm64
docker exec hello-root whoami

This should return the following user information (it really is root)

root

You should also be able to reach the dockerized application via localhost:8080.

Finally, stop the running container by using the following command:

docker stop hello-root

Linux capabilities

Back in the old days the only way in Linux has been to either execute a process in privileged (root) or unprivileged mode (all other users).

With linux capabilities you can now break down privileges used by executing processes/threads to just grant the least privileges required to successfully run a thread.

Just look up the detailed docs for linux capabilities by

man capabilities

Docker runs with a balanced set of capabilities between security and usability of containers. You can print the default capabilities set by docker by using this command:

docker container run --rm -it alpine sh -c 'apk add -U libcap; capsh --print'

If you even run the container in privileged mode (you should usually never do that) then you get full privileged root access with all linux capabilities set:

docker container run --privileged --rm -it alpine sh -c 'apk add -U libcap; capsh --print'

In privileged mode you can for example list and change partition tables:

docker container run --privileged --rm -it alpine sh -c 'apk add -U libcap; capsh --print; fdisk -l'

Usually you even don't need the default capabilities defined by docker. A common use case is to run a container listening on a privileged tcp port (below 1024), e.g. using a http server. For this you just need the capability CAP_NET_BIND_SERVICE:

docker container run --cap-drop=ALL --cap-add=net_bind_service --rm -it alpine sh -c 'apk add -U libcap; capsh --print'

For more details on docker security consult the docker security docs.

Linux CGroups

Docker uses the Linux cgroups to limit resource usage of containers.

To limit the container to use a maximum of 200MiB and only one half of a cpu use this command:

docker container run --cpu-shares=0.5 --memory=200m --rm --detach --name hello-root -p 8080:8080 andifalk/hello-root:latest-arm64

You will recognize that the spring boot application start up is much slower in this container due to less cpu power.

You can always check the state of the app by issuing the logs:

docker logs hello-root

To see the actual resource consumption of the container use the docker stats command:

docker stats hello-root

All details on limiting resources can be found in docker resource constraints.

Check image for Vulnerabilities

Now let's check our image for vulnerabilities of critical and high severity using these commands:

 trivy clean --scan-cache
 trivy image --severity=HIGH,CRITICAL andifalk/hello-root:latest-arm64

You only need the first command to clear the cache when using images with latest tag.

Note: It is a good practice to always use specific version tags instead of the latest tag. For demo purposes, this just makes things easier.

Next

Next: Rootless Container

Last updated