OpenVPN for a single application on Linux

Software howto, openvpn, privacy, security, vpn

It’s sometimes useful to run a single application through a VPN for privacy reasons. There are many ways to do it, and the most common one is apparently to run the application with a different user account and use some iptables magic to configure a specific routing table for this user. In my opinion this is way too troublesome and complicated, and much less comfortable to use. Luckily there’s another way to do that on modern Linux systemd: network namespaces.

Here’s how to run OpenVPN and a single other application in a separate namespace:

  1. Get an account with an OpenVPN provider; FrootVPN is free (at the moment) and works great.

  2. Create the net network namespace:

    # ip netns add frootvpn
    
  3. Start the loopback interface in the namespace (otherwise many things don’t work as expected…)

    # ip netns exec frootvpn ip addr add 127.0.0.1/8 dev lo
    # ip netns exec frootvpn ip link set lo up
    
  4. Create virtual network interfaces that will let OpenVPN (in the namespace) access the real network, and configure the interface in the namespace (vpn1) to use the interface out of the namespace (vpn0) as its default gateway

    # ip link add vpn0 type veth peer name vpn1
    # ip link set vpn0 up
    # ip link set vpn1 netns frootvpn up
    # ip addr add 10.200.200.1/24 dev vpn0
    # ip netns exec frootvpn ip addr add 10.200.200.2/24 dev vpn1
    # ip netns exec frootvpn ip route add default via 10.200.200.1 dev vpn1
    
  5. Enable IPv4 routing and NAT for the interface in the namespace. As my default interface is a wireless one, I use wl+ (which may match wlan0, wlp3s0, etc.) in iptables for the outgoing interface; if you use a wired interface you should probably use en+ (or br+ for a bridged interface)

    # iptables -A INPUT \! -i vpn0 -s 10.200.200.0/24 -j DROP
    # iptables -t nat -A POSTROUTING -s 10.200.200.0/24 -o wl+ -j MASQUERADE
    # sysctl -q net.ipv4.ip_forward=1
    
  6. Configure the nameserver to use inside the namespace

    # mkdir -p /etc/netns/frootvpn
    # echo 'nameserver 8.8.8.8' > /etc/netns/frootvpn/resolv.conf
    
  7. Almost done, now we should have full network access in the namespace

    # ip netns exec frootvpn ping www.google.com
    
  8. Finally start OpenVPN in the namespace

    # ip netns exec frootvpn openvpn --config /etc/openvpn/frootvpn.conf
    
  9. Once tun0 is up in the namespace, you’re ready to start the program you wanted!

    # while ! ip netns exec frootvpn ip a show dev tun0 up; do sleep .5; done
    # ip netns exec frootvpn sudo -u $MYSELF popcorntime
    
  10. It works!

Since this is a little long to do, I wrote a little helper script to manage the VPN namespace, and another script to run any app I want in this namespace. They are available in this gist.

The only issue I still have is how to let the application inside the namespace access my Chromecast… If anyone has ideas, I’m interested 😃 EDIT: It works! 😋

Comments

Join the conversation by sending an email. Your comment will be added here and to the public inbox after moderation.

scoobynz

Hi. Thanks for this guide. Im about to try this but first have a question. When you set the ip address 10.200.200.1/24 to vpn0, is this an arbitrary ip address of the address system of you LAN?

Schnouki

Hi scoobnyz. 10.200.200.1 is an arbitrary IP address; my LAN is in the 192.168.0.0/16 range. I even used this script when on a 10.0.0.0/8 LAN and it worked just fine (as the VPN vpn0 interface was restricted to a /24 inside this /8).

James McMurray

This is amazing. The only problem I had was that I had to disable UFW with sudo disable ufw first.

sphrak

Hi, im a little late to the game - but I followed your tutorial but beforehand I did some experiements with netns. It appears to be that a namespace is destroyed upon reboot? Is there a way to prevent this?

Schnouki

@sphrak: No way to prevent this, namespaces are runtime only and have to be recreated after rebooting :(

sphrak

I see well thanks for clearing that out - should be possible to run a script on boot though - that creates said ns :)

pskiebe

Hi schnouki, I stumbled upon your guide, I was able to adapt it to my needs and do have an actual VPN connection inside a network namespace now. Thank you very much for that. However, I have one problem that I couldn’t resolve on my own, no matter how hard I tried. I do want to forward a port from the host system to the application running inside the namespace. Any help would be very much appreciated, please have a look: https://unix.stackexchange.com/questions/257510/port-forwarding-to-application-in-network-namespace-with-vpn

Schnouki

Hi pskiebe, very interesting question indeed! I replied on StackExchange with a simple solution that uses socat. Please tell me if it works or doesn’t work for you!

pskiebe

Thank you so much! With your help, I was able to accomplish exactly, what I was trying to do. If anyone else is running an application inside the namespace with a web interface and doesn’t want to connect through the vpn to it, try this: socat -4 TCP-LISTEN:<port>,reuseaddr,fork TCP:10.200.200.2:<port>

henk1122

