How to create tun interface inside Docker container image?
Asked Answered
G

2

7

I'm trying to create a Docker image with a /dev/net/tun device so that the image can be used across Linux, Mac and Windows host machines. The device does not need access to the host's network interface.

Note that passing --device /dev/net/tun:/dev/net/tun to docker run is undesirable because this only works on Linux.

Once the container is started, I can manually add the device by running:

$ sudo mkdir /dev/net
$ sudo mknod /dev/net/tun c 10 200
$ sudo ip tuntap add mode tap tap

but when I add these lines to the Dockerfile it results in an error:

Step 35/46 : RUN mkdir /dev/net
 ---> Running in 5475f2e4b778
Removing intermediate container 5475f2e4b778
 ---> c6f8e2998e1a
Step 36/46 : RUN mknod /dev/net/tun c 10 200
 ---> Running in fdb0ed813cdb
mknod: /dev/net/tun: No such file or directory
The command '/bin/sh -c mknod /dev/net/tun c 10 200' returned a non-zero code: 1

I believe the crux here is creating a filesystem node from within a docker build step? Is this possible?

Gutsy answered 23/12, 2019 at 7:26 Comment(5)
Digging further, /dev is mounted as a tmpfs overlay. What I'm trying to accomplish may not be possible, unless there's a way to initialize a tun device outside of /dev?Gutsy
If this works manually inside the container, why don't you add the logic to your entrypoint script ?Trometer
The way our stuff works in continuous integration it would be a major annoyance to have an entrypoint script :/Gutsy
@Gutsy I have similar requirement but in my case, I want the interface to read packets from host which its not able to do without --network=host. Here are the details: #65556051Dianoetic
Does this answer your question? Why can't docker build COPY files to the /dev folder?Amalle
A
4

The /dev directory is special, and Docker build steps cannot really put anything there. That also is mentioned in an answer to question 56346114.

Apparently a device in /dev isn't a file with data in it, but a placeholder, an address, a pointer, a link to driver code in memory that does something when accessed. Such driver code in memory is not something that a Docker image would hold.

I got device creation working in a container by putting your command line code in an .sh script wrapping the app we really want to run.

Alumina answered 30/5, 2020 at 2:40 Comment(0)
G
2

I managed to work around this by programmatically creating the TUN device in our software that needs it (which are mostly unit tests). In the setup of the program we can create a temporary file node with major/minor code 10/200:

        // Create a random temporary filename. We are not using tmpfile() or the
        // usual suspects because we need to create the temp file using mknod(),
        // below.
        snprintf(tmp_filename_, IFNAMSIZ, "/tmp/ect_%d_%d", rand(), rand());

        // Create a temporary file node for use as a TUN interface.
        // Device 10, 200 is the device code for a TAP/TUN device.
        // See https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
        int result = mknod(tmp_filename_, S_IFCHR | 0644, makedev(10, 200));
        if (result < 0) {
            perror("Failed to make temporary file");
        }
        ASSERT_GE(result, 0);

and then in the tear-down of the program we close and delete the temporary file.

One issue remaining is this program only works when run as the root user because the program doesn't have cap_net_admin,cap_net_raw capabilities. Another annoyance that can be worked-around.

Gutsy answered 23/12, 2019 at 22:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.