“unshare --mount” dentro de um ambiente chroot do Jenkins

“unshare --mount” dentro de um ambiente chroot do Jenkins

Em alguns dos meus scripts de construção, tenho usado namespaces de montagem como um mecanismo para montar com segurança, sem nunca deixar essas montagens para trás quando o script termina. Os pontos de montagem não compartilhados são desmontados implicitamente quando o último processo nesse namespace termina.

Meus scripts geralmente incluem uma estrofe como esta:

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

Embora isso tenha funcionado bem para mim há algum tempo, recentemente tive um problema em um servidor de compilação Jenkins.

Acredito que o problema é que o próprio script de construção está sendo executado dentro de umambiente chroot jenkins. Então, quando o script é executado unshare --mount ..., ele falha com o erro:

unshare: cannot change root filesystem propagation: Invalid argument

Infelizmente, eu realmente não entendo essa restrição ou como contorná-la. Quando tento um chroot na linha de comando, não consigo replicar esse erro. Não sei o que o plugin Jenkins fez para causar isso.

O mais importante é que esses pontos de montagem sejam removidos na saídasempre sem falhar.

Responder1

Com base no comentário da AB, encontrei uma solução alternativa:

AB escreveu:

Eu recebo o mesmo erro se eu fizer chroot para uma instalação do diretório rootfs do lxc e unshare --mount bash. Se eu primeiro ligar o mount (--private) a este diretório em outro lugar e depois fizer o chroot lá, unshare --mountentão funcionará. Não sei o que isso significa, mas espero que possa ajudar a encontrar a causa ou uma solução alternativa (adicionando uma montagem de ligação no pipeline).

Com base nisso, descobri que isso não funciona:

unshare --mount bash -c 'echo hello'

Mas isso funciona:

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

Responder2

A causa do problema é que unsharetenta definir os sinalizadores de propagação de montagem do diretório raiz, o que só pode ser feito para pontos de montagem. O diretório raiz do ambiente chroot Jenkins não é um ponto de montagem.

Por exemplo:

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

Uma reprodução completa:

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

Uma solução alternativa simples, que não faz montagens fora do namespace de montagem. Ele escapa do chroot para definir a propagação da montagem e assume que o comando mount está disponível fora do chroot:

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

Ou converta o chroot em um pivot_rootambiente:

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

informação relacionada