Hi. When i try doing this, the interface lost connection after 10-20 seconds. The ping command can ping google for like 15 seconds, but then the destination host is unreachable. This is without my vpn started. Why does this happen and how can i solve this?

mdstest

Hi, Your tutorial is amazing. I am looking of a way to use namespace to accomodate openvpn as a server.

So lets say I have these namespace REMOTE1 and REMOTE2. The openvpn server will be running in each namespace.
REMOTE1 Openvpn server 10.10.10.1
REMOTE2 Openvon server 10.10.20.1
So if the server host has a public ip of 1.1.1.1.

How can an outside openvpn client reach the inside REMOTE vpn?

Benny

Thanks for the terrific guide. There are few issues that I see.

1.) if the connection the VPN server goes down, traffic gets routed through the default interface due to the veth link. To avoid traffic leaking outside of the VPN, how can we create a fallback route that blocks traffic?

2.) For some reason when I use transmission-gtk within the vpn namespace it does not work. other things such as ping, traceroute, or chromium work just fine. transmission-gtk also works fine if I start the VPN connection in the main namespace (i.e. not using a separate namespace as in your tutorial). So there must be some port or route that isn’t going through. Any way to debug this?

3.) It would be nice to have the update-resolv-conf script that’s run on the VPN connection coming up and down update the /etc/netns//resolv.conf that is used by the VPN namespace.

Schnouki

