There are lots of (mostly incomplete) instructions on how to setup NordVPN on OpnSense scattered all over the forum. Also, there are instructions from NordVPN (https://support.nordvpn.com/hc/en-us/articles/20397569418129-OPNsense-21-setup-with-NordVPN) themselves, but they do not focus on Wireguard.
Also, I found that my setup failed a few days ago, because NordVPN obviously cancelled one of their rented servers (de1019.nordvpn.com) which happened to be the one I used until then.
Therefore, I took the effort to create a setup with a failover for a fixed set of clients. This setup also will behave as a "kill switch", i.e. that no client targeted by it can bypass the NordVPN setup and expose itself accidentally.
1. You have to create not one, but two so-called "Access Token" in the NordVPN customer dashboard by clicking on NordVPN on the left. Then, the link to enter the access token menu will be at the bottom of the page. Create to different tokens and be sure to note them, because they will not be accessible later on.
2. Once you have those tokens, you will need to create a private key for both of them. This can be done via 'curl -s -u token:<YOUR_TOKEN_HERE> https://api.nordvpn.com/v1/users/services/credentials | jq -r .nordlynx_private_key'.
3. For entry into WG instances, you will also need the corresponding public keys, which you can get via 'echo <YOUR_PRIVATE_KEY_HERE> | wg pubkey'.
4. If you followed along, you should now be in posession of two private/public key pairs pertaining to your NordVPN account. These are neccessary to discriminate between the two connections, you cannot do this using only one key pair.
5. Using these informations, you can now add two Wireguard instances under VPN:Wireguard->Instances. Add these as "NORDVPN_INSTANCE1" and "NORDVPN_INSTANCE2", use different, free "Listen Ports" (these can be arbitrary, but must not be used anywhere else) and 10.5.0.2/16 as "Tunnel address" (yes, both are the same!).
6. Obtain two NordVPN server names from their API via 'curl -s "https://api.nordvpn.com/v1/servers/recommendations?&filters\[servers_technologies\]\[identifier\]=wireguard_udp&limit=2" | jq -r ".[]|.hostname"'. This will give you two names like "xx999.nordvpn.com".
7. Next, create two Wireguard peers under VPN:Wireguard->Peers. Name them NORDVPN_PEER1 and NORDVPN_PEER2, and choose different NordVPN server names obtained in the previous step for bot peers (e.g. 'deXXX.nordvpn.com'). Use "public key" = "m0tej5P6pYfBivkJc8yRV4KqQXmM81AChLlzlsOSjSs=", "Allowed IPs" = "0.0.0.0/0", "Endpoint port" = 51820, "Keepalive Interval" = 30. Choose NORDVPN_INSTANCE1 as Wireguard instance for NORDVPN_PEER1 and NORDVPN_INSTANCE2 for NORDVPN_PEER2 - use only one per each peer!
8. Next, you can check VPN:WireGuard:Status. You should see both the peer and the instance come up as green for both instance/peer pairs if Wireguard is enabled.
9. Now, assign those two Wireguard interfaces names via Interfaces:Assignments. Name them NORDVPN1 and NORDVPN2. Select each of them under Interfaces and check the "Enable Interface" and "Prevent Interface Removal" boxes.
10. Create an alias with the definition of all clients, that should go through NordVPN only. Name that NORDVPN_CLIENTS. I prefer to use a network group alias that includes MAC aliases for individual machines, see also the hint about IPv6 at the end of this article.
11. Create two SNAT rules for each of the two NordVPN interfaces under Firewall:NAT:Source NAT with:
Description = "NAT for NordVPN"
Interface = NORDVPN1 / NORDVPN2
Version = IPv4
Protocol = any
Source Address = NORDVPN_CLIENTS
Translate Source IP = leave empty
12. Create two Gateways under System:Gateways:Configuration with
Name = NORDVPN_GW1
Interface = NORDVPN1
Address Family = IPv4
IP Address = 10.5.0.1
Far Gateway = checked
Failover States = checked
Disable Gateway Monitoring = unchecked
Monitor IP = 103.86.96.100
Description = IPv4 Gateway for NordVPN
Name = NORDVPN_GW2
Interface = NORDVPN2
Address Family = IPv4
IP Address = 103.86.99.100
Far Gateway = checked
Disable Gateway Monitoring = unchecked
Monitor IP = 103.86.99.100
Description = IPv4 Gateway for NordVPN
(Note: The "IP address" of 103.86.99.100 in this second entry is not an error, but a trick to make this work!)
Leave everything else on default.
13. Create a gateway group under System:Gateways:Group named NORDVPN_FAILOVER with "Trigger Level" = "Member Down", having NORDVPN_GW1 as Tier 1 and NordVPN_GW2 as Tier 2, with anything else as tier "Never". After having them created, both gateways should be listed as active (green) here.
14. Finally, create a firewall rule under Firewall:Rules[new] (it should preceede most other rules):
Description = Force gateway for NordVPN clients
Interface = any
Quick = checked
Action = Pass
Direction = In
Version = IPv4
Protocol = any
Invert Source = unchecked
Source = NORDVPN_CLIENTS
Source Port = any
Invert Destination = checked
Destination = RFC1918
Destination Port = any
Gateway = NORDVPN_FAILOVER
This assumes that you have an RFC1918 alias. You could also use an inverted firewall group containing all of your local networks. What this is supposed to mean is "internet traffic", with the exception of "local traffic", should you want to access your NORDVPN_CLIENTS from any of your local networks.
If you want to try the failover, disable the first Wireguard peer. You will see the gateway failing and after a few seconds, the client traffic will go over the second connection. Re-enable the peer and soon afterwards, the traffic will be routed over the first connection again.
You can verify this when you check the change of your visible external IP with e.g. https://www.whatismyip.com/ from one of your NordVPN clients.
Note: This setup only handles IPv4! Currently, NordVPN does not support IPv6 anyways. If you want to make sure that your NordVPN clients will not expose themselves via IPv6, disable it completely on those clients or block IPv6 completely via a firewall rule for NORDVPN_CLIENTS (hint: you can use the MAC adresses instead of IPs to define the alias!).
P.S.: By default, OPNsense can take up to 60 seconds to detect a gateway failure and switch over. To make the failover happen faster, apply this adjustment:
Go to System:Gateways:Configuration, edit both gateways (`NORDVPN_GW1` and `NORDVPN_GW2`), scroll down, click "Advanced" under *Weight / Intervals* and change the values for both gateways to:
- "Ping Interval" = 1 (pings every second)
- "Time Period" = 3 (calculates average over 3 seconds)
- "Down Interval" = 3 (marks the gateway as down after 3 missed pings)