Linux Namespaces

Last update: 2021-11-21 04:00:04 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

Namespaces partition names and resource views that would otherwise be global. Each task has one namespace instance of each supported type, and tasks in the same instance observe the same instance-specific resources. Namespaces do not, by themselves, constrain CPU, memory, or I/O consumption; cgroups provide those resource controls.

Linux exposes eight namespace types. Time namespaces were added in Linux 5.6; the other types predate that release. The namespace flags in include/uapi/linux/sched.h are the version-specific source of truth.

TypeCreation flagIsolated resource
CgroupCLONE_NEWCGROUPCgroup-root view
IPCCLONE_NEWIPCSystem V IPC objects and POSIX message queues
NetworkCLONE_NEWNETNetwork devices, protocol stacks, routing tables, firewall state, and port space
MountCLONE_NEWNSMount table and mount-propagation relationships
PIDCLONE_NEWPIDProcess-ID number space and PID 1 semantics
TimeCLONE_NEWTIMEOffsets for monotonic and boot-time clocks
UserCLONE_NEWUSERUser and group ID mappings, credentials, and scoped capabilities
UTSCLONE_NEWUTSHostname and NIS domain name

The initial system starts with one instance of each type. Namespace identity is not a portable, globally meaningful 32-bit ID. Instead, /proc/PID/ns exposes namespace handles as symbolic links such as net:[4026531840]; the number identifies that kernel object’s inode in the namespace filesystem.

sh
ls -l /proc/self/ns
lsns
readlink /proc/self/ns/net

Creating and Joining Namespaces #

clone(2) and clone3(2) create a child in new namespaces when passed one or more CLONE_NEW* flags. unshare(2) makes the calling task use newly created namespaces, subject to each namespace type’s rules. PID and time namespace unsharing instead affects subsequently created children; the caller remains in its current PID or time namespace. setns(2) joins a namespace referred to by an open file descriptor. The unshare(1) and nsenter(1) tools are convenient front ends for experiments and administration.

sh
# New user, mount, and PID namespaces; the child becomes PID 1 in its PID namespace.
unshare --user --map-root-user --mount --pid --fork --mount-proc sh

# Join the mount namespace of an existing process.
sudo nsenter --target "$PID" --mount sh

Namespace operations are not interchangeable:

  • A new PID namespace affects subsequently created children. A task cannot move itself into another PID namespace; setns() into one makes future children visible there.
  • A new mount namespace initially copies the caller’s mount table. Shared mount propagation can still transfer mounts across namespaces, so container setup commonly makes the relevant tree private or slave before mounting filesystems.
  • Network namespaces begin with a loopback device that must be brought up. Moving a veth endpoint into the namespace is a common way to connect it.
  • User-namespace creation has credential and mapping rules that differ from the other namespace types. Do not assume that a successful setns() grants host privileges.

The kernel checks capabilities in the relevant user namespaces and rejects unsafe transitions. Callers should use the syscall manual pages and the target kernel source for exact error conditions because they vary by namespace type and kernel release.

Handles and Lifetime #

/proc/PID/ns/<type> can be opened as a namespace file descriptor. The namespace remains alive while tasks or namespace file descriptors refer to it. A bind mount of such an entry also holds a reference, which lets a supervisor retain a namespace after its last process exits:

sh
sudo mkdir -p /run/netns/demo
sudo touch /run/netns/demo
sudo mount --bind /proc/"$PID"/ns/net /run/netns/demo
ip netns exec demo ip link

The bind mount must be unmounted when no longer needed. Treat namespace handles as authority-bearing objects: access to /proc/PID/ns is subject to /proc visibility and ptrace-style access checks, and an inherited descriptor can allow the recipient to call setns().

User Namespaces and ID Mappings #

A user namespace maps a contiguous range of IDs inside the namespace to IDs in its parent user namespace. The creator can often obtain a namespace-local effective UID 0 with --map-root-user, but that identity has no automatic privilege over unmapped host IDs or global host resources.

For an unprivileged setup, a supervisor commonly writes uid_map, then writes deny to setgroups, and then writes gid_map. The ordering prevents a process from gaining group-based access through an unmapped group transition. Mapping rules are intentionally restrictive; delegated ranges and the newuidmap and newgidmap helpers handle mappings the caller cannot write directly.

Capabilities are evaluated relative to a user namespace. A process with CAP_SYS_ADMIN in a child user namespace can perform operations governed by that namespace, such as configuring its mount namespace, but it lacks that capability in the initial user namespace. Kernel objects owned by an ancestor namespace, device access, and LSM policy still form security boundaries.

Composition for Containers #

A container is a composition of isolation mechanisms rather than one namespace. One practical setup order is:

  1. Create a user namespace and install deliberate UID/GID mappings, because it supplies namespace-scoped capabilities for later setup.
  2. Create a mount namespace, change propagation deliberately, and construct the filesystem view.
  3. Create a PID namespace, fork the intended init process, and mount a new /proc from the mount namespace so process reporting matches the PID view.
  4. Create or join network, IPC, UTS, time, and cgroup namespaces as required; configure network links from a privileged host-side manager.
  5. Place the workload in a host-managed cgroup before execution and retain namespace file descriptors for inspection and teardown.

PID 1 deserves explicit handling because it reaps orphaned children and has special signal behavior. A minimal container command that merely creates a PID namespace can therefore leave zombies or ignore termination signals; a runtime needs an init/reaper or equivalent supervision.

Mount namespaces control filesystem mount visibility, whereas chroot(2) only changes pathname resolution. Combining both with careful mount propagation is necessary to avoid exposing or receiving unintended mounts.

Cgroups and Cgroup Namespaces #

A cgroup namespace virtualizes the path to a task’s cgroup root. It changes what the task reads from /proc/PID/cgroup and from a cgroup filesystem mounted in that namespace, which prevents an inner workload from discovering ancestor paths. It does not create an independent cgroup hierarchy and does not weaken host CPU, memory, I/O, or PID limits.

For a complete namespace ABI description, consult the relevant namespaces(7) manual page alongside the target release’s kernel source.

Comments

Read or comment on GitHub (sign-in required to comment)