Docker – Migrating Volumes with Correct Permissions

containersdocker

I have a question to the permissions of docker volumes.

I've now set up docker on multiple servers and for some containers I used bind mount points and for others I used named volumes, which for me seems like the same – the only difference I see is the directory (/var/lib/docker/volumes/... or self-picked).

Now I have one questions concerning best-practices for docker deployments.

UIDs and GUIDs are shared with the host system.
Let's assume container A has an application that runs as user db with UID being 800 and the corresponding volume has very important production data on it. On my host system UID 800 could be basically any service, let's assume it is an easy-to-take-over service. That service could potentially access the data of the container.

In this case it is desirable to use the named volumes, because they are in a directory that is protected against normal user access, or at least by not creating volumes in public locations (/, /srv, …),

When I initially create a named volume, it gets created inside /var/lib/docker/volumes/.... Most of the time this means that an application inside the container that doesn't run as root, cannot create any files on a volume. Consequently, every application that needs specific folders and permissions, has to set this in the ENTRYPOINT Script, before handing over to the application.

Is this intended behavior? And how do you now move a named volume? Docker doesn't seem to support this out of the box. I've seen lots of approches that use just tar or create a container that exports the volumes, but this seems very hard to automate. Is there an easier/official/scriptable approach to migrate (named) volumes, ideally by creating a tarball that is simple to import? Or am I miss-understanding something here?

Best Answer

Most of the time this means that an application inside the container that doesn't run as root, cannot create any files on a volume.

This is only the case if the image you used to create the named volume does not have the directory in the image, or that directory in the image is not owned by the correct user. The fact that /var/lib/docker is locked down to only allows root in no way prevents containers from using volumes inside of that directory as a user.

Named volumes typically have fewer permission issues because they will be initialized using the contents of the image at the given location. When you create the image, and a user inside of that image, you will want to chown/chmod any directories that user needs access to as part of the image building process. Possible issues I've seen from people trying this:

  • Defining the volume inside the Dockerfile, and then trying to update the directory. This will not work, the directory is mounted as a volume in a temporary container during the RUN commands, and any changes to that volume are discarded when the RUN command finishes. I typically recommend removing the volume definitions from a Dockerfile if you control them.

  • Changing the user at run time to a different uid from the uid inside the image. In this scenario, the image doesn't have the directory permissions setup correctly and will incorrectly initialize the named volume. Since you changed the uid at run time, you'll need to fix the permissions at run time with an entrypoint or a second container that mounts the same volume.

  • Modifying the uid or directory inside the image after someone has already deployed containers with named volumes based off of an older version. Named volumes only get initialized when empty, so changing the image will not change an already existing volume that now has the wrong owner/permissions.

  • Using the same named volume by two different containers simultaneously. In this scenario, the named volume will be initialized once, and the question of which image is used to initialize that volume is a race condition depending on which container gets created first.

Note, the security risk of host volumes is that a vulnerable application on the host, running with the same UID of a privileged application inside of a container, can get access to that container. While not good, it's not a container escape.

You can enable user namespaces to avoid this risk, but when you do so, be aware that host volumes that intentionally map to users or groups on your host will no longer work, including developers mounting code for rapid development, and tools that mount the docker socket for management. Since user namespaces are implemented on the entire docker instance, not per container, you'll want to be sure there are no scenarios where they won't work for you before making the change.

Moving data in and out of a named volume has always been with tar and docker I/O redirection, but you'll need to add a chmod/chown step after this if you change ownership from the host to the container:

# import data
tar -cC source_dir . | \
  docker run --rm -i -v target_vol:/target busybox tar -xC /target
# export data
docker run --rm -v source_vol:/source busybox tar -cC /source . | \
  tar -xC target_dir