Selective VPN routing [Solution: DSVR]

Before sharing about what I believe is my best solution yet, i’ll take a walk down memory lane…

Client VPN

Got my first VPN account (PPTP/L2TP) and happily used it from my various Windows/Mac/iOS clients. Very quickly I came across a few limitations, namely:

1) Only one device at a time could use the VPN.
2) More restricted clients did not have a client side capability to configure the VPN (like the Apple TV/PS3)
3) When a device was on the VPN, all traffic went down it – not ideal from a performance perspective, and when you’re location is determine by IP (e.g. Google Maps).
4) Minor at the time, but could not have multiple VPNs running.

Router VPN [+Source Based Routing]

Configuring  VPN connection at a single aggregation point (i.e. the router) was the next step, as this did solve some of the limitations (1 & 2 above). After a bunch of research (read: trial/error) I concluded that there was no practical way to be selective at the router based on destination as many services used over the VPN where using CDNs with massive network ranges which we’re not practically predictable.

I decided that source based routing was a good solution, in my case I selected my Apple TV to be the only source to push down the VPN this has served me well,  and I published a tutorial on it a while back.

Defining the next step

Even though my problem/use-case had largely been solved, I wanted to go further. I set myself the following goals for my next solution:

1) Selective destination routing, route only sites/domains I want.
2) Multiple concurrent VPN client support, each with their own destination mappings.
3) Multiple clients able to share the outgoing VPN(s) connection(s)
4) A ‘plug-n-play’ appliance, with no configuration changes to exiting infrastructure (clients/router/modem)
5) Simple web administration interface.

With those goals in mind – it was time to determine a solution.

New Solution: DSVR [Domain-Specific VPN Router]

dsvr-logical-v0.2

1. Selective destination routing

I’ve already touched on some of the challenges (e.g CDNs) – there were a few more – but ultimately I determined that a pre-populated list of IP routes for a service will not work.

Then an idea came to me – what if I made something that I’d best describe as a ‘DNS Router’, at a high-level it would need to do the following:

1) Intercept all outgoing DNS queries from my clients.
2) Analysis the query name against which domains I’m interested in pushing down the VPN.
3) Pass the unmodified DNS request upstream to a valid DNS server, and get a valid response back.
4) Before passing the DNS response to the original client, add routes for the RR data within the DNS record

What does this do? For each matched DNS request we are creating static routes down the VPN!

Theory is great – but I needed to now make this thing.

It was clear to me that the best starting point was to leverage existing work that’s been done creating a DNS proxy. I found a great one – dnschef – by Peter Kacherginsky. Using this as a strong foundation I was able to modify and expand this to perform the 4 points above, and also handle some edge cases around CNAME responses etc.

In my testing – this has proven to work and be stable.

2. ‘Plug-n-play’ appliance, no configuration changes, clients able to share

Deciding on a hardware platform for the appliance was easy – Raspberry Pi – it’s low-cost, low-power, runs linux and small. It’s not without it’s limitations though (e.g. NIC limited by USB bus)

The aggregation point I selected to place the Raspberry Pi was between the existing CE/Modem and home router. This meant the RPi had to be configured as a typical NAT router. Other than the OS level configuration required to create the router, the RPi only has a single NIC, so I added a second NIC interface using a USB add-on.

To achieve the second portion of this goal ‘no configuration changes to exiting infrastructure’, I used iptables to transparent redirect all outbound DNS queries to my new custom DNS proxy/router.

dsvr-physical-v0.2

3. Concurrent VPN client support

This was wasn’t too tough (for PPTP), basically just needed to configure the following for each:

1) PPP peer file
2) IP-UP script
3) INIT.D script

OpenVPN support is in the pipeline.

4. Web administration

Seeing that the core capability was done in Python, it was a logical choice to create the web interface in the same. I selected the Flask web-framework, and I used there small dev web server in lieu of the big boys like Apache.

The web interface allows for the following functions:

1) PPTP VPNs – Add/Modify/Delete
2) Specify which wildcard domains should route down which VPN
3) Stop/Start/Restart the core DSVR service
4) Status display for PPTP tunnel status, IP assignments, Route counts, uptime, mem/cpu usage
5) Reboot the RPi

