Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2. Namespaces

A namespace changes what a process can see. It does not limit how much it can use — that is control groups, the next chapter.

What a namespace is

Normally every process sees the same machine: the same list of processes, the same files, the same network, the same host name. A namespace gives a group of processes their own copy of one of those things. Inside a new PID namespace, for example, a process sees only its own children and thinks it is pid 1. Nothing is copied and nothing is emulated; the kernel just keeps a separate table and answers from it. Linux has eight kinds, and each one can be used alone or with the others. A “container” is simply a process that has its own copy of most of them.

            the host sees                         the sandbox sees
 ┌──────────────────────────────────┐     ┌─────────────────────────────┐
 │ pids   1 systemd … 4200 zygo     │     │ pids   1 python   2 sh      │
 │        4201 python  4202 sh      │     │                             │
 │ files  /home /etc /var …         │     │ files  / of the image only  │
 │ net    eth0 wlan0 lo  + LAN      │     │ net    lo                   │
 │ name   my-laptop                 │     │ name   sandbox              │
 │ users  0 root … 1000 you         │     │ users  0 root  (= you, 1000)│
 └──────────────────────────────────┘     └─────────────────────────────┘
        same processes 4201 and 4202 — two different views of them
NamespaceIts own copy ofSince
mountthe list of mounted file systems2.4.19 (2002)
UTShost name2.6.19 (2006)
IPCshared memory, message queues2.6.19
PIDprocess numbers2.6.24
networkinterfaces, addresses, routes, firewall2.6.29
useruser and group numbers3.8
cgroupthe view of the cgroup tree4.6
timeboot-time clocks5.6

Mount namespace

The mount namespace gives a process its own list of mounted file systems. It can mount and unmount things, and the rest of the machine does not see it. This was the first namespace, added in 2002, and it is why the flag is still called just CLONE_NEWNS — nobody expected others. A sandbox uses it to build a whole new file tree from an image, then switch its root to that tree, so the host’s files are simply not there. Chapter 4 explains how the switch is done.

PID namespace

The PID namespace gives a process its own process numbers. The first process inside becomes pid 1, and it can only see and signal processes in the same namespace, or in namespaces below it. From outside, the same processes are still visible, with their normal host numbers. So a sandboxed program cannot list, watch or kill anything on the host. When pid 1 of the namespace exits, the kernel kills everything else inside it, which makes clean-up easy.

Network namespace

The network namespace gives a process its own network: its own interfaces, its own addresses, its own routes and its own firewall rules. A new one has only a loopback interface (lo), so by default the process can reach nothing but itself. To give it more, something outside has to connect it — a virtual cable to the host, or a program that moves packets in user space. Chapter 4 covers both, and Zygo’s choice.

UTS namespace

UTS is an old name for “the system’s name”. This namespace gives a process its own host name and domain name. It is the smallest of the eight and hides no secret; it mostly stops a program from learning the host’s name, or changing it. Container tools set it so that hostname inside prints something sensible.

IPC namespace

IPC means “inter-process communication”. This namespace gives a process its own System V message queues, shared memory segments and POSIX message queues. Without it, two programs on one machine could meet through a shared memory segment with a guessable key. It is old and rarely thought about, but a sandbox that skips it leaves a small side door open.

User namespace

The user namespace gives a process its own list of users. Inside it, a process can be uid 0 — root — while outside it is still your own normal uid. A uid map (/proc/<pid>/uid_map) says which inside number matches which outside number. Root inside a user namespace has full capabilities, but only over things that belong to that namespace: its own mounts, its own network, never the host’s. This is the namespace that lets a normal user create all the others, and it is what makes rootless containers — and all of Zygo — possible.

  inside the sandbox          uid_map            on the host
  ──────────────────     ─────────────────     ──────────────
  uid 0    "root"   ───▶   0     1000   1  ───▶  uid 1000 (you)
  uid 1000 "app"    ───▶   (a second line, via newuidmap, if the host allows)
  any other uid     ───▶   not mapped: shows as "nobody", owns nothing

  root inside  = every capability over the sandbox's own mounts, network, …
               = no power at all over anything that belongs to the host

Cgroup namespace

The cgroup namespace changes how a process sees the control-group tree from chapter 3. Inside it, the process’s own group looks like the top of the tree, so it cannot learn where it sits on the host. It hides information; it does not set any limit. Zygo creates one, and in addition does not mount the cgroup file system inside the sandbox at all.

Time namespace

The newest one, added in Linux 5.6. It lets a process see a different value for the clocks that count time since boot. Its main use is moving a running container to another machine without its clocks jumping. Sandboxes for short jobs rarely need it, and Zygo does not create one.

Creating and joining

Three syscalls do the work. clone (and the newer clone3) starts a child in new namespaces. unshare moves the calling process into new namespaces. setns joins a namespace that already exists, given a file that points to it, such as /proc/<pid>/ns/net. Zygo uses clone3 to build a sandbox, because it makes the child pid 1 of its new PID namespace in one step. It uses setns on the warm path, to put a new process into a sandbox that is already built.

  clone3(flags)            unshare(flags)            setns(fd)
  ─────────────            ──────────────            ─────────
  parent                   process                   process      existing
    │                        │                         │          namespace
    └─▶ child, born         moves itself into         └──────────▶ ┌─────┐
        inside NEW          NEW namespaces                         │ net │
        namespaces                                                 └─────┘
  used by Zygo to          used by `unshare(1)`      used by Zygo to enter
  BUILD a sandbox          and many tools            a WARM sandbox

What namespaces do not do

Namespaces hide things; they do not count or limit anything. A process in its own namespaces can still use all the memory, fill the process table with a fork bomb, or keep every CPU busy. Namespaces also do nothing about which syscalls a process may call, so the whole kernel is still in reach. Those gaps are filled by control groups (chapter 3) and by seccomp and Landlock (chapter 4). You need all of them together; any one alone is not a sandbox.

Seeing them yourself

lsns lists the namespaces on the machine. ls -l /proc/self/ns shows the ones your shell is in, each as a number. unshare --user --map-root-user --pid --fork --mount-proc bash starts a shell where you are root and pid 1 — without being root on the host. Type ps inside it and you will see two processes. That small command is most of the idea behind every tool in this book.