A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. Changes to the global resource are visible to other processes that are members of the namespace, but are invisible to other processes. One use of namespaces is to implement containers.
Namespaces are a feature of the Linux kernel that isolates and virtualizes system resources of a collection of processes. Examples of resources that can be virtualized include process IDs, hostnames, user IDs, network access, interprocess communication, and filesystems.
Linux provides the following namespaces:
Namespace Constant Isolates
Cgroup CLONE_NEWCGROUP Cgroup root directory
IPC CLONE_NEWIPC System V IPC, POSIX message queues
Network CLONE_NEWNET Network devices, stacks, ports, etc.
Mount CLONE_NEWNS Mount points
PID CLONE_NEWPID Process IDs
User CLONE_NEWUSER User and group IDs
UTS CLONE_NEWUTS Hostname and NIS domain name
As of kernel version 4.10, there are 7 kinds of namespaces. Namespace functionality is the same across all kinds: each process is associated with a namespace and can only see or use the resources associated with that namespace, and descendant namespaces where applicable. This way each process (or group thereof) can have a unique view on the resource. Which resource is isolated depends on the kind of namespace that has been created for a given process group.
##############################
Mount (mnt)
Mount namespaces control mount points. Upon creation the mounts from the current mount namespace are copied to the new namespace, but mount points created afterwards do not propagate between namespaces (using shared subtrees, it is possible to propagate mount points between namespaces).
The clone flag CLONE_NEWNS – short for “NEW NameSpace” – was used because the mount namespace kind was the first to be introduced. At the time nobody thought of other namespaces but the name has stuck for backwards compatibility.
##############################
Process ID (pid)
The PID namespace provides processes with an independent set of process IDs (PIDs) from other namespaces. PID namespaces are nested, meaning when a new process is created it will have a PID for each namespace from its current namespace up to the initial PID namespace. Hence the initial PID namespace is able to see all processes, albeit with different PIDs than other namespaces will see processes with.
The first process created in a PID namespace is assigned the process id number 1 and receives most of the same special treatment as the normal init process, most notably that orphaned processes within the namespace are attached to it. This also means that the termination of this PID 1 process will immediately terminate all processes in its PID namespace and any descendants.
##############################
Network (net)
Network namespaces virtualize the network stack. On creation a network namespace contains only a loopback interface.
Each network interface (physical or virtual) is present in exactly 1 namespace and can be moved between namespaces.
Each namespace will have a private set of IP addresses, its own routing table, socket listing, connection tracking table, firewall, and other network-related resources.
On its destruction, a network namespace will destroy any virtual interfaces within it and move any physical interfaces back to the initial network namespace.
##############################
Interprocess Communication (ipc)
IPC namespaces isolate processes from SysV style inter-process communication. This prevents processes in different IPC namespaces from using, for example, the SHM family of functions to establish a range of shared memory between the two processes. Instead each process will be able to use the same identifiers for a shared memory region and produce two such distinct regions.
##############################
UTS
UTS namespaces allow a single system to appear to have different host and domain names to different processes.
##############################
User ID (user)
User namespaces are a feature to provide both privilege isolation and user identification segregation across multiple sets of processes. With administrative assistance it is possible to build a container with seeming administrative rights without actually giving elevated privileges to user processes. Like the PID namespace, user namespaces are nested and each new user namespace is considered to be a child of the user namespace that created it.
A user namespace contains a mapping table converting user IDs from the container’s point of view to the system’s point of view. This allows, for example, the root user to have user id 0 in the container but is actually treated as user id 1,400,000 by the system for ownership checks. A similar table is used for group id mappings and ownership checks.
To facilitate privilege isolation of administrative actions, each namespace type is considered owned by a user namespace based on the active user namespace at the moment of creation. A user with administrative privileges in the appropriate user namespace will be allowed to perform administrative actions within that other namespace type. For example, if a process has administrative permission to change the IP address of a network interface, it may do so as long as its own user namespace is the same as (or ancestor of) the user namespace that owns the network namespace. Hence the initial user namespace has administrative control over all namespace types in the system.
##############################
Control Group (cgroup)
To prevent leaking the control group to which a process belongs, a new namespace type was suggested and created to hide the actual control group a process is a member of. A process in such a namespace, checking which control group any process is part of, would see a path that is actually relative to the control group set at creation time, hiding its true control group position and identity. The code creating this namespace was merged into kernel version 4.6.
##############################
The kernel assigns each process a symbolic link per namespace kind in /proc/<pid>/ns/. The inode number pointed to by this symlink is the same for each process in this namespace. This uniquely identifies each namespace by the inode number pointed to by one of its symlinks.
Reading the symlink via readlink returns a string containing the namespace kind name and the inode number of the namespace.
Syscalls
Three syscalls can directly manipulate namespaces:
clone, flags to specify which new namespace the new process should be migrated to.
unshare, flags to specify which new namespace the current process should be migrated to.
setns, enters the namespace specified by a file descriptor.
Destruction
If a namespace is no longer referenced, it will be deleted, the handling of the contained resource depends on the namespace kind. Namespaces can be referenced in three ways:
a process belonging to the namespace
an open filedescriptor to the namespace’s file (/proc/<pid>/ns/<ns-kind>)
a bind mount of the namespace’s file (/proc/<pid>/ns/<ns-kind>)
Namespaces API includes the following system calls:
clone(2)
The clone(2) system call creates a new process. If the flags argument of the call specifies one or more of the CLONE_NEW* flags listed below, then new namespaces are created for each flag, and the child process is made a member of those namespaces. (This system call also implements a number of features unrelated to namespaces.)
setns(2)
The setns(2) system call allows the calling process to join an existing namespace. The namespace to join is specified via a file descriptor that refers to one of the /proc/[pid]/ns files described below.
unshare(2)
The unshare(2) system call moves the calling process to a new namespace. If the flags argument of the call specifies one or more of the CLONE_NEW* flags listed below, then new namespaces are created for each flag, and the calling process is made a
member of those namespaces. (This system call also implements a number of features unrelated to namespaces.)
Creation of new namespaces using clone(2) and unshare(2) in most cases requires the CAP_SYS_ADMIN capability. User namespaces are the exception: since Linux 3.8, no privilege is required to create a user namespace.
Each process has a /proc/[pid]/ns/ subdirectory containing one entry for each namespace that supports being manipulated by setns(2):
root@NOC-RAFI:~# ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Aug 17 15:37 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Aug 17 15:37 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Aug 17 15:37 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Aug 17 15:37 net -> net:[4026531957]
lrwxrwxrwx 1 root root 0 Aug 17 15:37 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Aug 17 15:37 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 17 15:37 uts -> uts:[4026531838]
root@NOC-RAFI:~# ls -l /proc/$$/ns/cgroup
lrwxrwxrwx 1 root root 0 Aug 17 15:46 /proc/23633/ns/cgroup -> cgroup:[4026531835]
root@NOC-RAFI:~# readlink /proc/$$/ns/uts
uts:[4026531838]
Creating a new cgroup namespace. First, (as superuser) we create a child cgroup in the freezer hierarchy, and put the shell into that cgroup:
[root@localhost ~]# ls -l /sys/fs/cgroup/freezer/
total 0
-rw-r–r– 1 root root 0 Aug 17 16:01 cgroup.clone_children
–w–w–w- 1 root root 0 Aug 17 16:01 cgroup.event_control
-rw-r–r– 1 root root 0 Aug 17 16:01 cgroup.procs
-r–r–r– 1 root root 0 Aug 17 16:01 cgroup.sane_behavior
-rw-r–r– 1 root root 0 Aug 17 16:01 notify_on_release
-rw-r–r– 1 root root 0 Aug 17 16:01 release_agent
-rw-r–r– 1 root root 0 Aug 17 16:01 tasks
[root@localhost ~]# mkdir /sys/fs/cgroup/freezer/rafi
[root@localhost ~]# ls -l /sys/fs/cgroup/freezer/rafi/
total 0
-rw-r–r– 1 root root 0 Aug 17 16:27 cgroup.clone_children
–w–w–w- 1 root root 0 Aug 17 16:27 cgroup.event_control
-rw-r–r– 1 root root 0 Aug 17 16:28 cgroup.procs
-r–r–r– 1 root root 0 Aug 17 16:27 freezer.parent_freezing
-r–r–r– 1 root root 0 Aug 17 16:27 freezer.self_freezing
-rw-r–r– 1 root root 0 Aug 17 16:27 freezer.state
-rw-r–r– 1 root root 0 Aug 17 16:27 notify_on_release
-rw-r–r– 1 root root 0 Aug 17 16:27 tasks
[root@localhost ~]# echo $$
2065
[root@localhost ~]# sh -c ‘echo 2065 > /sys/fs/cgroup/freezer/rafi/cgroup.procs’
[root@localhost ~]# cat /proc/self/cgroup |grep freezer
4:freezer:/rafi
[root@localhost ~]# unshare -m bash
[root@localhost ~]# cat /proc/self/cgroup |grep freezer
4:freezer:/rafi
[root@localhost ~]# cat /proc/1/cgroup |grep freezer
4:freezer:/
###########################################################3
Mount namespaces
were the first namespace type added to Linux, appearing in 2002 in Linux 2.4.19. They isolate the list of mount points seen by the processes in a namespace. Or, to put things another way, each mount namespace has its own list of mount points, meaning that processes in different namespaces see and are able to manipulate different views of the single directory hierarchy.
A mount namespace is the set of filesystem mounts that are visible to a process. From clone man page : Every process lives in a mount namespace. The namespace of a process is the data (the set of mounts) describing the file hierarchy as seen by that process.
Docker uses these primarily to make the container look like it has its entire own filesystem namespace. If you’ve ever used a chroot jail, this is its tougher cousin. It looks a lot like a chroot jail but goes all the way down to the kernel so that even mount and unmount system calls are namespaced. If you use docker exec or nsenter to get into a container, you’ll see a filesystem rooted on “/ “. but we know this isn’t the actual root partition of the system. It’s the mount namespaces that make that possible.
Mount namespaces provide isolation of the list of mount points seen by the processes in each namespace instance. Thus, the processes in each of the mount namespace instances will see distinct single-directory hierarchies.
When the system is first booted, there is a single mount namespace, the so-called “initial namespace”. New mount namespaces are created by using the CLONE_NEWNS flag with either the clone() system call (to create a new child process in the new namespace) or the unshare() system call (to move the caller into the new namespace). When a new mount namespace is created, it receives a copy of the mount point list replicated from the namespace of the caller of clone() or unshare().
Following the clone() or unshare() call, mount points can be independently added and removed in each namespace (via mount() and umount()). Changes to the mount point list are (by default) visible only to processes in the mount namespace where the process resides; the changes are not visible in other mount namespaces.
Mount namespaces serve a variety of purposes. For example, they can be used to provide per-user views of the filesystem. Other uses include mounting a /proc filesystem for a new PID namespace without causing side effects for other process and chroot()-style isolation of a process to a portion of the single directory hierarchy. In some use cases, mount namespaces are combined with bind mounts.
