I wound out replacing my existing router (which had a buggy NAT issue) with a TP-Link TL-WR1043ND running OpenWRT. It was pretty damned easy to get it all running and set it up as an in-place upgrade. However, I wanted more out of it.
What I want to do is to establish a VPN tunnel such that my VPS has some (highly restricted) access to my local network, and my local network has (nearly) unrestricted access to the VPS. I also want to have other devices (my phone) able to connect to my local network using VPN and have unrestricted access. And lastly, I want to do this with certificates (and not shared secrets). To do this, I used OpenVPN.
Desired Topology
- 10.0.0.0/24 – Internal LAN
- 10.0.2.0/24 – Trusted VPN
- 10.0.3.0/24 – Untrusted (DMZ) VPN
OpenWRT Configuration
Package Installation and TUN Configuration
First, run the following to install the required packages;
opkg update
opkg install openvpn-openssl
opkg install openvpn-easy-rsa
Once that’s done, edit /etc/config/network and add a declaration of a new TUN interface;
config interface 'vpn'
option proto 'none'
option ifname 'tun0'
Reboot your router, and you’ll find a new interface tun0 waiting. Now you need to set up your PKI infrastructure and generate some certs.
Configure PKI
Follow the installation instructions for easy-rsa. Once that’s done, you will have a functional self-signed CA. Go and generate some certificates like this;
build-key-server server
build-key trustedclient
build-key dmzclient
Take server.crt and server.key and copy them into the OpenVPN configuration;
cp /etc/easy-rsa/server.* /etc/openvpn/
mkdir /etc/openvpn/clients/
You’ve now got a basic PKI setup, and two client certificates ready to go, along with the server certificate for OpenVPN.
Configure OpenVPN Server
Edit /etc/config/openvpn like this;
package openvpn
config openvpn trusted_vpn
option enabled 1
option port 1194
option proto udp
option dev tun
option ca /etc/easy-rsa/keys/ca.crt
option cert /etc/openvpn/server.crt
option key /etc/openvpn/server.key
option dh /etc/easy-rsa/keys/dh2048.pem
option keepalive '10 120'
option comp_lzo yes
option persist_key 1
option persist_tun 1
option verb 3
option status /tmp/openvpn.status
option log /tmp/openvpn.log
option ccd_exclusive 1
option client_config_dir /etc/openvpn/clients
option server '10.0.2.0 255.255.255.0'
option route '10.0.3.0 255.255.255.0'
list push "route 10.0.0.0 255.255.255.0"
list push "dhcp-option DOMAIN localdomain.local"
list push "dhcp-option DNS 10.0.0.254"
You should disable the log once you’ve got everything working. What this does is the following;
- Clients must have a matching client config entry in /etc/openvpn/clients to be able to connect
- The server by default uses 10.0.2.0/24 for connecting clients, but also has a route for 10.0.3.0/24 down the TUN interface
- The server pushes a route to the client for 10.0.0.0/24, along with a couple of settings that are used by Windows clients (setting local domain and DNS servers to use)
For your trusted client, create a file /etc/openvpn/clients/trustedclient which has this line in it;
ifconfig-push 10.0.2.101 10.0.2.102
This causes the trusted client to always get the IP address 10.0.2.101. Note, as per the OpenVPN documentation, in order for the ifconfig-push’ed addresses to work with WIndows clients properly, they must come from the set;
[ 1, 2] [ 5, 6] [ 9, 10] [ 13, 14] [ 17, 18]
[ 21, 22] [ 25, 26] [ 29, 30] [ 33, 34] [ 37, 38]
[ 41, 42] [ 45, 46] [ 49, 50] [ 53, 54] [ 57, 58]
[ 61, 62] [ 65, 66] [ 69, 70] [ 73, 74] [ 77, 78]
[ 81, 82] [ 85, 86] [ 89, 90] [ 93, 94] [ 97, 98]
[101,102] [105,106] [109,110] [113,114] [117,118]
[121,122] [125,126] [129,130] [133,134] [137,138]
[141,142] [145,146] [149,150] [153,154] [157,158]
[161,162] [165,166] [169,170] [173,174] [177,178]
[181,182] [185,186] [189,190] [193,194] [197,198]
[201,202] [205,206] [209,210] [213,214] [217,218]
[221,222] [225,226] [229,230] [233,234] [237,238]
[241,242] [245,246] [249,250] [253,254]
Next, we repeat this with the DMZ client, but with a small change – we’ll also push the route for the trusted network. This is required because otherwise the router itself cannot contact anything in the DMZ due to the router’s interface IP being in the 10.0.2.0/24 range.
Create /etc/openvpn/clients/dmzclient as follows;
ifconfig-push 10.0.3.101 10.0.3.102
push route "10.0.2.0 255.255.255.0"
That concludes the OpenVPN server config. Now for the firewall.
Firewall Configuration
OpenVPN Access Port
Edit /etc/config/firewall . Up fairly high, amongst any other port forwards, add the following;
config rule
option name 'OpenVPN-Access'
option src wan
option proto udp
option dest_port 1194
option family ipv4
option target ACCEPT
This allows your VPN to be accessible from the ‘net.
Full-Access VPN Zone
Next, we’ll create a new zone for the full-access VPN, but we’ll specify that it only applies to a specific subnet;
config zone
option name vpn
option input ACCEPT
option forward REJECT
option output ACCEPT
list network vpn
list subnet '10.0.2.0/24'
option masq 0
option mtu_fix 1
config forwarding
option dest lan
option src vpn
config forwarding
option dest vpn
option src lan
DMZ VPN Zone
And lastly we’ll define a new zone for the DMZ VPN along with allowed traffic. Note that this does not specify a subnet, that way any traffic that comes in on the tun network that does not match the trusted VPN falls through to the DMZ zone.
config zone
option name dmz
option input REJECT
option forward REJECT
option output ACCEPT
list network vpn
option masq 0
option mtu_fix 1
config rule
option name 'http-Linkage'
option src dmz
option dest lan
option proto tcp
option src_ip 10.0.3.101
option dest_port 80
option dest_ip 10.0.0.15
option family ipv4
option target ACCEPT
config rule
option name Allow-DMZ-Ping
option src dmz
option dest lan
option proto icmp
option icmp_type echo-request
option family ipv4
option target ACCEPT
config forwarding
option dest dmz
option src lan
Notably, there is only a forward here from lan to dmz, that way we have to specifically allow what traffic we want to be passed from dmz to lan. Here, I allow the DMZ machine to ping into my local network, and to connect to 10.0.0.15 on port tcp/80 only.
OpenVPN Server Summary
Essentially, what we’ve done is create two network zones which are pushed out by OpenVPN. The firewall controls access between them. Which user certificate lands in which zone is determined by the IP they’re assigned by OpenVPN on connect, which is defined by the configuration file in /etc/openvpn/clients. All clients must have a config file waiting for them in this setup.
IMPORTANT – Don’t forget to add /etc/openvpn and /etc/easy-rsa to your /etc/sysupgrade.conf, otherwise you’ll lose all this on upgrading your router. That would be unfortunate.
OpenVPN Client Setup (DMZ Client)
You will require the following components to configure your client;
- /etc/easy-rsa/keys/ca.crt
- /etc/easy-rsa/keys/dmzclient.crt
- /etc/easy-rsa/keys/dmzclient.key
Download those off your router and store them somewhere. Then, you’ll need to create a client.ovpn file in the same folder as follows;
client
dev tun
remote yourvpnserverhostname 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ns-cert-type server
ca /etc/openvpn/ca.crt
cert /etc/openvpn/dmzclient.crt
key /etc/openvpn/dmzclient.key
comp-lzo
route-nopull
route 10.0.0.0 255.255.255.0
route 10.0.2.0 255.255.255.0
In my case, the files are in /etc/openvpn, as the config shows. Notably, this config also does not pull route data from the OpenVPN server but instead sets it by itself. Then, start your VPN with
openvpn --config client.ovpn
And it should all work! If not, consult the logs, and good luck!