„unshare --mount“ innerhalb einer Jenkins-Chroot-Umgebung

„unshare --mount“ innerhalb einer Jenkins-Chroot-Umgebung

In einigen meiner Build-Skripte habe ich Mount-Namespaces als Mechanismus zum sicheren Mounten verwendet, ohne diese Mounts jemals zurückzulassen, wenn das Skript beendet wird. Nicht freigegebene Einhängepunkte werden implizit ausgehängt, wenn der letzte Prozess in diesem Namespace beendet wird..

Meine Skripte enthalten normalerweise eine Strophe wie diese:

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

Während dies bei mir eine Zeit lang problemlos funktioniert hat, ist bei mir vor Kurzem ein Problem auf einem Jenkins-Build-Server aufgetreten.

Ich glaube, das Problem besteht darin, dass das Build-Skript selbst innerhalb einesJenkins Chroot-UmgebungWenn das Skript ausgeführt wird unshare --mount ..., schlägt es mit dem folgenden Fehler fehl:

unshare: cannot change root filesystem propagation: Invalid argument

Leider verstehe ich diese Einschränkung nicht wirklich und weiß auch nicht, wie ich sie umgehen kann. Wenn ich einen Chroot auf der Kommandozeile ausprobiere, kann ich diesen Fehler nicht reproduzieren. Ich weiß nicht, was das Jenkins-Plugin getan hat, um dies zu verursachen.

Das Wichtigste ist, dass diese Einhängepunkte beim Beenden entfernt werdenjedes Mal, ohne Ausnahme.

Antwort1

Basierend auf dem Kommentar von AB habe ich einen Workaround gefunden:

AB schrieb:

Ich erhalte denselben Fehler, wenn ich chroote und in das RootFS-Verzeichnis einer lxc-Installation einbinde unshare --mount bash. Wenn ich dieses Verzeichnis zuerst an anderer Stelle binde mounte (--private) und dann dort chroote, unshare --mountfunktioniert es. Ich weiß nicht, was das bedeutet, aber ich hoffe, dass dies dabei hilft, die Ursache oder einen Workaround zu finden (Hinzufügen einer Bind-Mount in der Pipeline).

Auf dieser Grundlage habe ich festgestellt, dass dies nicht funktioniert:

unshare --mount bash -c 'echo hello'

Aber das funktioniert:

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

Antwort2

Die Ursache des Problems liegt darin, dass unshareversucht wird, die Mount-Propagation-Flags des Stammverzeichnisses zu setzen, was nur für Mount-Punkte möglich ist. Das Stammverzeichnis der Jenkins-Chroot-Umgebung ist kein Mount-Punkt.

Zum Beispiel:

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

Eine vollständige Reproduktion:

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

Ein einfacher Workaround, der keine Mounts außerhalb des Mount-Namespaces vornimmt. Er verlässt das Chroot, um die Mount-Propagation festzulegen, und geht davon aus, dass der Mount-Befehl außerhalb des Chroots verfügbar ist:

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

Oder konvertieren Sie das Chroot in eine pivot_rootUmgebung:

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

verwandte Informationen