En algunos de mis scripts de compilación, he estado usando espacios de nombres de montaje como mecanismo para montar de forma segura sin dejar estos montajes cuando finaliza el script. Los puntos de montaje no compartidos se desmontan implícitamente cuando sale el último proceso en ese espacio de nombres..
Mis guiones suelen incluir una stansa 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
Si bien esto me ha funcionado bien durante algún tiempo, recientemente me encontré con un problema en un servidor de compilación jenkins.
Creo que el problema es que el script de compilación en sí se ejecuta dentro de unentorno chroot de jenkins. Entonces, cuando se ejecuta el script unshare --mount ...
, falla con el error:
unshare: cannot change root filesystem propagation: Invalid argument
Lamentablemente, realmente no entiendo esta restricción ni cómo sortearla. Cuando intento hacer un chroot en la línea de comando, no puedo replicar este error. No sé qué ha hecho el complemento jenkins para causar esto.
Lo más importante es que estos puntos de montaje se eliminen al salir.cada vez sin falta.
Respuesta1
Según el comentario de AB, encontré una solución alternativa:
AB escribió:
Recibo el mismo error si hago chroot en la instalación del directorio rootfs de un lxc y
unshare --mount bash
. Si primero vinculo el montaje (--private) de este directorio en otro lugar y luego hago el chroot allí,unshare --mount
entonces funciona. No sé qué significa esto, pero espero que pueda ayudar a encontrar la causa o una solución (agregar un montaje de enlace en la tubería).
Basado en esto descubrí que esto no funciona:
unshare --mount bash -c 'echo hello'
Pero esto funciona:
mount --bind --make-private / /mnt
chroot /mnt unshare --mount bash -c 'echo hello'
umount /mnt
Respuesta2
La causa del problema es que unshare
intenta establecer los indicadores de propagación de montaje del directorio raíz, lo que sólo se puede hacer para los puntos de montaje. El directorio raíz del entorno chroot de Jenkins no es un punto de montaje.
Por ejemplo:
$ unshare -rm mount --make-rprivate /opt
mount: /opt: not mount point or bad option.
Una reproducción 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
Una solución alternativa sencilla que no realiza montajes fuera del espacio de nombres de montaje. Escapa del chroot para configurar la propagación del montaje y asume que el comando de montaje está disponible fuera del chroot:
unshare --propagation unchanged -m sh -c \
'nsenter --mount=/proc/self/ns/mnt mount --make-rslave /; echo Do some mounts'
O convierta el chroot en un pivot_root
entorno:
#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");
}