Pi: Make a VPN gateway with UPnP port forwarding

Tunneling your traffic over an encrypted VPN can be good for both privacy concerns and circumventing geoblocking. If a service is only offered in a specific country or blocked at your current location. My use case is a bit of both. Currently living in the USA which is the biggest surveillance state on earth I want my traffic to originate from my home country, Sweden, where I know the law and whats allowed and not allowed. Avoiding the mighty force of the NSA completely can only be done by unplugging but at least it’s a little bit better. Also several services I want to use is only offered in Sweden, like local Swedish news as an example. Both of these can be solved by setting up a VPN tunnel back Sweden!

Infrastructure background

The basic setup of my system is a local network that is connected to my Comcast router. Behind that I have my “server”, a bunch of Raspberry Pi’s, Banana Pi’s and Odroid’s running different services. I have seen a lot of tutorials that run the VPN, in this case OpenVPN, on the same box as their torrent box or media player. Due to the heavy load on the CPU when encrypting and decrypting I prefer to have it on a separate box. It also follow my overall network design with separating services so I can jank out what ever box I want and run tests or make changes without it effecting everyday operations. Also this way I will have a gateway that can provide the VPN tunnel to several different clients.

On the other end I have a commercial VPN provider in Sweden. I have my own servers with gigabit speed but I still use the commercial one I got long ago since it’s prepaid for a long time still. There are a bunch of different VPN providers out there providing different type of services. What VPN provider to use is outside the scope of this post but you need to use one that supports OpenVPN.

I have used these steps to setup a VPN gateway on both the Raspberry Pi and the Banana Pi, both running Raspbian. Without mayor tweaks this will work on most major linux distributions.

Setting up the basics

As for any project first bring the Pi on the network make sure SSH works, change the root password, and update it. When that is done we need to setup a static IP-address on the box. There are several reasons why this is a better approach then DHCP and they can vary a bit from case to case. Number one is that it needs to have the same IP all the time since other machines will use it as their default gateway. That can be done by DHCP reservation even on my crappy Comcast router but I can’t give it specifik configuration like DNS etcetera. That will end up being a mess once the tunnel is up and the box tries to talk to the “internal” Comcast DNS over the internet. So we start by editing the interfaces.

[bash]sudo nano /etc/network/interfaces[/bash]

In the newer version of Raspbian you will find iface eth0 inet manual already. On versions previous to Raspbian Jessie (2016-02-26) it was set to auto. If you use an earlier version it will proberbly say iface eth0 inet auto then you should change it to the config below, of course changing out the values to correspond with your network.

[bash]iface eth0 inet manual

If your running Jessie and/or your pi is set to iface eth0 inet manual already without any configuration below and receiving a DHCP address you need to edit /etc/dhcpcd.conf instead. At the end of that file you add:

[bash]interface eth0
static ip_address=
static routers=
static domain_name_servers=[/bash]

When that is saved do a sudo reboot and try to connect to the new ip after a while.

Banana Pi: If your running this on a Banana Pi you need to update the /etc/resolv.conf as well. Just put your preferred DNS servers in there, one peer line.


Installing OpenVPN

First update the Pi if you haven’t already done so.

[bash]sudo apt-get update
sudo apt-get dist-upgrade[/bash]

Then install OpenVPN.

[bash]sudo apt-get install openvpn[/bash]

Most commercial VPN providers provide you with a .conf file for your OpenVPN client. It contains all the settings and certificates for connection to their service. In addition to this you have a username and a password. That works great running it from your desktop but this is a stand alone box that we want to bring the tunnel up after a power failure for example. So we need to open the .conf file in a text editor and add a line that reference another file containing our username and password. In the .conf file I add this line:

[bash]auth-user-pass /etc/openvpn/swe.auth[/bash]

Then a create the swe.auth file and put the username on the first line and the password on the second line. I then use WinSCP to transfer these files into /etc/openvpn/, you can use a USB-stick or just copy paste the contents through nano over SSH if you like as well. All the .conf files in the /etc/openvpn folder will automatically be ingested when the OpenVPN daemon starts and it will try to bring up the tunnel. So we restart the OpenVPN daemon:

[bash]sudo service openvpn restart[/bash]

