unshare() 之后是否可以在进程的命名空间中挂载设备

unshare() 之后是否可以在进程的命名空间中挂载设备

假设我有一个带有隔离 MOUNT 命名空间的进程,pid 1200该进程称为unshare()将其名称空间与父进程隔离。然后我想安装一个只能在pid 1200命名空间内部访问的设备pid 1200。这可能吗?

我想在运行的 LXC 容器内挂载设备或绑定挂载主机上的目录,而无需lxc.monitor.unshare = 1重新启动容器。

答案1

不太理想,但您始终可以挂载 NFS 或其他网络文件系统。


下面的部分做了不是工作(至少不适用于 4.2 内核),将其作为参考,这样人们就不必自己尝试。

尽管当您输入挂载命名空间(nsenter -msetns(CLONE_NEWNS))时,您的工作目录会自动更改为该命名空间的根(/),但仍然可以在某些文件描述符上打开目录,输入命名空间并仍然在该命名空间上打开该目录fd (例如fchdir()对其执行 a )。

所以你会认为这种方法可能有效:

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sched.h>

void die(char *msg) {perror(msg); exit(1);}
int main(int argc, char *argv[]) {
  int fd;
  if (argc != 3) {
    fprintf(stderr, "Usage: %s <source-in-current-namespace> <dest-in-namespace-on-stdin>\n");
    exit(1);
  }
  fd = open(argv[1], O_RDONLY|O_DIRECTORY);
  if (fd < 0) die("open");
  if (setns(0, CLONE_NEWNS) < 0) die("setns");
  if (fchdir(fd) < 0) die("fchdir");
  printf("cwd: %s\n", get_current_dir_name());
  if (mount(".", argv[2], 0, MS_BIND, 0) < 0) die("mount");
}

它确实可以工作直到fchdir(),但mount失败并显示EINVAL

# ~/a.out /home /mnt < /proc/1200/ns/mnt
cwd: (unreachable)/home
mount: Invalid argument

答案2

我在这方面取得了一些成功 - 如果没有使用 lxc 容器,我确实设法使其适用于其他私有挂载命名空间。因为 lxc 是建立在我也在使用的底层 linux 命名空间上的,所以我看不出有任何理由它不适合你。

首先,我设置命名空间,如下所示:

sudo unshare -m sh -c '
    mount -ttmpfs none /tmp
    echo x > /tmp/mytmp
    findmnt -o+PROPAGATION /tmp
    echo "$$"
    cd   /tmp
    exec "$0" -i

TARGET SOURCE FSTYPE OPTIONS     PROPAGATION
/tmp   tmpfs  tmpfs  rw          private
/tmp   none   tmpfs  rw,relatime private
29384
$ 

...我得到了一个交互式 shell。我在单独的终端会话中做的下一件事是......

sudo sh -c ' { cd /dev/fd/0 ; mkdir mnt
               ls -l;         cat mytmp
             } 3<$0/ns/mnt  <$0/29384/cwd
' /proc/29384

drwxr-xr-x 2 root root 40 Jan  4 02:52 mnt
-rw-r--r-- 1 root root  2 Jan  4 02:38 mytmp
x

...这非常令人鼓舞!

但我无法在那里挂载 - 每次我尝试将mount父 ns 目录覆盖到子 ns 中的目录时,它都会失败 - 悲惨。一些研究表明这是设计使然(特别是:请参阅有关 PROPAGATION 标志的警告man 7 user_namespaces。什么做过不过,工作是(在新的命名空间中):

sudo unshare --propagation slave -m sh -c '
     mount -ttmpfs none /tmp; cd /tmp
     exec "$0" -i'

然后在父命名空间会话中...

sudo mount --bind / /mnt
sudo mount --bind / /tmp
sudo mount --bind /tmp /mnt/img/tmp

现在上面的方法适用于第一种情况,但不适用于第二种情况。因为子 ns 不会向上传播 fs 更改,所以父级 ns 不会影响对其 fs 视图所做的更改。因此,因为孩子有自己的坐骑,所以/tmp父母所做的任何事情都无关紧要。但是,如果存在一些公共层次结构并且子 ns 配置为接收文件系统更改,那么它将要查看父级向下传播的更改。

在运行上述命令后的子 ns 中......

ls /tmp /mnt /mnt/tmp

/mnt:
bin   dev  etc   lib    mnt  proc  run   srv  tmp  var
boot  esp  home  lib64  opt  root  sbin  sys  usr

/mnt/tmp:
serverauth.FT3Z6IFyWW
systemd-private-...systemd-timesyncd.service-YUkVU6

/tmp:

所以我想回答这个问题 - 是的,我相信这是可能的。但是,我也相当确定你需要安排竟然如此提前。

答案3

这个答案有一些工作示例,使用 unshare (不是作为 root)和 nsenter (作为容器内的 root)来引起绑定挂载,并以同步方式调用它们来执行您想要的操作。不需要真正的 root 访问权限

遗憾的是,它需要 util-linux 2.39.1,这可能意味着 ubuntu 23.04 或更高版本。

请注意,取消共享调用具有以前互斥的选项。

我仍在尝试让它适用于早期的 ubuntu,例如 22.04(最新的 LTS)。一旦 24.04 出来我就会停止尝试。

相关内容