Читать книгу Cloud Native Security - Chris Binnie - Страница 25

Installing Rootless Mode

Оглавление

To get started we need to download an installation script as supplied by Docker. It can be found at get.docker.com/rootless and, as with all online content, the script should be read through to check for any security implications that you do not want to be exposed to before applying it. And, having read the comments at the top of the script, you need to run a diff command on the contents of the script github.com/docker/docker-install/blob/master/rootless-install.sh before using the script at the other URL (choose the Raw option for the displayed format on GitHub for easy copying):

$ diff -y get-docker.sh install.sh

This Docker functionality is being actively developed, so if you have trouble with one version of the installation script, try the other, which might be a more stable version.

It should go without saying at this juncture that we do not need to be the root user for the running containers. As a result, at this stage we will become the chris user with this command:

$ sudo -i chris

Clearly, you should alter the username to suit your own needs, potentially using your nonprivileged login user.

We will run the slightly more stable get.docker.com version of the script this way, saving it to install.sh as a filename:

$ curl https://get.docker.com/rootless > install.sh

Now, make it executable and run the script:

$ chmod +x install.sh ; ./install.sh

After the short process is completed, you are greeted with Docker Engine client and server version information; for example, the client is installed as follows to match the server version:

Client: Docker Engine - Community Version: 19.03.12 API version: 1.40 Go version: go1.13.10

In Listing 2.1 we can see the tail end of the installation script's output.

Listing 2.1: Rootless Mode Docker Has Installed and Is Offering the User Information

# Docker binaries are installed in /home/chris/bin # WARN: dockerd is not in your current PATH or pointing to /home/chris/bin/dockerd # Make sure the following environment variables are set (or add them to ~/.bashrc): export PATH=/home/chris/bin:$PATH export DOCKER_HOST=unix:///home/chris/rootless/docker.sock # # To control docker service run: # systemctl --user (start|stop|restart) docker #

Take a look at the post-install advice in Listing 2.1. The binaries have been installed within your user's home directory under the bin/ directory. You can confirm that they are there with an ls command.

The next thing to do is create three environment variables, as follows:

$ export XDG_RUNTIME_DIR=/home/$USER/rootless $ export PATH=/home/$USER/bin:$PATH $ export DOCKER_HOST=unix:///home/$USER/rootless/docker.sock

In the previous examples, for ease, $USER is used in place of chris.

You can also specify a different directory name here if you prefer. Before running the next command, we will need a directory to store our running container content and configuration, so create one now:

$ mkdir rootless

Now we can run this command to get rootless mode going, noting that we are using the preferred overlay2 storage driver:

$ bin/dockerd-rootless.sh --experimental --storage-driver overlay2

Listing 2.2 shows the end of the output, describing how /home/chris/rootless/docker.sock has connected to Docker Engine in rootless mode.

Listing 2.2: Docker in Rootless Mode Has Run Successfully

WARN[2020-08-24T15:51:34.554236269+01:00] Not using native diff for overlay2, this may cause degraded performance for building images: failed to set opaque flag on middle layer: operation not permitted storage-driver=overlay2 INFO[2020-08-24T15:51:34.555462723+01:00] Docker daemon commit=48a66213fe graphdriver(s)=overlay2 version=19.03.12 INFO[2020-08-24T15:51:34.556309674+01:00] Daemon has completed initialization INFO[2020-08-24T15:51:34.602091497+01:00] API listen on /home/chris/rootless/docker.sock

If you run the following command, you will see the processes running for Docker, as the less privileged user:

$ ps -ef | grep docker

Listing 2.3 shows the results.

Listing 2.3: Rootless Mode Docker Processes Running in the Background

chris 9286 9213 0 15:51 pts/0 00:00:00 rootlesskit --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run bin/dockerd-rootless.sh --experimental --storage-driver overlay2 chris 9295 9286 0 15:51 pts/0 00:00:00 /proc/self/exe --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run bin/dockerd-rootless.sh --experimental --storage-driver overlay2 chris 9325 9295 0 15:51 pts/0 00:00:04 dockerd --experimental --storage-driver overlay2 chris 9343 9325 0 15:51 ? 00:00:03 containerd --config /home/chris/rootless/docker/containerd/containerd.toml --log-level info

