Sharing my VPN client router recipe

this document describes all steps needed to: setup a VPN tunnel between a home router and a VPS, then route entire house's internet traffic through VPN.

as to routing, it's very helpful to keep normal internet access for guest/emergency/etc. so it's better to set up a dedicated LAN, and specifically route traffic originated from this LAN through VPN, instead of routing all traffic through VPN.

the hardware and software used are: a thinkpad with ubuntu 16.04, a DigitalOcean $5 droplet with Debian 9.7, GL.iNet 6416 travel router, OpenWrt 18.06.2, OpenVPN 2.4.

Prepare router

Install OpenWrt

go to openwrt project website, locate the specific device page on the "Table of Hardware" page, then follow instructions to flash openwrt firmware to router.

Basic configuration

  • login with ssh

      foo@laptop$ ssh root@192.168.1.1
      foo@laptop$ ssh root@openwrt
    
  • set root password

      root@wrt# passwd
    
  • customize hostname. edit /etc/config/system:

      config system
      +	option hostname 'wrt'
    
  • customize ntp upstream. edit /etc/config/system:

      config timeserver 'ntp'
      +	list server '0.pool.ntp.org'
      +	list server '1.pool.ntp.org'
      +	list server '2.pool.ntp.org'
      +	list server '3.pool.ntp.org'
    
  • disable IPv6 to avoid IPv6 traffic bypassing VPN. edit /etc/config/network:

      config interface 'wan6'
      +	option auto '0'
    
  • set country code for wifi radio. edit /etc/config/wireless:

      config wifi-device 'radio0'
      +	option country 'US'
    
  • disable the web admin GUI and start it manually when needed

      root@wrt# /etc/init.d/uhttpd disable
    

Network configuration

Configure lan and wlan

by default, the wired and wireless LAN are bridged together as one network. first unbridge them back to two networks: lan, with address 192.168.1.0/24, and wlan, with address 192.168.2.0/24. then adjust firewall settings so that only lan network has admin access to router.

  • configure interfaces. edit /etc/config/network:

      config interface 'lan'
      -	option type 'bridge'
    
      + config interface 'wlan'
      +	option proto 'static'
      +	option ipaddr '192.168.2.1'
      +	option netmask '255.255.255.0'
    
  • configure the existing wifi-iface. edit /etc/config/wireless:

      + config wifi-iface 'wlan'
      	option device 'radio0'
      +	option network 'wlan'
      	option mode 'ap'
      +	option ssid 'BozoNet'
      +	option encryption 'psk2+ccmp'
      +	option key 'topsecret'
    
  • define new DHCP pool. edit /etc/config/dhcp:

      + config dhcp 'wlan'
      +	option interface 'wlan'
      +	option start '100'
      +	option limit '150'
      +	option leasetime '12h'
    
  • adjust firewall settings. edit /etc/config/firewall:

      + config zone
      +	option name wlan
      +	list network 'wlan'
      +	option input REJECT
      +	option output ACCEPT
      +	option forward REJECT
    
      + config rule
      +	option name Allow-DNS-DHCP
      +	option src wlan
      +	option proto 'tcp udp'
      +	option dest_port '53 67'
      +	option target ACCEPT
    
      + config forwarding
      +	option src wlan
      +	option dest wan
    

Configure guest wlan

create a guest wireless network: guest, with address 192.168.4.0/24.

  • define new interface. edit /etc/config/network:

      + config interface 'guest'
      +	option proto 'static'
      +	option ipaddr '192.168.4.1'
      +	option netmask '255.255.255.0'
    
  • define new wireless network. edit /etc/config/wireless:

      + config wifi-iface 'guest'
      +	option device 'radio0'
      +	option network 'guest'
      +	option mode 'ap'
      +	option ssid 'BozoNet'
      +	option encryption 'psk2+ccmp'
      +	option key 'topsecret'
    
  • define new DHCP pool. edit /etc/config/dhcp:

      + config dhcp 'guest'
      +	option interface 'guest'
      +	option start '50'
      +	option limit '200'
      +	option leasetime '1h'
    
  • adjust firewall settings. edit /etc/config/firewall:

      + config zone
      +	option name guest
      +	list network 'guest'
      +	option input REJECT
      +	option output ACCEPT
      +	option forward REJECT
    
      + config rule
      +	option name Allow-DNS-DHCP
      +	option src guest
      +	option proto 'tcp udp'
      +	option dest_port '53 67'
      +	option target ACCEPT
    
      + config forwarding
      +	option src guest
      +	option dest wan
    