webadmin (1)

Get/Build it

This blog post is just a static introduction. The code, instructions, limitations, issues and TODO’s are being maintained on the DSVR github page.

Do note that this should be considered an ALPHA release – it has not been tested by many people – use at your own risk.

gitHub-download-button

28 thoughts on “Selective VPN routing [Solution: DSVR]

  1. This really cool. Thanks I want to try it but?
    1. I am not a programmer nor a networking expert.
    2. I have a 100MB cable internet connection and are worried about the USB from the Pi slowing down all traffic?
    3. My Modem is also my router (in one) DNS.
    4. I do have a DDRT acting as a AP further down the line – could I use that for your earlier project as that is where my PS3 and XbMC vpn users are connected.

    Thanks so much

    1. Hi Nigel – thanks for your comment.

      1. No programming involved, bit of linux command line. You should be able to follow the README installations on the GitHub page, let me know if anything needs explaining.
      2. Likelihood is that your 100mbit service doesn’t really give you 100mbit… but I would suggest you do a speed-test before and after to see if there is any degradation.
      3. This is an issue for the Raspberry Pi DSVR solution, however your point 4 indicates you have a downstream DDwrt router, so you could wire it up like this: internet->integrated modem/router->raspberry pi DSVR->DDWRT (wired & wireless)

      But considering all of the above – it seems that the source-based VPN routing solution is working for you… so probably best to leave good enough alone 🙂

    1. Darran
      I finally tried the sourced based routing but it did not work for me as my DDRT is acting as an access point and not a router.

      So I have implemented your Destination based solution with the Raspberry Pi.

      Supreme Kudos to you mate. Such an impressive project and your instructions were terrific to follow. It works really well and I had fun doing it.

      Now there is one unexpected challenge. Devices like the Boxee box or iPad use apps to access their IP services. These services only appear in the UI if you have a vpn (or you are in the right region). Do you think it is possible to find their IP addresses so that the catalogue of available aps can be first accessed and then the selected app IP can be put on the “interesting” list?

      1. Hi there – glad to hear it worked out!

        To your query about Boxee/iPad apps… I can’t talk for Boxee, but the apps available to you on iDevices/AppleTV is not a function of being on a VPN, or having a valid IP in that region. You just need to have your device set to the region (e.g. US) by having valid iTunes account associated with that region. For example, you can create a US iTunes account without any payment details (http://support.apple.com/kb/HT2534), then use iTunes voucher to credit it if you need some $$ in there.

        I do think it’s a good idea to have a central list of apps/services and their associated ‘interesting’ domain names… I think the Wiki on the github page would be a good spot, so please post anything you find there and I’ll do the same. (https://github.com/dboyd13/DSVR/wiki)

        Cheers.

  2. I saw your post on DDWRT forums about your DSVR project (when I was pulling my hair out trying to configure my router to do source based routing) and got intrigued enough to buy a raspberry pi to try this out. This really did simplify everything for me.
    You deserve more props for making this easy for the rest of us. Thank you..Seriously, thank you…

    1. Hi Oliver, glad to hear it worked for you 🙂 I am proud of this project, just hoping that over time more ppl find the value in it and we can all improve on it together. All I can ask in return is… spread the word – and post bugs/issues and requests in the github page 😉

  3. Hi Darran,

    Really impressive work! Can your software also be used for the following setup?

    Optical Termination Point->Optical Network Terminal (ONT)->Router->Raspberry Pi -> Apple TV (connecting to the Raspberry Pi by via WiFi)

    I unfortunately can’t insert anything between the ONT and the router since my Internet provider requires me to use their router and the ONT only recognises their router.

    1. Hey Tobias –

      First thought is that I think they ‘recognise’ their router is by it’s MAC address. If so, it’s possible to make the WAN side (ETH0) of the Raspberry Pi the same, you can try the following:

      1. Determine the MAC address of your provider router (WAN interface).
      2. SSH into the RPi, and modify the /etc/network/interfaces with hwaddress ether xx:xx:xx:xx:xx:xx in the ETH0 section – note the format and replace xx: portions with the desired MAC, it’ll be something like what I’ve put at the end of this response.
      3. Reboot the Raspberry Pi and provide equipment.
      4. Test!

      Let me know.

      Cheers.

      auto lo
      iface lo inet loopback

      #Onboard NIC connecting to the MODEM/CE (Internet)
      auto eth0
      iface eth0 inet dhcp
      hwaddress ether xx:xx:xx:xx:xx:xx
      post-up route del default gw 10.254.254.254
      post-up route add default dev eth0

      #USB NIC connecting to the Internal Router (DDwrt)
      auto eth1
      iface eth1 inet static
      address 10.254.254.254
      netmask 255.255.255.0
      network 10.254.254.0
      broadcast 10.254.254.255
      gateway 10.254.254.254
      post-up route add -net 192.168.1.0 netmask 255.255.255.0 gw 10.254.254.254 dev eth1

  4. Any chance of porting this to a smaller distribution of Linux, for instance the Moebius distribution, so everything can be installed on a 1 gig SDCard? I have a bunch of those around and I have no use for them…

    1. Hi. Had a quick look at it seems that Moebius is based on Debian so it should work without any modifications, and if modifications are required is think they’d be rather minor. DSVR core code is written in Python which is platform agnostic. Let me know f you give it a try and I’ll try assist if you run into any issues. Cheers.

  5. Hi Darran,

    Great work with the selective router. 🙂
    I was looking for a solution like this, so I’m intrigued. I wonder, is there any reason that the Pi needs to be between a modem and internal router, or could it be just behind a Modem/Router. Around here, DSL Routers are quite common, so it would be ideal if the link in between the devices could be avoided – or alternatively, if the Pi would act as the router itself.

    Do you see any chance for either of the two?

    Cheers
    Oliver

    1. “is there any reason that the Pi needs to be between a modem and internal router?”

      Yes, need a device upstream to provide media conversion to ethernet (from cable or DSL) and provide DCHP WAN address from ISP

      “or alternatively, if the Pi would act as the router itself.”

      You’d still need something upstream doing the media conversion as described above. Theoretically the RPi could become the replacement of the router, and I have considered this. The reasons I have not pursued this:
      >Performance is poor (slow USB bus for networking)
      >Does not have multiple ethernet ports like a typical router would
      >No built in 802.11x Wifi, general usb ones are low performance and low coverage.

      The “ultimate” solution in my mind is to take something established like DD-WRT/OpenWRT and add the DSVR functionality into a custom build and load it onto a consumer grade router (linksys, buffalo or similar). Problem is I’ve based this on Python, which is far simpler than doing in C which would be required to modify OpenWRT type solutions.

      Just wish I had more time to go for the “ultimate solution” 🙂

  6. Darran, this is amazing. I was looking for a solution like this one for a long time, and yours was the only one that worked as advertized. Your instructions are very easy to follow, and the Web configuration is a blessing.

    The only thing that didn’t work as expected was the hardering of the WAN port on the PI. The ShieldsUp page tells me that port 22 is still opened, and all other ports are simply closed but not stealth. Did I miss any step regarding how to go stealth on Eth0? Thanks a bunch,

    Julian

    1. I need to correct myself, since my statement above was incorrect: I was in a hotel behind NAT, so the open ports detected by ShieldsUp were not from my Raspberry Pi, but rather from the hotel’s router. I will test again once I have a public IP.The DSVR is working like a charm, and it’s amazing how you’ve put this project together, and how well you’ve documented it. Outstanding work.

  7. How does this work with the BBC iPlayer? Initially the dns request will be for the BBC.co.uk domain but the streams will come from other servers hosted under different domains?

    1. DSVR does follow CNAME re-directions typically used with CDNs. I have heard people report this is specifically functional with BBC iPlayer.

  8. Sorry for the additional questions but i’ve been trying to set this up for quite some time as well, but I’m running on different hardware. I’ve been digging through your code, and it seems like you are reading DNS queries, if DNS query matches a given domain, you call your script to add the route and then return the DNS response. Can you please confirm? I can’t write python but I can understand the logic. I think.

    Thanks

    Here’s what I have done so far, https://www.reddit.com/r/linuxadmin/comments/3j11vq/iptables_review_please_review_my_vpn/ I think it could add some value to your config.

Leave a comment