linux namespaces

Understanding Linux Namespaces

In this article we will explain what Linux namespaces are and how do we take advantage of them in our software systems nowadays.

Let’s start!

What Is a Namespace?

A Linux namespace is a feature that Linux kernel provides to allow us to isolate resources for a set of processes. In some way, they are a sort of implementation of the bulkhead pattern we frequently see in microservices architectures.

What are the main advantages in the use of namespaces? The main advantages are two:

  • Isolation of resources
    One troublesome process won’t be taking down the whole host, it’ll only affect those processes belonging to a particular namespace.
  • Security
    The other advantage is that a security flaw in the process or processes running under a given namespace, won’t give access to the attacker to the whole system. Whatever he/she could do, will always be contained within the boundaries of that namespace! This is why it’s also very important to avoid running our processes using privileged users whenever possible.
Namespaces and Containers

Nowadays, containers make extensive use of Linux namespaces to be able to group processes and provide resource isolation for them. Some of the most popular and user-friendly container engines at the moment are Docker or Podman.

Namespaces (and therefore containers) are something unique to the Linux kernel. You might be wondering, how is possible that we can run Docker or Podman in our Mac (OSX) or Windows machines then?

Well, the answer is simple: Both Docker and Podman run a virtual machine in your local machine (OSx or Windows) to be able to make use of namespaces and any other kernel features required to run containers.

For example, we are running Podman in our local machine, in order to make it work we had to initialise and start a podman machine first by running the following:

$ podman machine init
$ podman machine start

This initialises and starts a virtual machine in your local machine, which runs a linux operating system. How can we get the details about this machine to make sure that it’s actually a virtual machine? We can get this information by running this command:

podman machine inspect

This will inspect the default machine we have currently running in podman. The output will be something similar to this:

[
     {
          "ConfigPath": {
               "Path": "/Users/theboreddev/.config/containers/podman/machine/qemu/podman-machine-default.json"
          },
          "ConnectionInfo": {
               "PodmanSocket": {
                    "Path": "/Users/theboreddev/.local/share/containers/podman/machine/podman-machine-default/podman.sock"
               }
          },
          "Created": "2023-02-12T07:50:02.100074Z",
          "Image": {
               "IgnitionFilePath": {
                    "Path": "/Users/theboreddev/.config/containers/podman/machine/qemu/podman-machine-default.ign"
               },
               "ImageStream": "testing",
               "ImagePath": {
                    "Path": "/Users/theboreddev/.local/share/containers/podman/machine/qemu/podman-machine-default_fedora-coreos-37.20230205.2.0-qemu.aarch64.qcow2"
               }
          },
          "LastUp": "2023-02-12T07:50:02.100074Z",
          "Name": "podman-machine-default",
          "Resources": {
               "CPUs": 1,
               "DiskSize": 100,
               "Memory": 2048
          },
          "SSHConfig": {
               "IdentityPath": "/Users/theboreddev/.ssh/podman-machine-default",
               "Port": 56324,
               "RemoteUsername": "xxxx"
          },
          "State": "running"
     }
]

You can see above how Podman uses QEMU to run a virtual machine, using an image which runs Fedora Core OS as the operating system. If you want to run a different Linux version, our article “Run Ubuntu on Max Using QEMU” might be useful.

linux namespaces - podman

The way it works in Docker is very similar to what Podman does, although Docker uses VirtualBox instead. This is the default virtual machine that comes with Docker Desktop, but there are other alternatives like colima for example.

Now that we know what a Linux namespace is, you could be thinking: How can I create a new namespace?

Let’s see how!

How to Use Namespaces

In order to create a namespace in Linux, we have to use the unshare command to run a process in a new namespace.

If we consult unshare command’s help section, the first we can see is something like this:

$ unshare --help
Usage:
 unshare [options] [<program> [<argument>...]]
Run a program with some namespaces unshared from the parent.
Options:
 -m, --mount[=<file>]      unshare mounts namespace
 -u, --uts[=<file>]        unshare UTS namespace (hostname etc)
 -i, --ipc[=<file>]        unshare System V IPC namespace
 -n, --net[=<file>]        unshare network namespace
 -p, --pid[=<file>]        unshare pid namespace
 -U, --user[=<file>]       unshare user namespace
 -C, --cgroup[=<file>]     unshare cgroup namespace
 -T, --time[=<file>]       unshare time namespace

You can see how most of the first few options allow us to specify different types of namespaces, these are the different types of namespaces we have in Linux systems. We will see cgroups in more detail in a future article, as it takes a very important role in the containers world.

Types of Namespaces

Each type of namespace is different and it provides isolation for different resources in our system.

If we check the namespaces in the Linux manual pages, we can see a list of namespace types:

       Namespace Flag            Page                  Isolates
       Cgroup    CLONE_NEWCGROUP cgroup_namespaces(7)  Cgroup root
                                                       directory
       IPC       CLONE_NEWIPC    ipc_namespaces(7)     System V IPC,
                                                       POSIX message
                                                       queues
       Network   CLONE_NEWNET    network_namespaces(7) Network
                                                       devices,
                                                       stacks, ports,
                                                       etc.
       Mount     CLONE_NEWNS     mount_namespaces(7)   Mount points
       PID       CLONE_NEWPID    pid_namespaces(7)     Process IDs
       Time      CLONE_NEWTIME   time_namespaces(7)    Boot and
                                                       monotonic
                                                       clocks
       User      CLONE_NEWUSER   user_namespaces(7)    User and group
                                                       IDs
       UTS       CLONE_NEWUTS    uts_namespaces(7)     Hostname and
                                                       NIS domain
                                                       name

