This post is about safely isolating individual processes in such a way that the only kind of network connection they can successfully establish is through Tor.
A network namespace is logically another copy of the network stack, with its own routes, firewall rules, and network devices. Network Namespaces are administrated with the
ip(8) command. Specifically the
netns “OBJECT”. Documentation is available through
man ip netns.
To create a new network namespace called
nstor we use the
† ip netns add nstor
We can list network namespaces with the
† ip netns list nstor
Notice that there is no default namespace listed, we will look into that later.
We can then execute commands in this new network namespace using the
exec command. Let’s spawn a shell just for demonstration purposes:
† ip netns exec nstor bash † ifconfig † ifconfig -a lo: flags=8<LOOPBACK> mtu 65536 loop txqueuelen 1 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
According to the documentation, this
lo interface is different from the real
lo interface. We can test this using
netcat. Using 2 terminal windows:
On the server side:
† nc -l 127.0.0.1 -p 8888
On the client side:
† nc 127.0.0.1 8888
And anything you type in one terminal window will appear in the other. This time execute the client
nc inside the new network namespace:
† ip netns exec nstor nc 127.0.0.1 8888
Nothing happens. They really are different.
This in and of itself is useful for cutting off internet connection to individual applications.
As an aside, remember that the
list command only returned our new namespace and not the default one:
† ip netns list nstor
But what if we are inside a network namespace and want to get back to the default?
ip-netns man page:
By default a process inherits its network namespace from its parent. Ini‐ tially all the processes share the same default network namespace from the init process.
Unfortunately it does not seem possible to use the
ip tool for this purpose:
ip netns exec automates handling of this configuration, file convention for network namespace unaware applications, by creating a mount namespace and bind mounting all of the per network namespace configure files into their traditional location in /etc.
However there is a different tool called
nsenter with a different interface. It uses the
setns system call with special files under
/proc/$PID/ns/net as an argument, allowing us to join the same network namespace as any other process. Remember that the default network namespace comes from
init which is pid 1:
† nsenter --help Usage: nsenter [options] <program> [<argument>...] Run a program with namespaces of other processes. Options: -t, --target <pid> target process to get namespaces from ... -n, --net[=<file>] enter network namespace ... For more details see nsenter(1). † nsenter -n -t1 † ifconfig wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.0.3 netmask 255.255.255.0 broadcast 192.168.0.255 ...
That’s neat but our goal is to run Tor so some link to the world outside the network namespace is required. For this we can use a Virtual Ethernet pair:
# server † ip link add vethtor0 type veth peer name vethtor1 † ip address add 10.0.1.1/24 dev vethtor0 † ip link set vethtor0 up † ip link set vethtor1 netns nstor † nc -l 10.0.1.2 -p 8888 # client † ip netns exec nstor bash † ip address add 10.0.1.2/24 dev vethtor1 † ip link set vethtor1 up † nc 10.0.1.1 8888
By moving one Virtual Ethernet interface into the network namespace and giving them both an IP address with the same network prefix (10.0.1) we have created a (very limited) connection to the outside world; programs running inside the nework namespace (10.0.1.2) will be able to connect to a program listening on 10.0.1.1 outside of the network namespace.
The final step is to simply run Tor, outside of the network namespace, and have it listen on 10.0.1.1. Any program running inside the network namespace will have no way of contacting the outside world except through Tor, and only if it is configured to connect through 10.0.1.1. This effectively stops any accidental† leaks such as through Flash or Java or some non-browser application you want to torify.
† I say accidental here because as we saw earlier, it is still possible for the root user to switch back to the default namespace. If you are looking for possibly stronger security then check out Firejail.
# Create a new Network Namespace called nstor † ip netns add nstor # Create a new Virtual Ethernet Pair called vethtor0 and vethtor1 † ip link add vethtor0 type veth peer name vethtor1 † ip address add 10.0.1.1/24 dev vethtor0 † ip link set vethtor0 up # Move vethtor1 interface to the nstor Network Namespace † ip link set vethtor1 netns nstor # Now that the vethtor1 interface is in a different Network Namespace, we need # to use these nested `ip` commands. † ip netns exec nstor ip address add 10.0.1.2/24 dev vethtor1 † ip netns exec nstor ip link set vethtor1 up
† cat > torrc << EOF User tor PIDFile /var/run/tor/tor.pid Log notice stderr DataDirectory /var/lib/tor/data SOCKSPort 10.0.1.1:9150 RunAsDaemon 1 EOF † tor -f torrc † ip netns exec bash † ifconfig vethtor1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.0.1.2 netmask 255.255.255.0 broadcast 0.0.0.0 ether ae:ea:31:a3:e1:39 txqueuelen 1000 (Ethernet) RX packets 90 bytes 74537 (72.7 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 89 bytes 7296 (7.1 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 † ping 18.104.22.168 connect: Network is unreachable † curl example.com curl: (6) Couldn't resolve host 'example.com' † curl --socks5-hostname 10.0.1.1:9150 example.com <!doctype html> <html> <head> <title>Example Domain</title> ...