@henk1122: Sorry, no idea about that :( Perhaps a firewall that is a little too regarding on what goes out from your computer?

Schnouki

@mdstest: You’ll need to redirect incoming connections on the outside address to the inside address. IMHO the easiest way to do so is with socat, as explained here: http://unix.stackexchange.com/a/258967/8826 (from another comment here).

You will probably open 2 ports on the public IP, such as 1194 and 1195, with 1194 redirecting traffic to the first namespace, and 1195 to the second one. And you’ll need to configure your client to connect to 1.1.1.1 (public IP) with port 1194 or 1195.

If you do that however you will not be able to see the “source” IP of incoming connections from the OpenVPN logs: everything will be coming from your server’s IP inside each namespace (e.g. 10.10.10.1 and 10.10.20.1).

Schnouki

@Benny:

  1. is actually quite easy: remove the default route! Or rather don’t add it (so no “ip route add default …”). Instead, just add routes to reach your VPN server and optionnaly your DNS servers: “ip netns exec $VPN ip route add 1.2.3.4/32 via 10.200.200.1 dev vpn1”. You won’t be able to ping anything except for the whitelisted IPs until your VPN is connected.

  2. not sure as it works for me. Do you have a firewall that filters incoming connection on your computer?

  3. I don’t use update-resolv-conf so I couldn’t tell, sorry :/

Benny

Thanks. I went ahead and came up with a complete solution that addresses the concerns I had. Instead of namespaces, I used control groups, which are in many ways superior. You can grant a non-root user access to run processes in the isolated cgroup and using two instances of dnsmasq can separate DNS queries. More at the link.

http://serverfault.com/a/766290/345463

Jason Heeris

Second question: in the not-namespace (root namespace?), I tried your iptables commands. But the result of iptables -S is just:

# iptables -A INPUT \! -i vpn0 -s 10.200.200.0/24 -j DROP  
# iptables -t nat -A POSTROUTING -s 10.200.200.0/24 -o eth0 -j MASQUERADE  
# iptables -S  
-P INPUT ACCEPT  
-P FORWARD ACCEPT  
-P OUTPUT ACCEPT  
-A INPUT -s 10.200.200.0/24 ! -i vpn0 -j DROP  

It seems to be ignoring the second one, which I think is causing problems later on. What is going wrong here? I’m on Ubuntu 14.04.5, if that’s a factor.

Jason Heeris

Oh, I needed to do iptables -t nat -v -x -n -L to see the NAT rules. It’s there (four times, in fact, because I thought maybe if I typed louder it would work).

So after fixing all that up, my real problem is: even after adding the default route (in the namespace) and enabling NAT (outside the NS), I can’t ping anything from within the namespace. I’m not even trying to use names here (I haven’t set up DNS yet). I just see (from inside the NS):

# ping 8.8.8.8  
From 10.200.200.2 icmp\_seq=1 Destination Host Unreachable  
From 10.200.200.2 icmp\_seq=2 Destination Host Unreachable  
From 10.200.200.2 icmp\_seq=3 Destination Host Unreachable  
^C  

Any suggestions?

Jason Heeris

I’ll post the answer to this here in case anyone else has this problem: on my system, the effect of ip addr add 10.200.200.1/24 dev vpn0 was being mysteriously reversed after a while by… something. Could be Network Manager.

In other words, the line from ip addr show (in the root namespace) that looked like this:

inet 10.200.200.2/24 scope global vpn1  

…was disappearing. I had to run that first command again.

Update: It was network manager! Thanks to this thread I found that if you name your virtual interface ends veth0 and veth1 they will be ignored by network manager, and everything works.

Thanks for the excellent tutorial!

Jason Heeris

@Benny:

  1. Will cause problems if you actually want your VPN to set the default route (which most do, eg. with redirect-gateway def1 in the pushed parameters or config). It’ll fail with:
NOTE: unable to redirect default gateway -- Cannot read current default gateway from system

…and there will be no default route out of your namespace.

Anonymous

Using this with dante-server as socks proxy, this is amazing !

Алексей Кузнецов

Thanks for your tutorial. It works, I use it widely. Do you think it’s possible to put squid proxy server in front of the system so that every proxied request will go through vpn that lives inside the network namespace?

I mean we can run ip netns exec frootvpn curl ipv4.icanhazip.com that gives us vpn’s IP, but it seems to be more convenient to run just curl ipv4.icanhazip.com -x localhost:3128. Here I assumed that we have proxy server running on 3128 port that sends every request to the vpn in frootvpn namespace. The only problem is that I can’t set it up.

We have vpn0<->vpn1 link for accessing the real world from inside the namespace. Is it possible to use it in reverse way, send something to vpn0 interface (from the root namespace) and get result from the vpn? I played with iptables rules to make curl ipv4.icanhazip.com --interface vpn0 work with no success. I also added the second vpn2<->vpn3 link and tried to send something through the following chain: root.vpn2 -> frootvpn.vpn3 -> vpn -> frootvpn.vpn1 -> root.vpn0 with no success either.

It would be great if you share any thoughts about this case.

Giorgos

Hello, I just managed to make this work, I had to modify the /lib/systemd/system/transmission-daemon.service under these instructions to make it working under Ubuntu 16.04. My problem is I can’t access the web interface anymore. Any suggestions;

Schnouki

Hi Giorgos! I haven’t tested this with transmission-daemon, but I quickly read some docs and I think you’ll have to:

  • configure transmssion-daemon to allow connections from the default namespace. If you’re using the same config as me, this means adding “10.200.200.*” to the rpc-whitelist config as documented in https://github.com/transmis…
  • restart transmission-daemon
  • connect to http://10.200.200.2:9091 instead of http://localhost:9091.
    Please tell me if this works!
Giorgos

Actually I run the system headless and I have access to it’s web gui via a local desktop. So it must be something else to do the trick. I tried with your suggestion though. Also pinging 10.200.200.2(odroid) from the local(desktop) doesn’t return anything, but it Works! Any suggestion much appreciated.

Schnouki

Sorry for the delay.
When you say “local desktop”, you mean another (desktop) computer on the same LAN, right? In that case, you may have some luck with socat as described here: https://unix.stackexchange…. (and in the other comments). You will still have to configure transmission-daemon to accept connections from 10.200.200.* though. Hope this helps.

Justin Kromlinger

Note that you might want to limit all outgoing connections to the VPN domain and udp port, basically a killswitch:
iptables -t nat -A POSTROUTING -d $VPN_DOMAIN -p udp –dport 1195 -s 10.200.200.0/24 -o wl+ -j MASQUERADE

A

Thanks so much for this tutorial. I vaguely knew that (network) namespaces existed, but didn’t think they are so easy to use. This was exactly what I was looking for. Except for the Netfilter part - as I was already using nftables on that machine. Got it working with the following:

nft add rule inet filter input iif != vpn0 ip saddr 10.200.200.0/24 drop
nft add rule nat postrouting ip saddr 10.200.200.0/24 oif eth0 masquerade

Thanks again!

forevertheuni

Hi. It doesn’t work in my case (ubuntu 17.04).

then I try to start vpn (either with .ovpn of .conf) it just gets stuck at:

Tue Dec 5 10:52:21 2017 UDP link local: (not bound)
Tue Dec 5 10:52:21 2017 UDP link remote: [AF_INET]“IP.ADD.RE.SS”:1194

I start it with:
sudo ip netns exec namedvpn openvpn fileto.ovpn
or:
sudo ip netns exec namedvpn openvpn –config /etc/openvpn/blablabal.conf.

If I just do “sudo openvpn file.ovpn” everything works fine.
Any hints for me?
(I tried both the vppn.sh and manually). zsh vppn.sh up has no error messages and creates all the stuff.

Thank you!

pjotruk

Giorgos: Well, you can actually use the solution you mentioned here already and it is also submitted as solution here: https://unix.stackexchange.com/questions/257510/port-forwarding-to-application-in-network-namespace-with-vpn With Transmission-daemon default web ports and config described in this tutorial your approach would be like this:

sudo apt-get install socat

socat tcp-listen:9091,reuseaddr,fork tcp-connect:10.200.200.2:9091And Schnouki btw, this post is just amazing. I have a lot to learn from this. Thank you.

pstryk

@Benny: Question about 1. The kill switch seems not to work. When the VPN is down deluge is still downloading the torrent. This is what I have for routes:

ip netns exec deluge ip route add 1.2.3.4/32 via 10.200.200.1 dev vpn1 ip netns exec deluge ip route add 8.8.8.8 via 10.200.200.1 dev vpn1 (default route removed) Any way to fix it ?