Skip to content
LinuxDeep Dive Published Updated 6 min readViews unavailable

Linux Namespaces: The Kernel Primitive Behind Every Container

Understand Linux namespace types, ownership, lifetime and composition, then inspect containers without mistaking an isolated view for security.

Linux namespaces give a process a namespaced instance or view of selected kernel resources. Container runtimes compose namespaces with cgroups, capabilities, seccomp, LSM policy, mounts, devices, and a root filesystem. The kernel does have namespace objects; it simply has no single universal object whose security semantics are “a container.”

An isolated view is not automatically an authorization boundary. Each namespace type has ownership, privilege checks, sharing choices, and cross-namespace interactions.

Inspect namespace identities safely

/proc/<pid>/ns/ exposes magic links whose target includes namespace type and inode-like identifier:

ls -l /proc/self/ns
lsns
readlink /proc/1/ns/user

Two processes with the same link identifier for a type share that namespace. Access may be limited by procfs hidepid, ptrace checks, PID namespaces, and permissions. Record the observer’s namespace; “PID 1” can name a different process from inside a container.

Namespace lifetime is reference-counted

A namespace normally persists while a member process or open namespace file descriptor references it. Bind-mounting a /proc/<pid>/ns/* link can pin a namespace after its original process exits. Named network namespaces use this mechanism under /var/run/netns.

Audit open descriptors and bind mounts before assuming a namespace disappeared. Deleting a friendly name does not necessarily destroy an object that still has members or references.

PID namespaces virtualize process IDs

Processes have one PID at each level of nested PID namespaces. The first child created in a new PID namespace becomes PID 1 there; the unshare() caller itself does not move into a new PID namespace because its PID was already assigned. Utilities use --fork to create the child:

unshare --user --map-root-user --fork --pid --mount-proc sh

Run only in a disposable shell. Namespace PID 1 has special signal behavior and must reap orphaned children. If it exits, the kernel terminates remaining members and future forks into that PID namespace fail.

Mount namespaces need propagation policy

A mount namespace starts as a copy of the caller’s mount list, but mount propagation (shared, slave, private, unbindable) determines whether later mount/unmount events cross boundaries. A new mount namespace alone does not guarantee that mounts cannot propagate back to the host.

Inspect propagation:

findmnt -o TARGET,PROPAGATION
cat /proc/self/mountinfo

Container runtimes deliberately configure propagation and construct roots with bind mounts, overlay filesystems, pivot_root, or related mechanisms. chroot alone is neither a mount namespace nor a security boundary.

Network namespaces contain a network stack

A network namespace has interfaces, routes, sockets/port space, firewall state, and many /proc/sys/net settings. A new namespace initially has loopback down and no external connection. veth pairs, bridges, routes, NAT, and DNS configuration are separate setup steps.

Creating/moving links normally requires CAP_NET_ADMIN in the governing user namespace. Avoid raw ip netns experiments on a production host because a mistaken link or route operation can cut connectivity. Use a VM and an out-of-band console.

Namespaces do not encrypt traffic or authenticate peers. A veth connection to the host is connectivity, not a firewall policy.

User namespaces remap credentials

A user namespace maps namespace UIDs/GIDs to IDs in its parent. UID 0 inside may hold capabilities scoped to resources owned by that user namespace while mapping to an unprivileged host ID:

unshare --user --map-root-user sh -c 'id; cat /proc/self/uid_map; cat /proc/self/gid_map'

This can reduce host authority, but “rootless” is not invulnerable. Kernel attack surface remains, subordinate ID setup matters, and resources owned by the initial user namespace may reject the inner capability. Distribution policy can restrict unprivileged user-namespace creation.

Every non-user namespace is owned by a user namespace. Capability checks for administrative operations are evaluated in an owning/governing user namespace, not by the displayed numeric UID alone. Inspect NS_GET_USERNS through suitable tooling or trace the creating runtime’s user namespace and mappings.

Joining user and mount namespaces in the wrong order can change what credentials permit. Use runtime-supported exec/debug paths instead of improvising nsenter on sensitive workloads.

UTS and IPC isolate distinct resources

UTS namespaces isolate hostname and NIS domain name. They do not create DNS records or network separation. IPC namespaces isolate System V IPC objects and POSIX message queues; they do not isolate arbitrary shared files or every memory-sharing mechanism.

Joining an IPC namespace can expose semaphores, message queues, or shared-memory segments subject to normal permissions. Treat nsenter as privileged access, not observation-only tooling.

Cgroup namespaces virtualize the view

A cgroup namespace changes how cgroup paths appear through /proc/<pid>/cgroup and cgroup mounts relative to the namespace root. It does not create resource limits. The host cgroup hierarchy, delegated controllers, and limits remain authoritative.

This improves containerized tool behavior and hides some topology, but resource security still comes from cgroup configuration and delegation.

Time namespaces virtualize offsets

Time namespaces provide offsets for monotonic and boot-time clocks; wall-clock CLOCK_REALTIME is not virtualized by this mechanism. The first process remains associated with the old time namespace while children enter the new one, analogous to PID namespace creation behavior.

Changing offsets has strict lifecycle/capability rules. Test software with the timens_offsets proc interface only in a disposable user/time namespace; do not change the host clock to simulate it.

Containers choose what to share

A runtime can create new PID/mount/network/UTS/IPC/cgroup/user/time namespaces or intentionally join host/existing ones. Host networking, host PID mode, shared IPC, and privileged flags each remove specific separations; they are not performance-neutral labels.

The OCI runtime configuration records namespace types/paths. Audit the realized process through /proc because defaults and orchestration overrides vary by product/version.

setns, unshare, and clone differ

clone()/clone3() can create a child in new namespaces. unshare() moves or arranges the calling execution context for supported types, with PID/time effects applying to children. setns() joins a namespace referenced by a file descriptor, subject to type-specific permission and relationship rules.

nsenter is a diagnostic wrapper around joining namespaces; docker exec/OCI exec additionally asks the runtime to construct a managed process with the container’s credentials, root, cgroup, LSM label, seccomp policy, environment, and lifecycle. They are not synonymous.

Namespaces need complementary controls

Mount namespaces do not limit CPU/RAM; cgroup namespaces do not set limits; user namespaces do not filter syscalls; network namespaces do not create firewall policy; PID namespaces do not prevent kernel exploits. Combine:

  • cgroup v2 limits and accounting;
  • minimal capabilities and a reduced bounding set;
  • seccomp allowlists;
  • SELinux/AppArmor or another LSM;
  • read-only/nosuid/nodev mounts and controlled devices;
  • immutable images and verified runtime packages.

Avoid --privileged and arbitrary host bind mounts; both can negate multiple layers at once.

Build a forensic namespace matrix

For a target PID, record all namespace links, UID/GID maps, capabilities, cgroup, mount propagation/root, interfaces/routes, LSM label, seccomp state, and runtime configuration. Compare with a host control process and the intended policy.

Test forbidden visibility and forbidden operations, not only different output. The design is credible when every shared namespace is intentional, every new namespace has complementary enforcement, the runtime exec path preserves controls, and cleanup removes members, references, mounts, veth devices, and cgroups.

Related:

Sources:

Comments