Missing r/w permissions inside container to control GPIO pins

Hi,

I’m running this little project on my Raspberry Pi to control some GPIO pins of the Raspi. I’ve been doing so for years and recently, I started playing around with the new rootless .NET Docker images.

Unfortunately, I’m struggling with the permissions inside the Docker container - I simply have no write access to /dev/gpiomem.

Here’s what I tried:

  1. Create new user app_raspi with UID 64200 on the host: sudo adduser --uid 64200 --disabled-password --no-create-home app_raspi
  2. Add the newly created user app_raspi to the gpio group: sudo usermod -a -G gpio app_raspi

However, when starting my container with the newly created user, the underlying library of my project still cannot access the GPIO pins. So I checked the following things on the host:

myUser@myRaspi:~ $ grep gpio /etc/group
gpio:x:997:myUser,app_raspi

myUser@myRaspi:~ $ grep app_raspi /etc/passwd
app_raspi:x:64200:64200:,,,:/home/app_raspi:/bin/bash

myUser@myRaspi:~ $ sudo -u \#64200 test -r /dev/gpiomem; echo "$?"
0

myUser@myRaspi:~ $ sudo -u \#64200 test -w /dev/gpiomem; echo "$?"
0

myUser@myRaspi:~ $ ls -lh /dev/gpiomem
crw-rw---- 1 root gpio 245, 0 Jan  6 12:00 /dev/gpiomem

So I started the container with bash as entrypoint:
docker run -it --rm --user=64200 -v /dev/gpiomem:/dev/gpiomem --entrypoint /bin/bash mu88/raspifancontroller:latest
Here’s some output as well:

I have no name!@9218798747ed:/app$ whoami
whoami: cannot find name for user ID 64200

I have no name!@9218798747ed:/app$ id
uid=64200 gid=0(root) groups=0(root)

I have no name!@9218798747ed:/app$ test -r /dev/gpiomem; echo "$?"
1

I have no name!@9218798747ed:/app$ test -w /dev/gpiomem; echo "$?"
1

I have no name!@9218798747ed:/app$ test -r RaspiFanController.dll; echo "$?"
0

I have no name!@9218798747ed:/app$ test -x RaspiFanController.dll; echo "$?"
0

I have no name!@9218798747ed:/app$ ls -lh /dev/gpiomem
crw-rw---- 1 root 997 245, 0 Jan  6 11:00 /dev/gpiomem

I would have assumed that it should now work as app_raspi is a member of the gpio group on the host with r/w permissions and the container runs as 64200 - but I’m still missing something :thinking:

That will not affect the container. That has a different database of groups and users. Only the IDs will be the same. Since gpiomem is a device, you could try --device /dev/gpiomem instead of bind mounting.
You could also try --priveleged or capabilities I wouldn’t change the permissions of a device file.

That’s how I used it before, but the guys from Microsoft recommended binding it as a volume. Anyhow, binding it as a device doesn’t change anything, the GPIO pins cannot be accessed.

Isn’t using --privileged similar to using a root user as before?

Privileged mode would allow the container to communicate with the kernel without restrictions so it is usually not recommended, but you can test it and if it works, then figure out which capability you need to set.

Okay, I tried it with privileged mode, but the error remains the same.

Just for the sake of completeness: since I do only see the error in the .NET stack, I filed this issue

Thank you for sharing the GitJHub issue. Make sure you share it in your first post next time so we understand the history of the issue and what people already suggested…

Once again, setting group memberships on the host won’t help. You are running a process in a container. If the file has to be written by a specific group, that group has to be assigned to the user in the container. You can set groups similarly to setting a user id fo the container process. The username and the groupname don’t have to exist in the container. It is all about IDs.

docker run --group-add 997 ...

You could also add the group id after the user id, but that would completely override the group memberships in case the user actually existed in the container and was member of groups.

docker run --user 64200:997 ...

In your case , since the user is not the owner of the file, you don’t even need to create one. But if you create one, don’t do it on the host. That won’t help in the container. Create a user only if you know that some aplications want to refer to its name. Otherwise a name is basically useless.

