Sandboxed network interfaces using (unprivileged) unshare

networking
namespaces
linux
Author

Emmanuel Jeandel

Published

January 25, 2023

Update 2025-11-03 Some commands written in 2023 did not work on my computer in 2025. I slightly changed the text so that everything work again.

Unshare is a linux command that is used to execute commands (and shells) in new namespaces. This can be useful to isolate this command from the main namespaces. Among others, namespaces you can isolate are the PID namespace, the user namespace, and the network namespace.

What is especially nice is that you can change the network namespace without being root!

The command to do that is:

unshare -rn

This executes a new shell by default.

As ifconfig or ip addr will show, this new shell will live in a new namespace that doesn’t contain any of your usual network interfaces. It contains the interface lo, but be aware that the interface is down by default.

You will notice that you are root in the new shell, so you can in particular play with the network interfaces.

What can you do with this network namespace that contains no interface ? One use might be to execute commands that you don’t trust.

In this first post, I will explain how you can use these network namespaces to simulate a network of 2 machines:

In practice, you will have two shells opened (possibly in two terminals), one corresponding to each machine. The filesystem of both machines is still your nominal filesystem, which is usually useful for experiments.

Creating the namespaces

Here is how to do it, in a shell, first write the command

unshare -r

So that you are “root” in the shell.

From this shell, open two new shells, and in each of them write the command

unshare -n

What we have done this way is create three namespaces:

  • A user namespace, which makes you root
  • Two network namespaces, inside the user namespace

This means you now have two network namespaces, and the same user is root on both.

Creating the namespaces (alternative)

If you’re on a linux system where you don’t really know how to launch a terminal from inside another terminal (because you’re using a desktop environment), there is another way to obtain the same result

On a first shell, write

unshare -rn
echo $$

On the second shell:

nsenter --preserve-credentials --user -t PID
unshare -n

where PID is the PID of the first shell

Creating the interfaces

The two namespaces are anonymous. To reference an anonymous namespace, you can reference it by the PID of one application in that namespace. Therefore we will execute the command

echo $$

in both shells to know the PID of the bash process (or your favorite shell, if it is not bash)

In the following, FIRST_PID is the PID of the first bash process, and SECOND_PID the PID of the second bash process.

Now in the first shell, type

ip link add veth00 netns FIRST_PID type veth peer veth11 netns SECOND_PID

This will create an interface veth00 in the first namespace (the first shell) that is linked to the interface veth11 in the second namespace.

Now you can add IP to these interfaces, put them up, and do some networking!

firstshell# ip addr add 10.42.0.2/24 dev veth00
firstshell# ip link set veth00 up

secondshell# ip addr add 10.42.0.3/24 dev veth11
secondshell# ip link set veth11 up

And that’s it! You can test everything is working as it should by pinging the machines. You can also try to see the traffic using wireshark, remember to launch wireshark from inside one of the two shells, and to use the interfaces veth00 and veth11.

A script

The following script can be used to automate everything. It supposes that you have tmux on your PC, as well as x-terminal-emulator:

#!/usr/bin/bash

tmux  new-session -d -s master -n 1 "unshare -rn bash"
PIDMASTER=$(tmux list-panes -t master:0 -F '#{pane_active} #{pane_pid}' | cut -f 2  -d " ")

tmux  new-session -d -s reseaux0 -n 1 "nsenter --preserve-credentials --user -t $PIDMASTER unshare -n"
tmux  new-session -d -s reseaux1 -n 1 "nsenter --preserve-credentials --user -t $PIDMASTER unshare -n"

PID0=$(tmux list-panes -t reseaux0:0 -F '#{pane_active} #{pane_pid}' | cut -f 2  -d " ")
PID1=$(tmux list-panes -t reseaux1:0 -F '#{pane_active} #{pane_pid}' | cut -f 2  -d " ")

nsenter --preserve-credentials --user -n -t $PID0 sh -c "ip  link add veth00 netns $PID0 type veth peer veth11 netns $PID1"

nsenter --preserve-credentials --user -n -t $PID0 sh -c "ip addr add 10.42.0.40/24 dev veth00" 
nsenter --preserve-credentials --user -n -t $PID0 sh -c "ip link set veth00 up" 
nsenter --preserve-credentials --user -n -t $PID0 sh -c "ip link set lo up" 

nsenter --preserve-credentials --user -n -t $PID1 sh -c "ip addr add 10.42.0.50/24 dev veth11" 
nsenter --preserve-credentials --user -n -t $PID1 sh -c "ip link set veth11 up" 
nsenter --preserve-credentials --user -n -t $PID1 sh -c "ip link set lo up" 

tmux send-keys -t reseaux0:0  PS1=\'pc0:'\w\$'\' Enter
tmux send-keys -t reseaux1:0  PS1=\'pc1:'\w\$'\' Enter

tmux send-keys -t reseaux0:0 C-l
tmux send-keys -t reseaux1:0 C-l

x-terminal-emulator -e "tmux attach -t reseaux0"&
x-terminal-emulator -e "tmux attach -t reseaux1"&
x-terminal-emulator -e "tmux attach -t master"&

Remarks

  • If you try to ping 10.42.0.2 from the firstshell, this will not work, because this implicitely uses the lo interface that is down. If you want to be able to ping yourself, you can do ip link set lo up.

  • The interface is only visible in the shell if they share the same namespace. When the previous command is typed, veth00 is visible in the first shell, but veth11 is visible only in the second shell.

  • It won’t surprise anyone that most of these features are very useful to make containers. The important thing here is that we can do this in user mode. This does not provide a real sandboxing, but it is very useful if you just want to test some small networking scripts as a user which is not root.