zncb.

code:dsp:infosec:sounds | ams:txl:nrt:yul

Notes on Linux namespaces

Having a look at the state of Linux namespaces from a system administration perspective, I found myself confused by incomplete or misleading examples. So here’s a breakdown of what worked for me as a test setup on Debian Sid.

The objective is simple: run a bash shell in a pid namespace with it’s own network namespace, bridged with the host’s network.

Running a shell in a new pid namespace

This is simple enough, use ‘unshare’ like this:

1
2
3
# unshare -fp --mount-proc
# pstree -p
bash(1)───pstree(114)

Adding a network namespace

In order to isolate the networking too, we can use ‘unshare’ with a new network namespace via the ‘ip’ command:

1
2
# ip netns add foo
# ip netns exec foo unshare -fp --mount-proc

You should now have an isolated bash process, with it’s own network stack:

1
2
3
4
5
# pstree -p
bash(1)───pstree(114)
# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

Bridging the network

Network interfaces can only live in one namespace, in order to communicate between namespaces we can use veth device pairs which provide a sort of pipe behavior.

We can create a veth pair like this:

1
# ip link add veth-a type veth peer name veth-b

On my Debian Sid system this didn’t create ‘veth-b’, and subsequent commands failed with ‘Cannot find device “veth-b”’. So I had to issue the reciprocal command:

1
# ip link add veth-b type veth peer name veth-a

Now, we’ll assign one end of the veth pair to the ‘foo’ namespace:

1
# ip link set veth-b name eth0 netns foo

It should now appear under the ‘eth0’ name from inside our isolated bash shell. We can assign it an appropriate IP address normally with ‘ip addr add dev eth0’, and set a default route ‘ip route add default via dev eth0’.

On the host’s side, we now want to bridge ‘veth-a’ with ‘eth0’ in order for our little container to access the network.

Warning: this might break your host’s network temporarily.

1
2
3
# ip link add br0 type bridge
# ip link set eth0 master br0
# ip link set veth-a master br0

Make sure they are all in state UP, and we should be able to ping the outside world from our isolated namespace. Now, the hosts was probably setup to use ‘eth0’, and as it is now part of the bridge it will not work anymore. To get an equivalent setup, we’ll need to transfer the IPs assigned to ‘eth0’, to ‘br0’, and update our routing table. (I’m assuming a very simple setup here, e.g.: my laptop connected via ethernet with a static ip)

1
2
3
4
# ip addr del <ip> dev eth0
# ip add add <ip> dev br0
# ip route del default
# ip route add default via <gw_ip> dev br0

Now, your host’s networking should be back on track (actually, I had to bring interfaces down and back up for it work), and you can ping your host and isolated namespace transparently using their respective ip addresses. A thing of beauty.

Of course you probably don’t want to start setting up systems manually like this, but doing so does help me grasp a bit better how LXC, Docker and the like are working.