VyOS Policy Based Routing with OpenVPN
Make a selection of hosts use a vpn connection
UPDATE:
On newer versions og VyOS, it is not possible to have OpenVPN tunnels without certificate based authentication. This kills the possibility of using PIA without patching the image. I've created a new guide for the 1.4-rolling-202101300218
releade using Mullvad and WireGuard instead.
Sometimes you want some hosts on your network to use a vpn connection. This is where policy based routing comes in to play. Here is how i got it to work on VyOS 1.3 with PIA VPN.
This may work on the LTS releases of VyOS. I however need features only available in the 1.3.x releases, so results may vary. This config was successful on 1.3-rolling-202010260327
, but not on the newer 1.3-rolling-202011070217
as this version introduced bugs in OpenVPN.
Goals/functionality
- Have four different simultaneous OpenVPN connections to PIA spread out geographically.
- Use firewall groups to force specific hosts on a specific connection
- Pool the OpenVPN connections together in a load-balanced fashion so that it is possible for specific hosts to use all connections at once.
Setting it up
This configuration guide assumes you have experience in configuration of Vyatta based systems (EdgeOS, VyOS). We make use of openvpn, routing tables, firewall groups and policies to get this to work.
OpenVPN Connection
This is a fairly standard OpenVPN conection for PIA (unofficial). Note that the openvpn option --proto udp4
is required, even with protocol udp
.
# show interfaces openvpn
openvpn vtun101 {
authentication {
password xxxxxxx
username xxxxxxx
}
ddescription Norway
encryption {
cipher aes128
}
firewall {
in {
name WAN_IN
}
local {
name WAN_LOCAL
}
}
hash sha1
mode client
openvpn-option --fast-io
openvpn-option --route-nopull
openvpn-option "--ping-restart 60"
openvpn-option --persist-tun
openvpn-option --persist-key
openvpn-option "--ping 10"
openvpn-option "--proto udp4"
persistent-tunnel
protocol udp
remote-host no.privacy.network
remote-port 1198
tls {
ca-cert-file /config/auth/ca.rsa.2048.crt
crl-file /config/auth/crl.rsa.2048.pem
}
use-lzo-compression
}
I repeated this for a total of 4 tunnels (vtun101-104), where every tunnel uses a different PIA country.
Firewall groups
Next we create firewall groups for each vpn tunnel, designated by the country the tunnel terminates to. I chose to use VPN-PIA-XX, where XX is replaced by the two letter representation of the country. This is done for all 4 tunnels. Any hosts added to these groups will use that specific VPN connection.
Then we create a firewall group for the load balanced VPN group, for hosts that can use all connections at the same time.
set firewall group address-group VPN-PIA-LB
Routing tables
Next we set up our routing tables for these connections. To to this we need information on the vpn tunnels, so that we can find the gateway address on each tunnel.
$ ip route | grep vtun
10.7.112.0/24 dev vtun101 proto kernel scope link src 10.7.112.x
10.11.112.0/24 dev vtun102 proto kernel scope link src 10.11.112.x
10.25.112.0/24 dev vtun103 proto kernel scope link src 10.25.112.x
10.27.112.0/24 dev vtun104 proto kernel scope link src 10.27.112.x
We assume that the gateway is located at the first address of the subnet, so in the example of vtun101
the gateway is at 10.7.112.1
.
We apply this knowledge to create four routing tables (one for each VPN) 101-104, and also for the load balancing table at table 100.
Policy
Create a policy that modifies the routing table used by hosts found in our previously defined firewall groups.
# show policy
route VPN-PIA {
rule 1 {
description "Local destinations"
destination {
group {
network-group RFC1918
}
}
set {
table main
}
}
rule 100 {
description LoadBalance
destination {
address 0.0.0.0/0
}
set {
table 100
}
source {
group {
address-group VPN-PIA-LB
}
}
}
rule 101 {
description NO
destination {
address 0.0.0.0/0
}
set {
table 101
}
source {
group {
address-group VPN-PIA-NO
}
}
}
rule 102 {
description SE
destination {
address 0.0.0.0/0
}
set {
table 102
}
source {
group {
address-group VPN-PIA-SE
}
}
}
rule 103 {
description DK
destination {
address 0.0.0.0/0
}
set {
table 103
}
source {
group {
address-group VPN-PIA-DK
}
}
}
rule 104 {
description FI
destination {
address 0.0.0.0/0
}
set {
table 104
}
source {
group {
address-group VPN-PIA-FI
}
}
}
}
Apply the policy to packets coming in from eth1 (and any other interfaces where your clients may be located)
# set interfaces ethernet eth1 policy route VPN-PIA
Auto update gateway address
Since the PIA gateway addresses may change, i created a script to update the value once a minute if changes are detected. This script is a bit hacky, and I welcome you to send me any changes you made to it.
chmod +x /config/pbr.sh
set system task-scheduler task openvpn-pbr executable path /config/pbr.sh
set system task-scheduler task openvpn-pbr interval 5m
Killswitch
If you dont want these clients using your WAN connection directly if the internet is down, add a WAN_OUT rule for these clients:
# show firewall name WAN_OUT
default-action accept
rule 10 {
action accept
state {
established enable
related enable
}
}
rule 20 {
action reject
source {
group {
address-group VPN-PIA-NO
}
}
}
# repeat for all firewall groups
# show interfaces ethernet eth0 firewall
in {
name WAN_IN
}
local {
name WAN_LOCAL
}
out {
name WAN_OUT
}
Done!
Im now seeing downloads at my full 300mbit connection speed to my ISP over the load balanced connection (parallel downloads only).