We won’t get into too much detail, but you can clearly see how every namespace will isolate a different resource in our system. Therefore, we can see briefly what the different types of namespaces in a Linux system are:

  • User namespace
    Contains an independent set of user IDs and group IDs that can be assigned to processes.
  • PID namespace
    Contains its own set of process IDs (PIDs). Every time we create a new namespace, the process will get assigned PID 1. Every child process created in the new namespace will be assigned subsequent IDs.
  • Mount namespace
    They allow the management of mount points in our system. Doing unmount in our new namespace won’t have any effect on the main host, as every new mount will be private to the current namespace.
  • Network namespace
    Virtualises the network stack for the new namespace. This means that the new namespace will have its own virtual interface/s, private IPs, IP route table, sockets, etc.
  • IPC (Inter-Process Communication) namespace
    Allows defining shared memory segments between processes within a namespace for inter-process communications, not interfering with other namespaces.
  • UTS (Unix Time-Sharing) namespace
    Allows our system to have different host names and domain names for each namespace.
  • Time namespace
    This namespace was released quite recently in Linux (2020), it allows having different system times within our system by specifying different time namespaces.
  • CGroup (Control Groups) namespace
    Introduced in 2016 as part of Linux release 4.6, limits the resource usage in our system (cpu, memory, disk, etc) for a particular group of processes (under this namespace). As we will see in future articles, this is a very important feature of Linux Kernel in the containerised world we now live in!

Now that we have gone through all the existing types of namespaces in Linux, we’re going to show a simple example to understand the power that namespaces bring to us.

Let’s run the following command:

$ unshare --user --pid --map-root-user --mount-proc --fork bash 

What exactly are we doing here? What are we telling the Kernel to do? Let’s go step by step to try to understand what we are doing.

If we check each of these options in the manual here, we can see each of these options tell the Kernel to do the following:

  • –user: Create a new user namespace.
  • –pid: Create a new pid namespace. (Will fail if –fork is not specified)
  • –map-root-user: Wait to start the process until the current user (running unshare command) gets mapped to the superuser in the new namespace. This allows having root privileges within the namespace, but not outside of that scope.
  • –mount-proc: Mount /proc filesystem in the new namespace and create a new mount, this is to be able to have different processes with same process IDs in both namespaces.
  • –fork: Run command as a child process of the unshare command, instead of running it directly. It’s required when creating a new PID namespace.

Let’s see what this implies. First of all, if we list the processes in the new namespace, we only see the new processes running in the new namespace and not the processes running in the host’s default namespace:

$ ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/bash
    2 root      0:00 ps -ef

This is because we’ve used both --pid and --fork options.

You will also notice that our processes are running as root within the new namespace, this is what the --map--root-user does. The current user is root within this namespace only, this user won’t be able to do any harm to any of the external namespaces! This is great, right?

Lastly, we can see how the IDs of the processes are 1 and 2, this is because we create a new /proc mount for our namespace using --mount-proc.

Is that clear? We hope so!

It’s also worth mentioning that the unshare command we just ran is equivalent to the following command using a docker/podman client:

docker exec -it myImage /bin/bash

This will be very familiar to those used to work with containers, as this is normally used to gain access through a terminal inside the container. Now that we’ve mentioned containers, let’s see what the namespaces role is inside of Linux containers.

Namespaces Role in Containers

Namespaces have been available in the Linux kernel since 2002, however, the use of them was restricted to those with a very advanced knowledge of Linux systems. Linux namespaces and containers were made “popular” by Docker, when it made possible a wide adoption of containers across the sector for anyone with a minimum (or zero) understanding of Linux Kernel internals.

The use of namespaces is not transparent to the users, and that’s probably why most users don’t understand containers in they way they actually are.

There’s a considerable number of people in the sector that see containers as some sort of virtual machine that does not contain the kernel, but shares it with the host instead. This is a wrong understanding of what a container is, we’ll see why very soon.

linux namespaces - illusion
Photo by Randy Jacob on Unsplash

Containers create an illusion that could make you think that you’re actually running a virtual machine on your host, but this is far from the truth. When you run a virtual machine on your host , you are actually booting a new OS distribution on your machine, with the difference being that this OS will make calls to a middleware provided by the virtualisation engine you use (Virtual Box, QEMU, etc).

This middleware translates every kernel call inside your virtual machine to a system call that your host can understand. This is what mades possible, for example, to run different operating systems like Windows on a unix system!

linux namespace vs virtual machine
Image Credit: Author

On the other hand, a Linux container makes use of Linux namespaces to provide this illusion of running a different operating system. When we create an image based a any known Linux distribution, we are actually mounting its filesystem, giving us the impression of being on a new operating system. But actually we’re just inside of a new namespace!

linux namespaces - containers

Image Credit: Author

If you are interested in knowing more about Linux or UNIX systems, we recommend the following books:

Conclusion

In this article we have seen what Linux namespaces are and the very important role they play in distributed systems nowadays. We’ve also tried to clarify one of the biggest misconceptions around Linux containers with regards to Linux namespaces and virtual machines.

We really hope that every concept is now clearer to you. If you are still confused and this is unclear, don’t worry, this can happen. Just re-read these concepts and eventually you will get the point of Linux namespaces.

That’s all from us today! We really hope you’ve enjoyed reading this article as much as we enjoyed writing it!

Please follow us to be notified about new content like this one! Thanks for reading us!