Configure vpn-bound wlan

create a separate network for vpn usage: han, with address 192.168.8.0/24.

  • define new interface. edit /etc/config/network:

      + config interface 'han'
      +	option proto 'static'
      +	option ipaddr '192.168.8.1'
      +	option netmask '255.255.255.0'
    
  • define new wireless network. edit /etc/config/wireless:

      + config wifi-iface 'han'
      +	option device 'radio0'
      +	option network 'han'
      +	option mode 'ap'
      +	option ssid 'BozoNet'
      +	option encryption 'psk2+ccmp'
      +	option key 'topsecret'
    
  • define new DHCP pool. edit /etc/config/dhcp:

      + config dhcp 'han'
      +	option interface 'han'
      +	option start '100'
      +	option limit '150'
      +	option leasetime '12h'
    
  • adjust firewall settings. edit /etc/config/firewall:

      + config zone
      +	option name han
      +	list network 'han'
      +	option input REJECT
      +	option output ACCEPT
      +	option forward REJECT
    
      + config rule
      +	option name Allow-DNS-DHCP
      +	option src han
      +	option proto 'tcp udp'
      +	option dest_port '53 67'
      +	option target ACCEPT
    

Enable wifi

edit /etc/config/wireless:

config wifi-device 'radio0'
-	option disabled '1'

Restart router

root@wrt# reboot

Prepare VPS

  • create VPS instance. follow instructions on vendor's documentation.

  • login with ssh. for convenience, use /etc/hosts alias.

      foo@laptop$ ssh root@vps
    
  • configure automatic security update

      root@vps# apt-get update
      root@vps# apt-get install unattended-upgrades
      root@vps# dpkg-reconfigure unattended-upgrades
    

Setup VPN

Install OpenVPN

  • install openvpn (vps)

      root@vps# apt-get update
      root@vps# apt-get install openvpn
    
  • install openvpn (wrt)

      root@wrt# opkg update
      root@wrt# opkg install openvpn-openssl
    

Configure OpenVPN

  • build secret key

      foo@laptop$ sudo apt-get update
      foo@laptop$ sudo apt-get install openvpn
      foo@laptop$ openvpn --genkey --secret tunnel.key
      foo@laptop$ scp tunnel.key root@vps:/etc/openvpn/
      foo@laptop$ scp tunnel.key root@wrt:/etc/openvpn/
      foo@laptop$ rm tunnel.key
    
  • configure openvpn (vps). on vps, create /etc/openvpn/tunnel.conf:

      proto udp4
      port 1194
    
      dev tun0
      ifconfig 10.8.0.1 10.8.0.2
    
      secret tunnel.key 0
      cipher AES-128-CBC
      auth SHA1
      ncp-disable
    
      user nobody
      group nogroup
      persist-key
      persist-tun
      disable-occ
      verb 3
    
  • configure openvpn (wrt). on wrt, create /etc/openvpn/tunnel.conf:

      proto udp4
      nobind
    
      remote VPS_IP 1194
    
      dev tun0
      ifconfig 10.8.0.2 10.8.0.1
    
      secret tunnel.key 1
      cipher AES-128-CBC
      auth SHA1
      ncp-disable
    
      user nobody
      group nogroup
      persist-key
      persist-tun
      auth-nocache
      disable-occ
      verb 3
    
  • test connectivity

      root@vps:/etc/openvpn# openvpn tunnel.conf
      root@wrt:/etc/openvpn# openvpn tunnel.conf
      root@wrt# ping -c4 10.8.0.1
      root@vps# ping -c4 10.8.0.2
    
  • adjust firewall (wrt). on wrt, edit /etc/config/firewall:

      + config zone
      +	option name vpn
      +	list device 'tun0'
      +	option input REJECT
      +	option output ACCEPT
      +	option forward REJECT
    
      + config forwarding
      +	option src han
      +	option dest vpn
    
  • workaround the timestamp bug (wrt). on wrt, edit /etc/init.d/sysfixtime:

      stop() {
      +	touch /etc/banner
      }
    

Start OpenVPN

after reboot, both peer should auto start by default. ip addr should output information about tun0 device.

Route internet traffic through VPN

tell wrt to route specific internet traffic through VPN, then let vps forward such traffic to the internet.

Configure routing on vps

