Run OpenVPN in a Linux network namespace
2022-02-04
Users may wish to conceal their home IP for a variety of reasons. This is often done with a proxy such as Tor. Redirecting all traffic through a proxy is slow however and may not be necessary for all applications. Making only specific applications use a proxy can be done using Linux network namespaces.
This guide assumes you’re using OpenVPN, though any type of VPN should work.
Creating a new network namespace
A new network namespace can be created with the ip
utility:
$ ip netns add mynetns
To execute a program inside this namespace, use ip netns exec
$ ip netns exec mynetns ip l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
For ip
, a shorter syntax is:
$ ip -n mynetns l
To allow connections to the outside world a veth
device must be added.
$ ip l add mynetns_a type veth peer name mynetns_b
$ ip l
...
6: mynetns_b@mynetns_a: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 0a:3d:7c:b3:f9:a4 brd ff:ff:ff:ff:ff:ff
7: mynetns_a@mynetns_b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 0e:94:83:65:95:e8 brd ff:ff:ff:ff:ff:ff
We now need to move one of the interfaces into the proper namespace:
$ ip l set mynetns_a netns mynetns
$ ip -n mynetns l
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
7: mynetns_a@if6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 0e:94:83:65:95:e8 brd ff:ff:ff:ff:ff:ff link-netnsid 0
Give the interfaces an IP:
$ ip -n mynetns a add 192.168.0.2/24 dev mynetns_a
$ ip a add 192.168.0.1/24 dev mynetns_b
Bring the interfaces up:
$ ip -n mynetns l set up dev mynetns_a
$ ip l set up dev mynetns_b
You should now be able to send packets through the interface:
$ ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.040 ms
Forwarding packets
We now need to forward packets to the interface. Since we’re using IPv4 we’ll use NAT:
$ echo 1 > /proc/sys/net/ipv4/ip_foward
$ iptables -t filter -A FORWARD -i mynetns_b -j ACCEPT
$ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Configure the default route:
$ ip -n mynetns r add default via 192.168.0.1
To test if it works, try pinging a public IP:
$ ip netns exec ping demindiro.com
PING demindiro.com (104.244.79.104) 56(84) bytes of data.
64 bytes from mail.salt-inc.org (104.244.79.104): icmp_seq=1 ttl=62 time=0.165 ms
Running OpenVPN inside the namespace
Running any service inside the namespace is trivial:
$ cd /etc/openvpn
$ ip netns exec openvpn myvpn.conf
Service script
This run
script can be used with runit
:
#!/bin/sh
ip netns add mynetns
ip l add mynetns_a type veth peer name mynetns_b
ip l set mynetns_a netns mynetns
ip a add 192.168.0.1/24 dev mynetns_b
ip -n mynetns a add 192.168.0.2/24 dev mynetns_a
ip l set up mynetns_b
ip -n mynetns l set up mynetns_a
ip -n mynetns r add default via 192.168.0.1
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t filter -A FORWARD -i mynetns_b -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
cd /etc/openvpn
exec ip netns exec mynetns openvpn myvpn.conf
For cleanup, use this finish
script:
#!/bin/sh
ip netns del mynetns
echo 0 > /proc/sys/net/ipv4/ip_forward
iptables -t filter -D FORWARD -i mynetns_b -j ACCEPT
iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
In any service that should use the VPN, add this:
sv start myvpn || exit
# ...
exec ip netns exec mynetns myservice ...