How to communicate over serial port from a Docker container
12 Sep 2022Recently, I have worked on a IoT project at Enix. In order to flash the ESP32 chip that we put on our board with our firmware, we use esptool, the official tool to communicate with the ROM bootloader in Espressif chips through UART protocol as described in its documentation. It communicate with the ROM bootloader over the serial port represented by the device /dev/ttyUSB[0-9]+
.
One of our goals was to make flashing and upgrading the firmware very easy. So we decided to build a docker image that could flash the device with a simple docker run
command, taking advantage of image tagging to build one image per version of the firmware, so the user only have to fetch the docker image, nothing else.
To communicate with the /dev/ttyUSB0
device (the default one), we need to make it available from the container it first. So as usual I tried to start my container, trying to mount the device like any other file docker run --rm -it -v /dev/ttyUSB0:/dev/ttyUSB0 flashboard
. It worked well until... it didn't 😅. When I tried to erase the flash with esptool, it just told me that the device didn't exists!
$ docker run --rm -it -v /dev/ttyUSB0:/dev/ttyUSB0 flashboard
$ esptool.py --port /dev/ttyUSB0 erase_flash
esptool.py v4.2.1
Serial port /dev/ttyUSB0
A fatal error occurred: Could not open /dev/ttyUSB0, the port doesn't exist
So what happened here? Docker actually prevent users from mounting devices in unprivileged container for security reasons. Indeed using the mknod command, you could just setup a device file with the same major and minor as the root filesystem and easily escape the container. So docker prevent device being accessed from a unprivileged container, even if the device file is mounted. So you got 2 solutions here: you can either add the --privileged
flag (it basically allows to escape from the container, so in most cases it's not what you want) or add the --device
flag. The device flag will mount the device file AND allows you to communicate with the device represented by this file by allowing communication between the software and the device driver.
Since the project isn't released yet I will not explain what is it about, but I hope I will be able to do so in a further post 🙂 For now I can only suggest that you read the following links that might help you understand a bit more how devices work in Linux and how it is related to docker security:
- StackOverflow: Why Docker needs the --device flag?
- Privileged containers and capabilities
- StackOverflow: Use of MAJOR and MINOR device numbers
Last update: 12 Sep 2022