To test if we are connected we can use wget -qO- http://ipecho.net/plain ; echo, of course it’s a good idea to do this before your bring up the tunnel as well so you have something to compare it to. You can always check from your desktop running against your regular router to see the difference.

Setup routing

Now the box it self can use the VPN tunnel but it isn’t a gateway yet. So first we need to enable IP routing. First command adds the configuration to the sysctl.conf file and the second loads them in. These settings will be loaded from the file upon reboot as well so it’s persistent.

[bash]sudo /bin/su -c "echo -e ‘n#Enable IP Routingnnet.ipv4.ip_forward = 1’ > /etc/sysctl.conf"
sudo sysctl -p[/bash]

When the settings are loaded with sudo sysctl -p you should see this printed on the screen:

[bash]net.ipv4.ip_forward = 1[/bash]

Setup iptables (firewall)

Now the box can route but we also need to setup some routing rules for the traffic to flow from the internal network, over the tunnel and out on the internet at the other end. We do this by using iptables. Iptables is a basic rule based firewall pre-installed in most Linux distributions. It can look complicated at first but when you get the hang of it it’s pretty straight forward. You have your basic chains INPUTOUTPUT and FORWARD. The names are pretty self explanatory but here is a brief, simplified breakdown of the three:

  • INPUT – all traffic coming into the box (with the box as destination) from the outside on any of the network interfaces.
  • OUTPUT – all traffic leaving the box with the box as it’s origin.
  • FORWARD – all traffic forwarded (and routed) between the box different network interfaces.

All chains have a set of rules that iptables try to match the traffic to. If no rule match it will go to the chains default policy. Breaking down this line by line will give you a better understanding of what we are doing here.

[bash]sudo iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE[/bash]

First we enable NAT (network address translation), it’s the same technique as your router uses. You have private ip-addresses on your internal network (at least most people do) and they can not be routed out onto the internet. You need to use your single public IP for all your clients behind the router. NAT keeps track on who requested what so when the response comes back it can send it to the correct internal host.

[bash]sudo iptables -A FORWARD -i eth0 -o tun0 -j ACCEPT[/bash]

We setup a rule allowing all requests coming in on eth0 (the internal physical network card) to be routed over the tunnel (tun0). In this setup we allow all traffic from inside to outside to go through.

[bash]sudo iptables -A FORWARD -i tun0 -o eth0 -m state –state RELATED,ESTABLISHED -j ACCEPT[/bash]

We den need to allow the responses to come back into the network but we only want to allow responses. By specifying –state RELATED,ESTABLISHED we only allow traffic back in that we initiated from the inside.

[bash]iptables -A INPUT -i lo -j ACCEPT[/bash]

Let the box talk to it self. This rule is very importent since we are setting a default policy on INPUT to DROP.

[bash]sudo iptables -A INPUT -i eth0 -p icmp -j ACCEPT[/bash]

We allow hosts on the local network to ping the box.

[bash]sudo iptables -A INPUT -i eth0 -p tcp –dport 22 -j ACCEPT[/bash]

We allow SSH from hosts on the internal network.

[bash]sudo iptables -A INPUT -m state –state ESTABLISHED,RELATED -j ACCEPT[/bash]

We allow traffic responses from anyone as long as we established it.

[bash]sudo iptables -P FORWARD DROP
sudo iptables -P INPUT DROP[/bash]

We set the default policys to drop everything but the traffic we specifically allowed by setting the default policy to drop for both the FORWARD and the INPUT chain. In this case we leave the OUTPUT chain untouched with it’s default policy of ALLOW. That means that all traffic the box tries to send out will go through. If we where to run this on a torrent box for or any other box that are only allowed onto the internet over the VPN we would need to setup the OUTPUT chain to only allow DNS and VPN (udp 1194). That would prevent any traffic from the local host to reach the internet if the tunnel is down. Since we are building a VPN gateway where we limit the traffic from other hosts with the FORWARD chain no traffic will get through from other clients when the tunnel is down. All internal clients will use the box as there default gateway, the only forwarding rules in place are between eth0 and tun0 which means that if the tunnel is down no traffic can be forwarded.

[bash]sudo apt-get install iptables-persistent
sudo systemctl enable netfilter-persistent[/bash]

