如何在 Linux 命名空间中创建 /dev

如何在 Linux 命名空间中创建 /dev

AFAIK 容器术语,我本质上想要完成的是编写我自己的“容器运行时”。

我在做什么:

user@host:~$ mkdir test
user@host:~$ cd test
user@host:~/test$ mkdir dev
user@host:~/test$ mkdir proc
user@host:~/test$ echo 1 |sudo tee /proc/sys/kernel/unprivileged_userns_clone
user@host:~$ unshare --ipc --mount --net --pid --uts --cgroup --user \
  --map-root-user --fork bash
root@host:~/test# mount none -t tmpfs dev/
root@host:~/test# touch dev/zero
root@host:~/test# mount /dev/zero -o bind dev/zero
root@host:~/test# echo 1 > dev/zero
bash: dev/zero: Permission denied
root@host:~/test# ls -lah dev
total 4.0K
drwxrwxrwt 2 root   root      60 Sep  1 15:12 .
drwxr-xr-x 3 root   root    4.0K Sep  1 13:47 ..
crw-rw-rw- 1 nobody nogroup 1, 5 Sep  1 13:55 zero
root@host:~/test# mount # we are still looking at hosts /proc
<...>
none on /home/user/test/dev type tmpfs (rw,relatime,uid=1000,gid=1000)
udev on /home/user/test/dev/zero type devtmpfs (rw,nosuid,relatime,size=3921088k,nr_inodes=980272,mode=755)
root@host:~/test# mount none -t proc proc/
root@host:~/test# cat proc/mounts
<...>
none /home/user/test/dev tmpfs rw,relatime,uid=1000,gid=1000 0 0
udev /home/user/test/dev/zero devtmpfs rw,nosuid,relatime,size=3921088k,nr_inodes=980272,mode=755 0 0
none /home/user/test/proc proc rw,relatime 0 0

回显会dev/zero产生错误。有人能告诉我我做错了什么吗?

我从 dockers runc(libcontainer) 那里得到了这个想法: https://github.com/docker/runc/blob/ae2948042b08ad3d6d13cd09f40a50ffff4fc688/libcontainer/rootfs_linux.go#L463

这个问题可能相关: -bash: /dev/null: 权限被拒绝

操作系统:Debian Buster 内核:4.19.37

答案1

快速解决您的问题:使用选项挂载tmpfsmode=设置文件系统根目录的权限),并使用模式没有粘滞位 ( 01000) 或其他人的写权限 ( 002) ——两者都是默认情况下:

root@host:~/test# mount -o mode=0755 -t tmpfs none dev/

你不会失去任何东西;/dev首先让每个人都可以在容器内粘写目录并不是一个好主意(了解 docker 是否也在这样做会很有趣;-))


发生这种情况是因为用户命名空间内的挂载:内核根据执行挂载的进程tmpfs的未翻译凭据设置文件系统根目录的所有者(然后在输出中),并将其权限设置为默认值(对于每个人) + 粘性位)。tmpfsuid=1000,gid=1000mount(1)01777rwx

非 root 所有者、其他人的写入权限 ( ) 以及目录上的S_IWOTH粘着位 ( )的组合会在较新的 Linux内核中触发奇怪的行为:如果标志为使用过,即使是由目录所有者完成的:S_ISVTXopen(2)EACCESO_CREAT

$ mkdir doo; chmod 1777 doo 
$ su -c 'mknod -m 666 doo/null c 1 3'  # or touch doo/null; mount -B /dev/null doo/null 
$ echo > doo/null
bash: doo/null: Permission denied

$ perl -MFcntl -e 'sysopen(NULL, "doo/null", O_WRONLY|O_CREAT, 0666) or die $!'
Permission denied at -e line 1.
$ perl -MFcntl -e 'sysopen(NULL, "doo/null", O_WRONLY, 0666) or die $!'
$ # ok!

$ chmod -t doo
$ echo > doo/null
$ # ok!

这可以在 Debian Buster / 4.19.0-5 和更新的内核上重现,但不能在 Debian Stretch / 4.9.0-4 上重现。

该行为是作为此行为的副作用引入的犯罪:

commit 30aba6656f61ed44cba445a3c0d38b296fa9e8f5
Author: Salvatore Mesoraca <[email protected]>
Date:   Thu Aug 23 17:00:35 2018 -0700

    namei: allow restricted O_CREAT of FIFOs and regular files
...
diff --git a/fs/namei.c b/fs/namei.c
...
+static int may_create_in_sticky(struct dentry * const dir,
+                               struct inode * const inode)
+{
+       if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) ||
+           (!sysctl_protected_regular && S_ISREG(inode->i_mode)) ||
+           likely(!(dir->d_inode->i_mode & S_ISVTX)) ||
+           uid_eq(inode->i_uid, dir->d_inode->i_uid) ||
+           uid_eq(current_fsuid(), inode->i_uid))
+               return 0;
+
+       if (likely(dir->d_inode->i_mode & 0002) ||
+           (dir->d_inode->i_mode & 0020 &&
+            ((sysctl_protected_fifos >= 2 && S_ISFIFO(inode->i_mode)) ||
+             (sysctl_protected_regular >= 2 && S_ISREG(inode->i_mode))))) {
+               return -EACCES;
+       }
+       return 0;
+}

不会doo/null清除第一个文件,if因为它既不是 fifo,也不是常规文件,包含的目录确实设置了粘性位,并且其 uid 与目录的 uid 以及尝试打开它的进程的 uid 不同。

它将立即匹配第二个,if因为包含的目录设置了002(write-for-others)位。

我在 lkml 讨论中没有找到引入这一点的地方(1,2,3)是否考虑过这种影响。无论如何,将字符设备放入世界可写的粘性目录中不太可能是有意的。

相关内容