Mounting Windows File Shares in Docker

A network graph on top of an image of shipping containers

I recently had the need to mount a Windows network file share location within a Docker container. Thus, I tested the mounting of the share on my local machine. It worked, so I figured I’d simply set up the container to mimic the steps I took to mount the share on the host machine. Unfortunately, it wasn’t as straightforward as that. However, I was able to achieve the mount, and this post explains the steps I took to achieve the mount.

The Setup

First, my host machine is Linux based laptop running Ubuntu 18.04 Desktop with Docker Community Edition installed. This workstation is situated within a large domain managed by Windows Active Directory.

Therefore, to connect and mount the file shares in this environment, my Linux host utilizes the CIFS – Common Internet File System – protocol. Consequently, I must tell the Linux mount command I want to use this protocol; I use the -t (type) option.

mount -t cifs example.com/path/to/share /path/to/mount/location

SMB Credentials File

The Linux mount command allows for the use of a file to provide network credentials. I named my credentials file smbcreds.

username=MY_USER
password=MY_PASSWORD
domain=example.com

Since I’m using this file, I must supply an argument to the mount command that provides the path to the file; I’ll use the -o (options) option.

mount -t cifs -o credentials=/path/to/smbcreds  
example.com/path/to/share /path/to/mount/location

As far as mounting the Windows share goes, using the preceding command is all I need. As I’ll cover, doing this in the Docker container isn’t as simple.

What’s SMB and CIFS?

For those unfamiliar with SMB and CIFS, they’re network file sharing protocols. SMB stands for Server Message Block. It’s a protocol originally developed by IBM. Later, Microsoft released their own version of the Protocol. Accordingly, it is in use on Windows servers and workstations.

Similarly, CIFS was developed by Microsoft and is a version of SMB. The two protocols are compatible and synonymous.

Docker Image and Container

Here’s the Dockerfile I used to build the Docker image.

FROM ubuntu:18.04
RUN mkdir -p /app/mnt
WORKDIR /app
RUN apt-get update && apt-get install -y cifs-utils
COPY smbcreds /app/
COPY test.bash /app/
RUN chmod +x /app/test.bash
CMD ["/app/test.bash"]

First, the base image is Ubuntu 18.04. Then, inside that image I’m creating a directory called app. Also, inside of the app directory I’m creating another directory called mnt which I’m using as a mount point for the network share.

After the Ubuntu base is installed, I install the cifs-utils package which provides the CIFS protocol support. Then, I copy my credentials file into the app directory along with a script (test.bash) that performs the mount command – as elaborated in the previous section.

$ docker image build -t myimage .
Sending build context to Docker daemon 4.096kB
Step 1/8 : FROM ubuntu:18.04
---> 7698f282e524
Step 2/8 : RUN mkdir -p /app/mnt
---> Using cache
---> ae90f6f8f32a
Step 3/8 : WORKDIR /app
---> Using cache
---> 30cb44d469c6
Step 4/8 : RUN apt-get update && apt-get install -y cifs-utils
---> Using cache
---> d8fb24d7ad03
Step 5/8 : COPY smbcreds /app/
---> Using cache
---> 03c5a0710ad2
Step 6/8 : COPY test.bash /app/
---> Using cache
---> 21b3649b44a2
Step 7/8 : RUN chmod +x /app/test.bash
---> Running in 946104eb9d21
Removing intermediate container 946104eb9d21
---> 3b4173ee3d87
Step 8/8 : CMD ["/app/test.bash"]
---> Running in 05c81bd460bd
Removing intermediate container 05c81bd460bd
---> 5c17e1511b29
Successfully built 5c17e1511b29

At this point, so far, so good… Now, let’s create the container and run the script that mounts the network share. In addition, to the mounting there’s a command in the script that lists the contents of the mounted location. As a result, we can confirm the mount was successful.

docker container run --network host myimage

Due to the share being located on the same network as my host, I need to instruct the container to use the host network. I do this with the --network host option.

But, when I run the command I receive the following error message:

Unable to apply new capability set.
umount: /app/mnt : must be superuser to unmount.

Runtime privilege and Linux capabilities

First of all, I forgot to mention the test.bash script also un-mounts the share. It seems as though, there’s an error happening when that command is executed.

More importantly, perhaps, what’s this Unable to apply new capability set? After doing some research, I learned that by default containers run “unprivileged”. Due to this setting, the container has no access to the host’s devices or many other kernel capabilities. In fact, this is a good default as the idea behind Docker is isolation from the host machine.

For this reason, our container doesn’t have the capability to mount the network share. Therefore, we must manually add the capability. And we can accomplish this with the --privileged flag.

$ docker container run --network host --privileged myimage 
domain=example.com
mount.cifs kernel mount options: ip=192.168.64.190,unc=\smbshare\DATA,user=MY_USER,domain=example.com,prefixpath=path\to\share,pass=*********
1-17-19
Archive
Thumbs.db

It works! The following part of the output is the contents of the mounted directory.

 1-17-19
Archive
Thumbs.db

Evidently, one can exercise fine grained control over which capabilities are added to the container with the –cap-add option. But, I couldn’t figure out how to get this working to mount the SMB share. I used –cap-add ALL and still was not able to mount in the container successfully. I suspect there’s some device the container needs access to, but I don’t know which one. Because I’m left wondering, dear reader, please let me know if you find out how to do this.

For more information on runtime privileges and Linux capabilities, see the Docker documentation.

Leave a Reply

Your email address will not be published. Required fields are marked *