We make the iptable rules we just added persistent after reboot, just answer yes on the questions in the install. The second command will make it persistent after reboot. If you change any iptable rules after this just run the command below to save them. A reference to iptables can be found here http://ipset.netfilter.org/iptables.man.html

[bash]sudo netfilter-persistent save[/bash]

UPnP port forwarding

To be able to use UPnP enabled devices and software that needs to open ports with out VPN tunnel we use linux-igd. It’s basically a daemon that processes UPnP port forwarding requests and updates the iptables. These updates are non persistent so they clear out every time we reboot. We will also set this up so it starts automatically with the VPN tunnel and also destroys all the forwards when then tunnel goes down or is disabled. First we install the deamon:

[bash]sudo apt-get install linux-igd[/bash]

Then we need two bash scripts that can execute when the tunnel comes up or goes down. So in the /etc/openvpn folder we can just use nano to create the scripts.

[bash]sudo nano /etc/openvpn/tunnel.up[/bash]

This script will run when the tunnel comes up and need to start the upnp daemon. So we put this into it:

/usr/sbin/upnpd tun0 eth0[/bash]

The upnpd takes two parameters first the public interface, in this case the tunnel (tun0) and the second one is the internal interface (eth0). Then we need a script for killing it all of when the tunnel goes down.

[bash]sudo nano /etc/openvpn/tunnel.down[/bash]

In this one we need to kill of all the opened ports.

/usr/bin/killall upnpd[/bash]

Make the scripts executable:

[bash]sudo chmod +x /etc/openvpn/tunnel.up /etc/openvpn/tunnel.down[/bash]

And then we need to reference them in our OpenVPN config file. In this case /etc/openvpn/swe.conf so we open that with nano and add these lines at the bottom:

[bash]# add up and down script for uPNP
script-security 2
up /etc/openvpn/tunnel.up
down /etc/openvpn/tunnel.down[/bash]

We also need to to open the port for the UPnP requests coming in. I read through a lot of documentation on this and there are a number of ports mentioned that’s needed for this. It took me quite a while to figure this one out. Almost all documentation at least agree on UDP 1900,5351,5353 and TCP 49152. That was the ports I found after a while as well, using Transmission as the client requesting UPnP port forward. You can read more about how to troubleshoot this on my serverfault question “Allow UPnP and NAT-PMP request in iptables”. In the end these two lines worked with my Transmission implementation:

[bash]sudo iptables -A INPUT -i eth0 -p udp -m multiport –dports 1900,5351,5353 -j ACCEPT
sudo iptables -A INPUT -i eth0 -p tcp -m multiport –dports 49152 -j ACCEPT
sudo netfilter-persistent save[/bash]

Now just restart the OpenVPN service and the tunnel should come up and work with UPnP port forwarding.

Manual port forwarding

UPnP port forwarding is convenient and easy to use but not all implementations supports it. Maybe you don’t want any device on the inside to be able to open ports to the rest of the world. If any of these are true in your case you can of course do manual port forwarding with iptables. In this example we want to listen on tcp port 666 and forward that to internal ip

[bash]sudo iptables -t nat -A PREROUTING -p tcp –dport 666 -j DNAT –to
sudo iptables -t filter -A FORWARD -p tcp -d –dport 666 -j ACCEPT
sudo netfilter-persistent save[/bash]

First line is to setup a NAT rule for that port. The second one is to allow the traffic through to the internal IP-address. The last line saves the rules so they are persistent after reboot.

Clients logon delay

If you use this gateway for your other Pi implementations as a default gateway you will probably experience a logon delay running SSH into the box. Since the Pi can’t do reverse DNS (IP-address -> hostname) for internal hosts anymore it will wait for the timeout when you try to logon. In short, for the log it resolves the incoming IP-address connecting over SSH to a hostname. Since the, in this case Googles, public DNS can’t lookup your internal boxes it will timeout. This results in a delay from when you put in your username and press enter until the password prompt appears. This can easalie be fixed by editing /etc/ssh/sshd_config and disabling the reverse lookup for SSH logins. Just add this line at the end.

[bash]UseDNS no[/bash]

