«unshare --mount» внутри chroot-окружения jenkins

«unshare --mount» внутри chroot-окружения jenkins

В некоторых моих скриптах сборки я использовал пространства имен монтирования как механизм для безопасного монтирования, не оставляя эти монтирования после завершения скрипта. Неразделяемые точки монтирования неявно отключаются при завершении последнего процесса в этом пространстве имен..

Мои сценарии обычно включают в себя такую ​​строфу:

#!/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

Хотя некоторое время у меня все работало нормально, недавно я столкнулся с проблемой на сервере сборки Jenkins.

Я считаю, что проблема в том, что сам скрипт сборки выполняется внутрисреда chroot jenkins. Поэтому при выполнении скрипта unshare --mount ...происходит сбой с ошибкой:

unshare: cannot change root filesystem propagation: Invalid argument

К сожалению, я действительно не понимаю это ограничение или как его обойти. Когда я пробую chroot в командной строке, я не могу воспроизвести эту ошибку. Я не знаю, что сделал плагин jenkins, чтобы вызвать это.

Самое главное, что эти точки монтирования удаляются при выходе.каждый раз без сбоев.

решение1

На основании комментария AB я нашел обходной путь:

АБ написал:

Я получаю ту же ошибку, если я chroot в корневую файловую систему lxc и 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");
}

Связанный контент