To start a rootless mode container, we need to point Docker Engine precisely at where the Docker socket file is located. Within a second terminal, we will run the following commands to spawn a rootless Apache container:

$ systemctl --user start docker $ export XDG_RUNTIME_DIR=/home/chris/rootless; \ export DOCKER_HOST=unix:///home/chris/rootless/docker.sock; \ export PATH=/home/chris/bin:$PATH $ docker run -d -p 8000:80 httpd Unable to find image 'httpd:latest' locally latest: Pulling from library/httpd bf5952930446: Already exists 3d3fecf6569b: Pull complete b5fc3125d912: Pull complete 679d69c01e90: Pull complete 76291586768e: Pull complete Digest: sha256:3cbdff4bc16681541885ccf1524a532afa28d2a6578ab7c2d5154a7abc182379 Status: Downloaded newer image for httpd:latest a8a031f6a3a3827eb255e1d92619519828f0b1cecfadde25f802a064c6258138

Excellent. That is what success looks like when the Docker runtime downloads an image and spawns a container in rootless mode. Note that if you had not chosen TCP port 8000 but instead a port lower than 1024 (normally TCP port 80 for web servers), then you would have received an error because, as a nonroot user, we can't open a privileged or root port.

Also, take note that this feature is very new, and the process to getting rootless Docker to work may vary between builds. You have been warned!

If you run into trouble and need to start again, then carefully as the root user you can try the following command (after trying to execute it as your lower privileged user first) to kill off related processes:

$ pkill rootlesskit; pkill dockerd; pkill experimental; pkill containerd

This should stop all the processes so you can start fresh.

Let's do one final test to show that we have a container running in rootless mode that would be accessing the web server. A reminder that unfortunately network namespaces work differently when using rootless mode. Instead, you can try a few other familiar commands such as the following:

$ docker ps CONTAINER ID IMAGE COMMAND STATUS PORTS a8a031f6a3a3 httpd "httpd-foreground" Up 15 minutes 0.0.0.0:8000->80/tcp

In the slightly abbreviated output, you can see that Apache's httpd container is running as hoped. To prove that the networking is different with this implementation, we can use this command to check our container's IP address (replacing a8a031f6a3a3 with the name or hash ID of your container):

$ docker inspect a8a031f6a3a3 | grep IPAddress "IPAddress": "172.17.0.2", "MacAddress": "02:42:ac:11:00:02", "IPAddress": "172.17.0.2",

We can see that the container is using 172.17.0.2 as its IP address, but now try to connect to the exposed port, TCP port 8000:

$ nc -v 172.17.0.2 8000

Nothing happens. The connection does not work using the netcat tool, so we can see that there's definitely a change in the way standard networking is running. According to the documentation cited earlier, this is expected behavior and occurs because “the daemon is namespaced inside RootlessKit's network namespace.” We are not using privileged ports (lower than port 1024), so it is possible to access the container's exposed port; but as you might have guessed, we must do so via the host's network stack. For some potentially helpful context, if you're familiar with Kubernetes, this functionality is called NodePort, where a container directly uses a host port on the host's IP Address so that the container is accessible from outside of the cluster (more can be found at kubernetes.io/docs/concepts/services-networking/service). The following netcat command will work and will not just quietly fail this time:

$ nc -v localhost 8000 Connection to localhost 8000 port [tcp/*] succeeded!

And, to prove that is the correct container that responded to our netcat request, we can use the curl command to check that port on our localhost too:

$ curl localhost:8000 <html><body><h1>It works!</h1></body></html>

We have completed the installation of rootless mode using Docker and additionally successfully proven the networking service of a container running as the user chris. The next steps to continue exploring this improvement to container security would be running a number of containers of varying types to check limitations that this mode introduces in a more complex environment.

Cloud Native Security

Подняться наверх