Reading — step 1 of 5
Read
Mount Namespace
The second lie: what the filesystem looks like. A container sees an Ubuntu or Alpine root at / — while the host's real / is untouched and invisible. The mount namespace is how one kernel serves completely different filesystem views to different processes, and it's the oldest namespace in Linux (2002 — predating containers by a decade).
Mounts are a table, and the table can be private
Unix builds its single directory tree by mounting filesystems onto directories: / from one disk, /proc from the kernel, a USB stick at /media/usb. The kernel tracks these grafts in a mount table. A mount namespace (CLONE_NEWNS) gives a process group a private copy of that table: mounts and unmounts inside it edit the copy, not the original.
host mount table: container's table (a namespace copy):
/ ← disk / ← overlay of image layers
/proc ← procfs /proc ← a FRESH procfs (its own PIDs!)
/home ← disk2 /tmp ← tmpfs
Note /proc: last lesson promised this connection. Mounting a new procfs inside the container's mount+PID namespaces is what makes ps show the container's processes — the two namespaces cooperating to keep the story consistent. Container setup is largely this: enter fresh namespaces, then build a mount table that matches the lie you're telling.
chroot vs pivot_root: changing the floor
Giving the container its own / needs more than mounting — the process's idea of "root directory" must move into the image filesystem. The old tool is chroot ("pretend this directory is /") — and it's famously escapable: the real root is still in the mount table underneath, and privileged processes have documented tricks to climb back out. Real container runtimes use pivot_root: swap the mount table's root — new rootfs becomes /, the old root is moved to a subdirectory, and then — the crucial move — the old root is unmounted entirely. Nothing to escape to: the host filesystem isn't hidden from the container, it's absent from the container's table. That distinction — hidden vs absent — is the same security philosophy as PID namespaces, applied to files.
Bind mounts: deliberate holes in the wall
Total isolation is the default; sharing is opt-in. A bind mount grafts an existing directory to a second location — and across namespaces it's how docker run -v /host/data:/data works: the host directory appears in the container's mount table by explicit invitation. (Docker "volumes" are the same mechanism pointed at Docker-managed directories.) The security model reads directly off the table: a container can touch exactly the host paths that were bind-mounted in — the rest isn't locked, it's unlisted.
The kernel also tracks propagation between related mount tables (whether a new mount in one namespace echoes into another — private vs shared). File the term; it's the answer to advanced puzzles like "why did my new host mount not appear in the running container."
Your exercise: Mount Namespace Simulator
Implement the table semantics: namespaces cloning the parent's table at creation, mounts/unmounts applying only to the owner's copy, bind mounts linking a source path into another table, and path resolution answering "what does this process see at /x/y?" from its own table. The behaviors that separate right from almost-right: changes after the clone must NOT leak between tables (copy, not reference — the entire point of the namespace), and resolution must use the deepest matching mount (mounting over /data shadows what was under it, exactly like the real kernel). Nail those and df output in a container will read like your own data structure.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…