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 onlan/wlan/guest
should show ISP name, device onhan
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.