configure vps as an upstream NAT router

  • enable IP forwarding. edit /etc/sysctl.conf:

      + net.ipv4.ip_forward=1
    
  • add a route to han network. edit /etc/openvpn/tunnel.conf:

      + route 192.168.8.0 255.255.255.0
    
  • enable masquerading

      root@vps# iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
      root@vps# iptables -t nat -A POSTROUTING -s 192.168.8.0/24 -o eth0 -j MASQUERADE
      root@vps# apt-get install iptables-persistent
    
  • verify config

      root@vps# reboot
      root@vps# sysctl net.ipv4.ip_forward
      root@vps# ip route
      root@vps# iptables-save -t nat
    

Configure routing on wrt

keep the default routing behavior, so internet traffic is routed through WAN. then override this with more specific rules, so internet traffic from han network is routed through VPN instead.

create two default routes, one towards VPN and one towards WAN. put them each in a separate routing table, then define routing rules that selects the correct table (route) during routing.

  • define new routing tables. edit /etc/iproute2/rt_tables:

      + 10	wan
      + 11	vpn
    
  • move the existing default route to wan table. edit /lib/netifd/dhcp.script:

      - proto_add_ipv4_route 0.0.0.0 0 "$i" "$ip"
      + append PROTO_ROUTE "0.0.0.0/0/$i///$INTERFACE/$ip"
    
  • add a catch-all route to wan table. edit /etc/config/network:

      + config route
      +	option name 'wan-catch-all'
      +	option type 'unreachable'
      +	option target '0.0.0.0/0'
      +	option interface 'loopback'
      +	option metric '100'
      +	option table 'wan'
    
  • add default route to vpn table

    edit /etc/openvpn/tunnel.conf:

      + script-security 2
      + up tunnel.up
    

    create /etc/openvpn/tunnel.up:

      #!/bin/sh
      ip route add default via $ifconfig_remote dev $dev src $ifconfig_local table vpn
    

    make it executable:

      root@wrt# chmod +x /etc/openvpn/tunnel.up
    
  • add a catch-all route to vpn table. edit /etc/config/network:

      + config route
      +	option name 'vpn-catch-all'
      +	option type 'unreachable'
      +	option target '0.0.0.0/0'
      +	option interface 'loopback'
      +	option metric '100'
      +	option table 'vpn'
    
  • define routing rules. edit /etc/config/network:

      + config rule
      +	option name 'HAN-via-VPN'
      +	option priority '40100'
      +	option in 'han'
      +	option lookup 'vpn'
    
      + config rule
      +	option name 'All-via-WAN'
      +	option priority '40200'
      +	option lookup 'wan'
    
  • verify config

      root@wrt# reboot
      root@wrt# ip route show table main
      root@wrt# ip route show table wan
      root@wrt# ip route show table vpn
      root@wrt# ip rule
    

Test routing

check IP address at icanhazip.com. device on lan/wlan/guest should show WAN IP, device on han should show vps IP.

Prevent DNS leaks

dnsmasq provides DNS service to local networks. by default, it forwards queries to ISP nameserver through WAN. this is usually fine, but can leak queries from han network. to prevent leaks, create a second dnsmasq instance that forwards to a trusted nameserver through VPN. then, configure this instance to serve han exclusively.

  • configure the existing dnsmasq. edit /etc/config/dhcp:

      + config dnsmasq 'main'
      	option domainneeded '1'
      	option boguspriv '1'
      	option filterwin2k '0'
      	option localise_queries '1'
      	option rebind_protection '1'
      	option rebind_localhost '1'
      	option local '/lan/'
      	option domain 'lan'
      	option expandhosts '1'
      	option nonegcache '0'
      	option authoritative '1'
      	option readethers '1'
      +	option leasefile '/tmp/dhcp.leases.main'
      	option resolvfile '/tmp/resolv.conf.auto'
      	option nonwildcard '1'
      	option localservice '1'
      +	list interface 'lan'
      +	list interface 'wlan'
      +	list interface 'guest'
    
      config dhcp 'lan'
      +	option instance 'main'
      	option ...
    
      config dhcp 'wlan'
      +	option instance 'main'
      	option ...
    
      config dhcp 'guest'
      +	option instance 'main'
      	option ...
    
  • find trusted nameserver. the nameserver from VPS provider is fine. on vps, find nameserver address in resolv.conf:

      root@vps# cat /etc/resolv.conf
    
  • create a second dnsmasq instance. edit /etc/config/dhcp:

      + config dnsmasq 'secure'
      +	option domainneeded '1'
      +	option boguspriv '1'
      +	option filterwin2k '0'
      +	option localise_queries '1'
      +	option rebind_protection '1'
      +	option rebind_localhost '1'
      +	option local '/lan/'
      +	option domain 'lan'
      +	option expandhosts '1'
      +	option cachesize '500'
      +	option nonegcache '0'
      +	option authoritative '1'
      +	option readethers '1'
      +	option leasefile '/tmp/dhcp.leases.secure'
      +	option noresolv '1'
      +	list server 'NS1@tun0'	<- insert trusted
      +	list server 'NS2@tun0'	<- nameserver here
      +	option nonwildcard '1'
      +	option localservice '1'
      +	list interface 'han'
      +	list notinterface 'lo'
    
      config dhcp 'han'
      +	option instance 'secure'
      	option ...
    
  • workaround the bind-fight bug during boot. edit /etc/init.d/dnsmasq:

      dnsmasq_start()
      -	[ -n "$BOOT" ] || {
      	config_list_foreach "$cfg" "interface" append_interface
      	config_list_foreach "$cfg" "notinterface" append_notinterface
      -	}
    
  • verify config

      root@wrt# reboot
      root@wrt# logread
      root@wrt# less /var/etc/dnsmasq.conf.main
      root@wrt# less /var/etc/dnsmasq.conf.secure
      root@wrt# netstat -atunp
    
  • test DNS leak. check result at dnsleaktest.com. device on lan/wlan/guest should show ISP name, device on han should show VPS provider name.

