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 chroot
.
Unlike when you use chroot
, 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
.
Use pivot_root
- and then umount
the old root filesystem, with the -l
/ MNT_DETACH
option. (You don't need umount -R
, which can take longer.).
Technically, using pivot_root
generally needs to involve using chroot
as well; it's not "either-or".
As per 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 chroot
sets.
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 chroot
is nsenter --mount=/proc/self/ns/mnt
. This is perhaps a stronger argument for the principle. nsenter
/ 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 :-).
pivot_root
andchroot
: I took a look at the Docker sources and found that if it fails executingpivot_root
, it falls back tochroot
, i.e. these mechanisms are considered to be at least similar in functionality for containerization purposes.