What are network namespaces

Network Namespaces are a Linux feature,
that allows different processes to have different views of the network.

Aspects of networking that can be isolated between processes include:

Interfaces

Different processes can connect to addresses on different interfaces.

Routes

Since processes can see different addresses from different namespaces,
they also need different routes to connect to networks on those interfaces.

Firewall rules

Firewalls are mostly out of scope for this article,
but since firewall rules are dependant on the source or target interfaces,
you need different firewall rules in different network namespaces.

How do you manage network namespaces

When a network namespace is created with the unshare(2) or clone(2) system calls,
it is bound to the life of the current process,
so if your process exits,
the network namespace is removed.

This is ideal for sandboxing processes,
so that they have restricted access to the network,
as the network namespaces are automatically cleaned up
when the process they were isolating the network for
no longer exists.

A privileged process can make their network namespace persistent,
which is useful for if the network namespace needs to exist when there are no processes in it.

There is no standard daemon to start persistent network namespaces on-boot,
however, the PrivateNetwork= option to systemd service files can be used
to start a process in a network namespace on boot.

Uses of network namespaces

Network namespaces can be used to sandbox processes,
so that they can only connect through the interfaces provided.

You could have a machine with two network interfaces,
one which connects out to the internet,
the other connected to your internal network.
You could set up network namespaces
such that you can run the ssh server on your internal network,
and run a web server in a network namespace that has the internet-facing interface,
so if your web server is compromised,
it can't connect to your internal network.

docker and systemd-nspawn combine network namespaces
with other namespaces to provide containers.
docker is primarily interested in application containers,
while systemd-nspawn is more interested in system containers,
though both technologies can be used for either.

A more novel use of network namespaces is virtual routers,
as implemented in OpenStack Neutron.

Network namespaced routing

We previously mentioned that routes were a property of network namespaces,
that virtual ethernet devices have a pair of ends,
and that we can move a network interface into another network namespace.

By combining these features,
we can construct a series of virtual computers and experiment with routing.

Two node networking

To start with we're going to add another virtual computer,
start a simple echo server on it,
and configure the network such that we can connect to it from our host computer.

As we've done in previous articles,
we're going to make the left and right virtual ethernet pair.

$ sudo ip link add left type veth peer name right

But to demonstrate routing to another machine we need to create one,
so we're making another network namespace,
as we only care about its networking.

$ sudo ip netns add testns

We're going to move one half of the virtual ethernet device pair
into another namespace,
to simulate a physical link between the two virtual computers.

$ sudo ip link set dev right netns testns

We're going to say that we connect to interfaces in the testns namespace
by connecting to addresses in the 10.248.179.1/24 subnet.

This is because your host namespace does not understand how to reach that address,
since it is not an address in your network namespace,
nor is it an address on any of the subnets in the namespace,
and there are no defined rules for how to reach it.

So let's add a rule!

$ sudo ip route add 10.248.180.0/24 dev left via 10.248.179.2

This says that you can find the 10.248.180.0/24 subnet
by sending packets to the 10.248.179.2 address
through the left interface.

You should now be able to type messages into the following netcat command,
and see the result in your other terminal.

3 namespaces

This works fine,
but in the wider internet you need to connect through long chains of computers
before you reach your final destination,
so we're going to use 3 network namespaces to represent a longer chain.

We are going to have two extra network namespaces, called near and far.

$ sudo ip netns add near
$ sudo ip netns add far

We are going to create a chain of network interfaces called one, two, three and four.

To speak to these interfaces, we need to assign address ranges, so for our
host to near link we will use 10.248.1.0/24,
for our near to far link we will use 10.248.2.0/24,
and for our destination address we will use 10.248.3.1/24.

This says that if you're broadcasting from 10.248.1.1 and going to
anything in 10.248.1.0/24 then it goes to the one interface. However
this doesn't match because we want to go to the four interface which
has address 10.248.3.1.

We can make this route to our near interface by adding a new rule.

$ sudo ip route add 10.248.2.0/24 dev one via 10.248.1.2

We can prove this hop works by starting a server in the near namespace
(again, in a separate terminal).

$ sudo ip netns exec near nc -l 10.248.2.1 12345

We can try to talk to it:

$ nc -v 10.248.2.2 12345

This doesn't yet work, because it's a bidirectional protocol,
so the return route needs to work too.

Now you can reach the far namespace, but it's still not working.
This is because tcp is a bidirectional communication protocol,
and it doesn't know how to send the response to a message from 10.248.1.1,
so we need to add another rule.