You shared checking file permissions and you ran the “id” command as well, which clearly shows that the user in the container is not a member of any group excep the root group. That is because you defined only the user, but not the group.

Because you shared the GitHub issue, I also see how someone suggested using bind mounts instead of the device option. Note that a device is not a regular file and certainly not a folder as it was suggested on GitHub. Bind mounts are mainly for regular files and the device options was created for devices. One day I will have to check what that does exactly, but in your case the problem seems to be the lack of group membership in the container.

1 Like

Wow, super impressive how quickly you found the solution - thank you very much! Now I can successfully control the GPIO pins again :muscle:t2:
This is my current docker-compose.yaml:

version: '3'
services:
  raspifancontroller:
    container_name: raspifancontroller
    user: "64198:997"
    environment:
      - LD_LIBRARY_PATH=/opt/vc/lib
      - AppSettings__UpperTemperatureThreshold=70
      - AppSettings__LowerTemperatureThreshold=55
    image: mu88/raspifancontroller:latest
    ports:
      - 127.0.0.1:5000:8080
    volumes:
      - /lib:/lib
      - /usr/bin/vcgencmd:/usr/bin/vcgencmd
    devices:
      - /dev/vchiq
      - /dev/gpiomem

As you can see, I’m also mounting the volume /usr/bin/vcgencmd into the container. I need this tool to access the current temperature (see here).
Calling /usr/bin/vcgencmd measure_temp currently fails with the following error:

vcgencmd: error while loading shared libraries: libvchiq_arm.so.0: cannot open shared object file: No such file or directory

Some time ago, somebody helped me here. So I tried doing the same:

myAdmin@myRaspi:~/Docker/RaspiFanController $ ldd /usr/bin/vcgencmd
        linux-vdso.so.1 (0x0000007f9be0c000)
        libvchiq_arm.so.0 => /lib/aarch64-linux-gnu/libvchiq_arm.so.0 (0x0000007f9bd9b000)
        libvcos.so.0 => /lib/aarch64-linux-gnu/libvcos.so.0 (0x0000007f9bd7d000)
        libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000007f9bd4c000)
        libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000007f9bbd8000)
        /lib/ld-linux-aarch64.so.1 (0x0000007f9bddc000)
        libdl.so.2 => /lib/aarch64-linux-gnu/libdl.so.2 (0x0000007f9bbc4000)

But when trying to start a container, bash cannot be started anymore:

myAdmin@myRaspi:~/Docker/RaspiFanController $ docker run -it --rm --user="64198:997" -v /usr/bin/vcgencmd:/usr/bin/vcgencmd -v /lib/aarch64-linux-gnu:/lib/aarch64-linux-gnu -e LD_LIBRARY_PATH=/lib/aarch64-linux-gnu --entrypoint /bin/bash mu88/raspifancontroller:latest
/bin/bash: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by /bin/bash)
/bin/bash: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.36' not found (required by /bin/bash)
/bin/bash: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by /bin/bash)

As soon as I remove -v /lib/aarch64-linux-gnu:/lib/aarch64-linux-gnu -e LD_LIBRARY_PATH=/lib/aarch64-linux-gnu, it is working again, but without this volume binding I cannot test what’s missing to for vcgencmd.

Never mount a binary into a container. Binaries can have dependencies which won’t exist in the container and libraries can be different on each platform. For example Alpine uses “musl libc”. If you need a binary in a container, always install it properly in the container or make sure you built the binary for the target container.

I see, thx. That means that I have to switch back to a Dockerfile and install vcgencmd in there, right? Because some time ago (see here), I switched over to an (IMHO simpler) approach provided by Microsoft to build the image (AKA SDK Container Building Tools).

Yes. Mounting binaries are possible and sometimes even required when you build an image from scratch and there is no linux distribution in the container, but even then I wouldn’t mount something that was installed on the host, only files I built locally for the target architecture and distribution and I stored it in a local folder not in a system folder.

1 Like

Okay, thank you so much for your help! I really appreciated it and learned a lot!