40 Comments on “Pi: Make a VPN gateway with UPnP port forwarding”

    • It depends on which Raspberry Pi you use. My Pi is a basic model B and the tunnel is running around 10Mbit/s. I have just tried the same setup on Raspberry Pi, Raspberry Pi 2 and Raspberry Pi 3 and there is a difference between them. The more CPU power you have the better it will coupe with the encryption load. I will try to get an article ready with my findings in the next couple of days.


  1. I am on Kodi 15.2 and it seems I have no ways to edit anything as is boots straight into kodi. Any options?




  2. Pingback: OpenVPN performance on the Pi « Hackviking

  3. Hello Kristofer, fellow Swede here..
    I have been looking at a lot of your Pi Projects, and i think that i maybe will be able to build my self a perfect travel companion Pi, by trying out some parts of your projects.

    I would love if you could give me your 2 cents on if you think its going to be possible to get all these functions in one pi without having to have multiple sd cards with diffrent setups.

    I havent found any pi project out there trying to build a perfect traveling Pi.

    I do some traveling abroad, and so far i have not been able to find a perfect sollution.
    (been trying some laptop sollutions, some travel routers from hootoo ,and even some pi projects that have taken care of some single problems, but never any complete sollution)

    The perfect one for me would be able to have a Raspberry Pi with 2 wifi dongles (or a pi 3 with one dongle that i could use in these ways:

    1. For use in a hotel, connect the pi to the hotel wifi or wired lan (even those that use some sort of web interface for auth) and then share the connection in an own private hotspot for my other devices (android phone, laptop, ipad and chromecast dongle)

    2. Same as above but with the ability to have the Pi connected to my home router (asus rt-ac68u with asuswrt-merlin custom firmware) that is running a openvpn server. I use this vpn on my laptop to be able to stream swedish content on my laptop netflix, tv4 play, comhem play, cmore and canal digital. But i want to move the vpn to a Pi hotspot to be able to use my chromecast for streaming my swedish content.

    3. i just came home from a 3 week roadtrip in America with my wife..
    last week we spent in naples where the appartment we used had a comcast router and the wifi coverage was pretty bad, so i would like to be able to use the pi as a wifi extender (maybe use a wifi dongle on the pi with an better powerful antenna?) and be able to extend the range both throu the other wifi or via lan cable between the router and my Pi

    4. same as above and the ability to use the same vpn connetion home

    What do you think, is these possible, or should i look in an other direction, or do you know of any other good traveling companion

    // Thanks in advance / Mathias


    • Hi Mathias,

      Nice with a fellow Swede!

      1. Multiple wifi networks on the same adapter can be tricky. Don’t know if the hardware can do that in the Pi but I doubt it since it might need to switch channels back and forth since it only have one physical radio. I would recommend to use a Pi3 and use the built in wifi to connect to the actual hotel wifi. Then an external dongle with a better antenna to have your private network on. Then you can route the traffic between the two interfaces like any other router. You can even use IPtables to firewall the whole thing and apply UPnP for opening ports from devices on the inside like I did with the VPN router

      2. Look at the post linked in point 1. It does exactly what you are after. I have seen performance around 10-15Mbit/s with OpenVPN on the Pi and it should be enough for some simple streaming. I use that in my SF apartment to get “swedish” internet for play channels back home.

      3. See answer 1. You can extend with a private network and give that access to properly firewalled internet and OpenVPN connection back home.

      4. If you just extend the wifi already there and add a VPN connection others might slip in on your wifi and end up on your OpenVPN connection. My recommendation is one dongle to take the public wifi in and one private with access to the internet and your VPN through iptable rules.

      5. Didn’t really have time to look into the notrack but it looks to be bash script so it should run without issue on you Pi. If it’s based on additional software it will work as long as it can compile on the ARM architecture.

      Hope that gave you some more information!



  4. On 1. I was thinking of a Pi3 with a extra dongle = 2 wifi interfaces.

    I do have one wipi dongle (http://se.farnell.com/productimages/standard/en_GB/2133900-40.jpg) and a Edimax dongle (http://ecx.images-amazon.com/images/I/71P8Pfd7npL._SL1500_.jpg)

    But im thinking of getting one of these from modmypi (http://www.modmypi.com/raspberry-pi/accessories/wifi-dongles/wifi-dongle-ultra-long-range-high-gain-w-5dbi-antenna) to make my hotspot a bit more powerful.

    One problem i have had on hotels is how to log in to the hotel wifi when they have a html page for auth when im running my pi headless. (unfortunally there often is minimum time to try diffrent sollutions when at the hotel, so i havent been able to try alot when at a location.

    But im thinking the sollution might be to have the pi boot into desktop and then access it with vnc to use the browser to connect to the hotel wifi, or do you know if there is a way to do it by commandline?


  5. My Asus router exports a .opvn file and not a .conf file.
    Do you think its the same to add the line auth-user-pass /etc/openvpn/swe.auth into that .opvn file?
    I cant seem to test this at home where the pi is allredy on my local network (i cant vpn on my android if connected to my wifi allredy, but with 4g connection it works) so i guess i have to bring the Travel Pi to work next week and try to set it up from there.


  6. Hi! Fellow Swede here also! Thanks for this awesome guide but I have an issue. Seems the VPN goes down every night, in the morning I have no connection and have to reboot the pi. When rebooted everything goes back to normal. What could this be? Does it have like a sleeptimer or do you think my VPN provider have a time out of the tunnel?

    Thanks in advance.



  7. Thanks the reply! I tested it again and seems it goes down when the network is inactive, so it could pretty much be the provider. I will check with them and also try another one.


  8. Hello!
    Thanks for this awesome tutorial. I have implemented the previous version of it (without the UPNP part) and works very well, except for net.flix, which still detects the country I am in. I believe this is because Netflix captures DNS requests from the clients in my local network (in which I configure the raspberry as DNS server). I have tried some solutions I found around to forward DNS through the VPN using bind9 or dnsmasq in the vpn gateway, but I am not able to get it working. I would appreciate some advice for this. Thanks!!


    • Easiest way would be to use Google public DNS servers, and Then if you use the raspberry pi VPN gateway as default gateway it should send your DNS requests over the vpn. You can easily verify this by running a trace route against and make sure it goes over the vpn.


  9. Thanks for your quick reply! This is the way I tried to configure it, but if I use the pi as gateway somehow the DNS requests don’t go through it, and if I use the pi as DNS server as well, the clients cannot resolve the DNS.Then I tried to play with bind / dnsmasq with no success so far. I will use trace route to diagnose and let you know any findings.
    As you can guess I don’t have have much network skills… still, I tried to do my homework before asking


    • If the computer you are trying to run Netflix on uses the pi as default gateway the DNS should go through that. You should check that any traffic is actually going through by a simple test on whatsmyip.org and compare that to a none vpn computer.


      • Yes, it uses the default gateway. Whatsmyip gives back the correct IP (through the VPN), and some geo-restricted services detect correctly the VPN’s location and work. I only have the problem with Netflix, and I guess the reason is DNS routing.

        Actually, if I use a VPN client in my computer (without using the pi as gateway), then I have no problems and everything works fine. But I need the gateway for an Apple TV (no VPN clients there!).

        Thanks again for your patience!


      • Yes, it is the same VPN provider – actually another raspberry pi in my home country with an openvpn server.
        A tracert from the gateway pi shows the VPN interface as first step when openvpn client is running, then my home router and then goes through servers in my home country. When the openvpn is stopped, it goes to the home router and then through different servers, so I guess it’s OK.
        I will check for the local clients when I’m back home to see how the routing is done – thanks again for your time so far!

        By the way, and adding up to your openvpn performance testing, I installed two openvpn servers, one on a pi 1 B and another one on a pi 3 b. A speed test on a client laptop gave way better performance on the pi 3: roughly 30 Mb/s vs 8 Mb/s (up / down – servers are installed in 300/30Mbs lines and the client was on a similar one).


      • Ok, I found the issue with Neflix – It turns out that it makes ipv6 connections. As I am not skilled to configure OpenVPN to support ipv6, and it turns out that disabling ipv6 in my router breaks other things (probably ISP stuff), I have resolved to filter out ipv6 traffic in the router (without disabling it) and therefore Netflix falls back to ipv4, which goes correctly through the tunnel.
        That was really a tricky one! The gateway was doing its job nicely since the beginning.
        Thank you very much for your help!


      • Yes! This is actually how I found out – I discovered that Netflix was working fine thought the VPN in my laptop, which happened to have ipv6 disabled, but not in my desktop or mobile devices.
        I had to go to the router because I cannot disable ipv6 on the Apple TV, there is no such setting. Searching the web I found out that actually the Apple TV changes the ipv6 address relatively often, which makes specific address blocking in the router not practical (this was my second choice). The third choice, MAC blacklisting, was not allowed for ipv6 only in my router.


  10. Thanks for the tutorial!

    In my case, I’m using openconnect to setup a Cisco VPN connection on a Raspberry Pi 3, which works well. However, I’m not sure if it is possible to forward this connection over WiFi using openconnect? I thought that the part of the tutorial starting from ‘routing’ also would apply to me, but until now I’m still not able to create a VPN gateway using these settings. Any tips? Thanks!


  11. Hi Kristofer. I was wondering if I can run a Pi-VPN scenario by you. I have a need to have inbound ports (think “servers”) that are not available to me on Public WiFi (think “Starbucks). My plan would be to create a Pi that is an OpenVPN server, with a dyndns client so it can be “found”, and a UPNP client. I will plug it into a friend’s router. It will request a certain set of ports be forwarded to it. So, go to a friend, ask permission, plug the Pi in, and it gets an IP from his router, requests several TCP and UDP ports and ranges, and then waits for my connenction. My client OpenVPN device finds the Pi, logs in, and gets the ports needed. At that point my client will send and receive through the VPN, just as if I were at my friend’s house with DMZ or whatever ports I need, even though I am out on some random WiFi connection I can’t control. Encryption is not necessary. Thank you! -Pres


    • That should work just fine as long as the router at the openVPN server end either has support for UPNP or you open static ports to it. Never had that need my self so I haven’t tried it but it’s basically a matter of triggering the ports when a VPN is established and sort out the routing. Triggering can be done with a connection script and routing as well.


  12. hi, I already have a raspberry pi 3 running on my network with a torrent service and a dlna service. can I configure the vpn on top of this,


  13. Hey there! My VPN provider doesn’t have a .conf file, but an .ovpn file. I know this works when using the GUI for VPN, but I don’t want a GUI eating up resources and so on. How do I use that file?



    • That shouldn’t be a problem, they are basically the same thing. You can either point to the .ovpn file or rename it to .conf file. Just remember to add the auth-user-pass configuration so the VPN tunnel can start without user interaction. If you Google openvpn auth-user-pass there is a lot of information on setting that up.


      • Alright, done that. But it doesn’t work. I’ll always get my regular internet IP back from the wget line. If I check the openvpn service it says “active (exited)”. I don’t know how to change that… If I run openvpn as sudo user it shows no errors and doesn’t terminate, I see the IP my VPN provider assigns me in openvpn’s output etc. Just doesn’t work as a service… Any ideas?


      • When you run it as sudo does it connect properly? Do you have to enter the password when you run it? If so you need to fix the password step in my last comment. If that isn’t the problem start by looking at the OpenVPN log and go through it line by line until you find any errors. If the tunnel connect ok make sure you have switched the default gateway on your client to point to your pi.


  14. Yes, it runs fine as sudo. It even remarks that the swe.auth file is accessible by anyone and the tunnel is established, I can see an IP in the typical range my VPN provider assigns clients. Of course as long as it runs sudo I can’t check if the tunnel works correctly.
    I tried to take a look at the openvpn logs, but it doesn’t even write any in /var/log or /var/log/openvpn…


    • Then you know your config is good. Check that the service is configured to pick up the correct config file. If it doesn’t log anything it probably doesn’t even start correctly. Check the messages or syslog to see if you get any errors. Tip: you can run multiple ssh sessions against the pi so you can start the tunnel with one and check if it works with the other.


    • I don’t know what happened, but after some fiddling around and running nopenvpn as a daemon with the –daemon option it now works. Tunnel is established, public IP is “foreign”. Thanks for the help!


      • Do you have a specific use case you need it for or is it just a nice to have? First check that your tunnel provider supports it. There would be several additional steps to get that to work, it would be a different post.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: