jenkins chroot 环境中的“unshare --mount”

jenkins chroot 环境中的“unshare --mount”

在我的一些构建脚本中,我一直使用挂载命名空间作为安全挂载的机制,而在脚本终止时不会留下这些挂载。 当该命名空间中的最后一个进程退出时,非共享挂载点将被隐式卸载

我的脚本通常包含这样的节:

#!/bin/bash
self_ns=$(ls -lh /proc/self/ns/mnt)
init_ns=$(ls -lh /proc/$PPID/ns/mnt)
if [ "${self_ns#*mnt:}" = "${init_ns#*mnt:}" ] ; then
    unshare --mount $0 "$@"
    exit $?
fi

虽然这对我来说运行良好一段时间了,但我最近在詹金斯构建服务器上遇到了问题。

我相信问题在于构建脚本本身是在詹金斯 chroot 环境。因此,当脚本执行时unshare --mount ...,它会失败并出现错误:

unshare: cannot change root filesystem propagation: Invalid argument

不幸的是,我真的不明白这个限制或如何绕过它。当我在命令行上尝试 chroot 时,我无法复制此错误。我不知道 jenkins 插件做了什么导致这个的。

最重要的是这些挂载点在退出时被删除每次都没有失败

答案1

根据AB的评论我找到了解决方法:

AB 写道:

如果我 chroot 到 lxc 的 rootfs 目录安装和unshare --mount bash.如果我首先在其他地方绑定挂载(--private)这个目录,然后在那里执行 chroot,unshare --mount然后就可以了。我不知道这意味着什么,但我希望这可以帮助找到原因或解决方法(在管道中添加绑定安装)。

基于此,我发现这不起作用:

unshare --mount bash -c 'echo hello'

但这有效:

mount --bind --make-private / /mnt 
chroot /mnt unshare --mount bash -c 'echo hello'
umount /mnt

答案2

问题的原因是unshare尝试设置根目录的安装传播标志,这只能针对安装点完成。 Jenkins chroot 环境的根目录不是挂载点。

例如:

$ unshare -rm mount --make-rprivate /opt
mount: /opt: not mount point or bad option.

完整再现:

#!/bin/bash
try() {
  mount -t tmpfs t /mnt
  mkdir /mnt/t
  for i in /bin /lib* /sbin /usr /home /proc
  do
    mkdir "/mnt/t$i"
    mount --rbind "$i" "/mnt/t$i"
  done
  chroot /mnt/t unshare -m echo OK
}
export -f try
unshare -rm bash -c try

一个简单的解决方法,它不会在挂载命名空间之外进行挂载。它转义 chroot 以设置挂载传播,并假设挂载命令在 chroot 之外可用:

unshare --propagation unchanged -m sh -c \
'nsenter --mount=/proc/self/ns/mnt mount --make-rslave /; echo Do some mounts'

或者将 chroot 转换为pivot_root环境:

#define _GNU_SOURCE
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

static int pivot_root(const char *new_root, const char *put_old){
    return syscall(SYS_pivot_root, new_root, put_old);
}

int main(int argc, char **argv) {

    if (unshare(CLONE_NEWNS))
        error(1, errno, "unshare");

    int root = open("/", O_DIRECTORY | O_PATH | O_CLOEXEC);
    if (root < 0) error(1, errno, "open /");

    int ns = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
    if (ns < 0) error(1, errno, "open mount namespace");

    if (setns(ns, CLONE_NEWNS))
        error(1, errno, "setns");

    if (fchdir(root))
        error(1, errno, "fchdir");

    if (mount("/", "/", 0, MS_REC|MS_SLAVE, 0))
        error(1, errno, "mount --make-rslave");

    if (mount(".", "proc", 0, MS_REC|MS_BIND, 0))
        error(1, errno, "mount --rbind");

    if (chdir("proc"))
        error(1, errno, "chdir");

    if (pivot_root(".", "proc"))
        error(1, errno, "pivot_root");

    if (umount2("proc", MNT_DETACH))
        error(1, errno, "umount");

    execvp(argv[1], argv + 1);
    error(1, errno, "exec");
}

相关内容