Entering a mount namespace before setting up a
chroot, lets you avoid cluttering the host namespace with additional mounts, e.g. for
/proc. You can use
chroot inside a mount namespace as a nice and simple hack.
I think there are advantages to understanding
pivot_root, but it has a bit of a learning curve. The documentation does not quite explain everything... although there is a usage example in
man 8 pivot_root (for the shell command).
man 2 pivot_root (for the system call) might be clearer if it did the same, and included an example C program.
How to use pivot_root
Immediately after entering the mount namespace, you also need
mount --make-rslave / or equivalent. Otherwise, all your mount changes propagate to the mounts in the original namespace, including the
pivot_root. You don't want that :).
If you used the
unshare --mount command, note it is documented to apply
mount --make-rprivate by default. AFAICS this is a bad default and you don't want this in production code. E.g. at this point, it would stop
eject from working on a mounted DVD or USB in the host namespace. The DVD or USB would remain mounted inside the private mount tree, and the kernel would not let you eject the DVD.
Once you've done that, you can mount e.g. the
/proc directory you will be using. The same way you would for
Unlike when you use
pivot_root requires that your new root filesystem is a mount point. If it is not one already, you can satisfy this by simply applying a bind mount:
mount --rbind new_root new_root.
pivot_root - and then
umount the old root filesystem, with the
MNT_DETACH option. (You don't need
umount -R, which can take longer.).
pivot_root generally needs to involve using
chroot as well; it's not "either-or".
man 2 pivot_root, it's only defined as swapping the root of the mount namespace. It isn't defined to change which physical directory the process root is pointing to. Or the current working directory (
/proc/self/cwd). It happens that it does do so, but this is a hack to handle kernel threads. The manpage says that could change in future.
Usually you want this sequence:
chdir(new_root); // cd new_root
pivot_root(".", put_old); // pivot_root . put_old
chroot("."); // chroot .
The postition of the
chroot in this sequence is yet another subtle detail. Although the point of
pivot_root is to rearrange the mount namespace, the kernel code seems to find the root filesystem to move by looking at the per-process root, which is what
Why to use pivot_root
In principle, it makes sense to use
pivot_root for security and isolation. I like to think about the theory of capability-based security. You pass in a list of the specific resources needed, and the process can access no other resources. In this case we are talking about the filesystems passed in to a mount namespace. This idea applies generally to the Linux "namespaces" feature, though I'm probably not expressing it very well.
chroot only sets the process root, but the process still refers to the full mount namespace. If a process retains the privilege to perform
chroot, then it can traverse back up the filesystem namespace. As detailed in
man 2 chroot, "the superuser can escape from a 'chroot jail' by...".
Another thought-provoking way to undo
nsenter --mount=/proc/self/ns/mnt. This is perhaps a stronger argument for the principle.
setns() necessarily re-loads the process root, from the root of the mount namespace... although the fact that this works when the two refer to different physical directories, might be considered a kernel bug. (Technical note: there could be multiple filesystems mounted on top of each other at the root;
setns() uses the top, most recently mounted one).
This illustrates one advantage of combining a mount namespace with a "PID namespace". Being inside a PID namespace would prevent you from entering the mount namespace of an unconfined process. It also prevents you entering the root of an unconfined process (
/proc/$PID/root). And of course a PID namespace also prevents you from killing any process which is outside it :-).