Finish

setup is complete. now all devices from han network will access internet through VPN. meanwhile the lan/wlan/guest network will still have normal internet access, however lan should only be used for router administration.

3 Likes

Here are the links I learned from:






https://www.mail-archive.com/openvpn-users@lists.sourceforge.net/msg03394.html
https://www.mail-archive.com/openvpn-users@lists.sourceforge.net/msg03776.html
https://openmaniak.com/openvpn.php
https://help.ubuntu.com/lts/serverguide/openvpn.html
https://wiki.debian.org/OpenVPN



https://blog.cavebeat.org
https://wiki.archlinux.org/index.php/OpenVPN
http://dcamero.azurewebsites.net/openvpn-obfsproxy.html

http://www.thekelleys.org.uk/dnsmasq/docs/


http://www.rjsystems.nl/en/2100-adv-routing.php
http://www.tldp.org/HOWTO/Adv-Routing-HOWTO/lartc.rpdb.multiple-links.html
http://www.cyber.com.au/~twb/doc/dual-uplink.txt

https://www.agwa.name/blog/post/hardening_openvpn_for_def_con




http://greycoder.com/

https://prism-break.org/en/


http://www.sbprojects.net/projects/raspberrypi/tor.php

http://www.gl-inet.com/
http://enochma.com/page/3/
http://www.jianshu.com/p/2f51144c35c9
http://www.figotan.org/2016/05/04/cook-your-own-vpn/
http://shadowsocks.blogspot.sg
http://hyspace.io/posts/2015/08/22/纪念shadowsocks/
https://biergaizi.info/archives/2013/02/1621.html
https://sodatea.github.io/2015/12/23/all-i-know-about-the-wall/

http://igfw.net/archives/12556
https://www.raspberrypi.org/forums/viewtopic.php?f=36&t=135170
http://dcamero.azurewebsites.net/shadowsocks-ubuntu-1510-windows.html
https://scramblevpn.wordpress.com
https://plus.google.com/+GhostAssassin/posts/TtWFAQmSMVE
https://www.zhihu.com/question/28252105/answer/53481328

by the way, unfortunately I end up keep my GL.iNet 6416 router as an emergency router only because its not stable enough. during my initial testing i get 8 mbps speed with vpn, but after couple weeks the speed drops to less than 3~4 mbps without vpn (feels like dying), i really wish it is more robust :cry: .

hope i can help somebody here. openwrt is awesome.

free china, free taiwan, free hong kong, free tibet, June 4th, 1989

1 Like

Doesn't your VPS vendor provide native IPv6?

Can't you use the table option in the wan interface settings?

good to see you, i've learned some of your posts before!

for ipv6, I don't know enough to handle ipv6 currently so i leave it to another day.

for table option, i tried but that moved all routes of that interface, looks a bit ugly

Fabulous. For those of us who do not need a VPS, can we use your recipe to connect to a commercial vpn, say expressVPN....

Thanks again.

Hi, thanks for the write up, really informative. I was wondering if you've tried Zerotier one for the tunnel?

If you're running your own VPN server anyways, you may find out that switching from OpenVPN to the wireguard (both on your VPS and router) will provide significant speed boost.