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 --mount
entã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 unshare
tenta 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_root
ambiente:
#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");
}