How to isolate app‘s network activity using netns and virtual router

There are a few scenarios when the network isolation can be useful. First, it allows to listen single appications for their network activity. Second, applications can be routed in a specific way, that doesn’t affect the whole system. And third, not covered in this guide, network namespaces can be used to create isolated virtual networks.

Applications in a network namespace will see only their own set of network adapters. They will have their own routing tables as well. With this things, we can control the applications network activity with ease. For example, we can route some applications traffic via VPN, while the others will continue to use the default routing settings.

The basics

In order to setup the routing we need the following things:

  • Virtual router. It can be anything linux-based. I prefer virtualized pfsense.
  • A network namespace. It would be used to launch applications inside it.
  • A pair of virtual adapters. They would be used to connect the host with the network namespace. Remember, the network namespace is isolated by its own.
  • A routing table. It would be used to route namespace traffic in a different way.
  • A few ip/iptables rules. They would be used to make the things work together. They would be simple, I promise.

Virtual router / gateway

For the simplest case you can use basic linux machine with ip forwarding enabled. More complex solutions can include transparent VPN routing and some sort of fail-tolerance options. I found pfsense virtual machine useful for this kind of things. But basically, all you need is a gateway address. It would be used to route all the traffic from the network namespace.

Virtual namespaces

We have no network namespaces from the start:
ip netns ls

Let’s create a new namespace ns1:
ip netns add ns1

Virtual adapters

Now we need to connect the network namespace with the host. We can do this by using a pair of virtual adapters. All the data sent to one adapter would be transfered to another. Let’s create veth1a and veth1b pair:
ip link add veth1a type veth peer name veth1b

You can check both adapters with ip addr command. They are currently presented in the default network namespace.

Next we move veth1b adapter to the ns1 namespace:
ip link set veth1b netns ns1

For this moment “physical” links are set, but both interfaces have no IP addresses. Let’s assign one to each. We will use 192.168.60.0/24 subnet for this example:

Assign address to veth1a:
ip addr add 192.168.60.1/24 dev veth1a
ip link set veth1a up

Assign address to veth1b:
ip netns exec ns1 ip addr add 192.168.60.100/24 dev veth1b
ip netns exec ns1 ip link set veth1b up

(optional) We can also enable localhost interface for ns1:
ip netns exec ns1 ip link set lo up

Add default route for ns1 namespace, so all the traffic inside can be routed:
ip netns exec ns1 ip route add default via 192.168.60.1

At this point we can ping both adapters from the corresponding sides:
ping 192.168.60.100
ip netns exec ns1 ping 192.168.60.1

Routing table

In order to separate ns1 traffic from the global routing scheme we need an additional routing table. To create the new one:
echo "1 tns1" >> /etc/iproute2/rt_tables

Then we pass all the traffic from 192.168.60.0/24 subnet to it:
ip rule add from 192.168.60.0/24 table tns1

Set 192.168.56.5 as a default gateway for tns1:
ip route add default via 192.168.56.5 table tns1

Set 192.168.60.1 as a gateway for internal veth1 traffic:
ip route add 192.168.60.0/24 via 192.168.60.1 table tns1

Bringing the things together

The configuration part is done. The last two things we need is to enable linux routing between the interfaces (they are disabled by default). We also need a NAT server to forward veth1 traffic via the vboxnet0 interface. Luckly, it’s already included in a linux kernel and only a rule is required.

Enable ip forwarding between the interfaces, so veth1a interface traffic can be routed to vboxnet0 interface:
echo 1 > /proc/sys/net/ipv4/ip_forward

Enable masquerade. Basically it allows the hosts from 192.168.60.0/24 subnet to use vboxnet0 interface as gateway. It doesn’t say how the traffic should be routed (we did it a few steps before in a Routing table part), it just allows to transparently connect the subnet to another interface via NAT.
iptables -tnat -A POSTROUTING -s 192.168.60.0/24 -o vboxnet0 -j MASQUERADE

Launching applications

To launch an application in the ns1 namespace the following command is used:
ip netns exec ns1 <app>

For example, you can run a bash and to play with ip addr or ping inside:
ip netns exec ns1 bash
ip addr
ping 8.8.8.8

Namespace applications are launched as root by default. To avoid this, you can launch them as user via sudo:
ip netns exec sudo -u user firefox

(optional) You can add the line to visudo to allow regular users to use ip netns exec command without password.
user ALL=NOPASSWD: /usr/bin/ip netns exec ns1 sudo -u user *

… and then to launch an application from user account with sudo:
sudo ip netns exec ns1 sudo -u user firefox

Note though, that the inner sudo are used to restrict access to root account. It can be ommited if not required.

Inspecting traffic

Inspecting namespace traffic is simple. Just listen the veth1a interface:
tcpdump -i veth1a