使用 "docker exec" 命令访问特权 docker 容器中的 ttyUSB 时出现"Permission denied"错误。

5

我正在尝试在特权的Docker容器中(作为用户)访问串口,但我得到了“权限被拒绝”的错误,尽管权限已经正确设置。以下是一个最小可重现示例(假设串口设备连接到/dev/ttyUSB0):

# start docker container with your user id, give it privileged access and mount /dev
docker run -itd --user $(id -u) --name test --privileged -v /dev:/dev ubuntu
# add user and add it to dialout (not sure if this is necessary as we have privileged access)
docker exec -it --user 0 test sh -c "groupadd -g $(id -g) user && useradd -m -u $(id -u) -g $(id -g) -G dialout user"
# install picocom to test serial connection
docker exec -it --user 0 test sh -c "apt update && apt install -y picocom"
# run picocom on /dev/ttyUSB0 to check if we can open it
docker exec -it test sh -c "picocom /dev/ttyUSB0"

但是当我尝试这样做时,出现了以下错误:

FATAL: cannot open /dev/ttyUSB0: Permission denied

在以root身份执行命令或在“docker run”命令中直接访问串行设备时,一切正常,但我需要能够从已经运行的容器中访问串行设备。

有人知道我缺少什么吗?


那个输出中的第三行包含相关信息。与该“已运行容器”关联的用户名是什么?那个用户名是否在“dialout”组中?您可能会忽略用户,组文件权限和进程所有权之间的关系。 - sawdust
@sawdust 谢谢您的回复。我不确定我理解您的意思。Docker容器使用我的用户ID(例如1000)启动。在容器启动时,此用户ID没有关联任何用户名。接下来,我在此容器中创建一个具有此用户ID的用户,并将其添加到dialout组。这应该意味着这个新创建的用户有权限打开端口,对吗? - wilson1994
使用Shell命令ps -Af查看所有正在运行的进程。更改用户帐户后,需要重新登录或重启才能生效,以便将其添加到组中。 - sawdust
但是,当执行“docker exec”时,您会在容器中启动一个新进程,这可以视为“新登录”,因此包括将用户添加到dialout的更改?例如,当我打印ps -Af的输出时: $ docker exec -it test sh -c "ps -Af" UID PID PPID C STIME TTY TIME CMD user 1 0 0 13:42 pts/8 00:00:00 /bin/bash user 296 0 0 13:47 pts/9 00:00:00 sh -c ps -Af user 302 296 0 13:47 pts/9 00:00:00 ps -Af - wilson1994
“...你可以将其视为“新登录”,从而包括将用户添加到dialout的更改?”-- 你的假设并未得到验证。请参见https://unix.stackexchange.com/questions/6387/i-added-a-user-to-a-group-but-group-permissions-on-files-still-have-no-effect 在访问串行终端之前运行完整性检查:(1)检查设备所有权:ls -l /dev/ttyUSB*,(2)检查谁在dialout组中(假设dialout是设备的组所有者):members dialout,(3)检查当前用户:whoami - sawdust
现在我看到问题所在了:我的主机操作系统是arch,那里的串口由uucp组拥有,组ID为987。但在ubuntu docker容器内,这个组ID与dialout(20)不匹配,因此ttyUSB0不属于dialout。 - wilson1994
3个回答

4

感谢 @sawdust 指引我找到答案。

问题在于我正在 Manjaro(Arch)操作系统上运行一个 Ubuntu Docker 容器,在 Arch 上 ttyUSB 的所有者是 uucp(组 ID 987),而在 Ubuntu 上它的所有者是 dialout(组 ID 20)。

因此,当将 /dev/ttyUSB0 挂载到 Docker 容器中时,它仍由 gid 987 拥有,但在 Ubuntu 环境中,这个组 ID 不是 dialout,因此即使将用户添加到 dialout 中,用户也无权打开串口。

一个快速修复的方法是创建一个具有正确 gid 的组并将您的用户添加到其中:

docker exec -it --user 0 test sh -c "groupadd -g 987 ttyusb && usermod -a -G ttyusb user"

但这并不是完整的解决方案,因为这只能使它适用于您的主机操作系统和Docker操作系统的组合,而不一定适用于具有其他不同环境的用户。


0
如果仍然相关,以下解决方案对我有效:
docker run --gpus all -it --privileged --name [container_name] \
        -v "$(pwd)/..":/home/app \
        -v /dev/bus/usb:/dev/bus/usb \
        -v /dev/ttyACM0:/dev/ttyACM0 \
        [image_container_name] bash

请注意,在您的应用程序中可能不需要使用“gpu”,因此您可以删除该标志“--gpus all”。

请解释每个参数和标志的作用。您提供的语法(即两次指定“--privileged”)表明您不理解每个标志调用的行为。 - Zak
嗨@Zak,这是每个标志的简短概述,请注意你的应用程序可能不需要所有标志。
  • --gpus all -it --name [container_name]
    -v "$(pwd)/..":/home/app
    --privileged -v /dev/bus/usb:/dev/bus/usb
    --privileged -v /dev/ttyACM0:/dev/ttyACM0
    [image_container_name] bash
- Merwanski
非常感谢您的迅速回复,我真的很感激。我认为您误解了我的请求。我要求您更新您的_ANSWER_,并解释为什么选择使用每个标志。通过调用docker --help可以清楚地了解标准命令语法,但是在编写命令中选择每个参数时要实现的目标却不清楚。例如,如果您能解释一下为什么要两次添加--privileged,那对我来说将会非常有趣。另一个问题是,请解释一下为什么要将当前目录挂载为/home/app。请详细说明。 - Zak
嘿@Zak,好的,我明白你的意思,顺便说一下抱歉,我发现我的先前回复不完整,有一部分丢失了...关于新问题: *“privileged”标志应该只添加一次而不是两次,我会在此之后更新我的第一个答案 *“将当前目录挂载为/home/app”,这只是为了避免将我的源文件复制到容器内,并能够直接从我的电脑上继续工作并被容器看到(我希望现在清楚了),如果不清楚,请告诉我。 - Merwanski

0

使用 "docker run --rm --group-add 986 -it --privileged ..." 命令可以解决问题。

如果您调用 groups 命令,会显示错误但仍然可以工作。

$ groups

dcuser dialout staff groups: cannot find name for group ID 986

986


1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接