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");
}

相關內容