TUTORIAL: Set up WireGuard for limited local hosts to use external VPN provider

Started by Greelan, January 31, 2021, 12:32:41 AM

Previous topic - Next topic
UPDATE: This tutorial has now been included in the official OPNsense documentation. Please submit any updates or improvements there (via GitHub).

UPDATE #2 28 March 2021: This tutorial has been updated to remove reference to including the VPN provider's DNS servers in the Local configuration, as this can break DNS resolution on OPNsense itself. Also, if your network generally uses local DNS servers, you will likely experience DNS leaks unless you take further steps. Suggested solutions are proposed to be added to the official OPNsense documentation.


This tutorial is designed to assist with setting up WireGuard on OPNsense to connect only limited (not all) local hosts to an external VPN provider.

These circumstances may apply where only certain local hosts are intended to use the VPN tunnel. Or it could apply where multiple connections to the VPN provider are desired, with each connection intended to be used by different specific local hosts.

This tutorial draws heavily on the great work of @Jonny, in particular as shown here: https://m.imgur.com/gallery/JBf2RF6

This tutorial focuses on the configuration of OPNsense. You will also have to configure the peer at your VPN provider - consult your VPN provider's documentation as to how to do that.

Your OPNsense local public key will need to be registered with your VPN provider, and you will need to get your VPN provider's endpoint public key and the VPN tunnel IP provided for your local peer by your VPN provider. In some cases, you will not be able to get the endpoint public key and VPN tunnel IP until you register your local public key. In that case, create the OPNsense local configuration first, using a dummy tunnel IP and no peer selected, so that the public key is generated, and then update the configuration later once the other information is known.

This tutorial discusses IPv4 configuration only. It can be readily adapted for IPv6 as well.

Configure the endpoint

Go to VPN -> WireGuard -> Endpoints
Click + to add a new Endpoint
Configure the Endpoint as follows (if an option is not mentioned below, leave it as the default):

   Enabled: Checked
   Name: Call it whatever you want (eg VPNProviderName_Location)
   Public Key: Insert the public key from your VPN provider
   Allowed IPs: 0.0.0.0/0
   Endpoint Address: Insert the public IP address (desirably) or domain name of your VPN provider, as provided by it
   Endpoint Port: Insert the port of your VPN provider, as provided by it
   Keepalive: 25

Save the Endpoint configuration, and then click Save again

Configure the local peer

Go to VPN -> WireGuard -> Local
Click + to add a new Local configuration
Turn on "advanced mode"
Configure the Local configuration as follows (if an option is not mentioned below, leave it as the default):

   Enabled: Checked
   Name: Call it whatever you want (eg VPNProviderName)
   Public Key: This will initially be blank; it will be populated once the configuration is saved
   Private Key: This will initially be blank; it will be populated once the configuration is saved
   Listen Port: 51820 or a higher numbered unique port
   DNS Server: Leave this blank
   Tunnel Address: Insert the VPN tunnel IP provided by your VPN provider, in CIDR format, eg 10.24.24.10/32
   Peers: In the dropdown, select the Endpoint you configured above
   Disable Routes: Checked
   Gateway: Specify an IP that is 1 number below your VPN tunnel IP, eg 10.24.24.9. Note that the IP you choose is essentially arbitrary; pretty much any unique IP will do. The suggestion here is for convenience and to avoid conflicts

Save the local peer configuration, and then click Save again

Turn on WireGuard

Turn on WireGuard under VPN -> WireGuard -> General if it is not already on

Assign an interface to WireGuard and enable it

Go to Interfaces -> Assignments
In the dropdown next to "New interface:", select the WireGuard device (wg0 if this is your first one)
Add a description (eg WAN_VPNProviderName)
Click + to add it, then click Save

Then select your new interface under the Interfaces menu
Configure it as follows (if an option is not mentioned below, leave it as the default):

   Enable: Checked
   Lock: Checked if you wish to
   Description: Same as under Assignments, if this box is not already populated
   IPv4 Configuration Type: None
   IPv6 Configuration Type: None

Save the interface configuration and then click Apply changes

Restart WireGuard

Now restart WireGuard - you can do this from the Dashboard (if you have the services widget) or by turning it off and on under VPN -> WireGuard -> General

Create a gateway

Go to System -> Gateways -> Single
Click Add
Configure the gateway as follows (if an option is not mentioned below, leave it as the default):

   Name: Call it whatever you want, easiest to name it the same as the interface
   Description: Add one if you wish to   
   Interface: Select your newly created interface in the dropdown
   Address Family: Select IPv4 in the dropdown
   IP address: Insert the gateway IP that you configured under the WireGuard local peer configuration
   Far Gateway: Checked
   Monitor IP: Insert an external IP to monitor the gateway, such as 1.1.1.1 or 8.8.8.8

Save the gateway configuration and then click Apply changes

Create an Alias for the relevant local hosts that will access the tunnel

Go to Firewall -> Aliases
Click + to add a new Alias
Configure the Alias as follows (if an option is not mentioned below, leave it as the default):

   Enabled: Checked
   Name: Call it whatever your want, eg WG_VPN_Hosts
   Type: Select either Host(s) or Network(s) in the dropdown, depending on whether you want specific host IPs to use the tunnel, or an entire local network (such as a VLAN)
   Content: Enter the host IPs, or the network in CIDR format
   Description: Add one if you wish to

Save the Alias, and then click Apply

Create a firewall rule

This will involve two steps - first creating a second Alias for all local (private) networks, and then creating the firewall rule itself. The ultimate effect of these two steps is that only traffic from the relevant hosts that is destined for non-local destinations will be sent down the tunnel. This will ensure that the relevant hosts can still access local resources

First go to Firewall -> Aliases
Click + to add a new Alias
Configure the Alias as follows (if an option is not mentioned below, leave it as the default):

   Enabled: Checked
   Name: RFC1918_Networks
   Type: Select Network(s) in the dropdown
   Content: 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
   Description: All local (RFC1918) networks

Save the Alias, and then click Apply

Then go to Firewall -> Rules -> [Name of the interface for the network in which the hosts/network resides, eg LAN for LAN hosts]
Click Add to add a new rule
Configure the rule as follows (if an option is not mentioned below, leave it as the default):

   Action: Pass
   Quick: Checked
   Interface: Whatever interface you are configuring the rule on
   Direction: in
   TCP/IP Version: IPv4
   Protocol: any
   Source / Invert: Unchecked
   Source: Select the relevant hosts Alias you created above in the dropdown (eg WG_VPN_Hosts)
   Destination / Invert: Checked
   Destination: Select the RFC1918_Networks Alias you created above in the dropdown
   Destination port range: any
   Description: Add one if you wish to   
   Gateway: Select the gateway you created above (eg WAN_VPNProviderName)

Save the rule, and then click Apply Changes

Then make sure that the new rule is above any other rule on the interface that would otherwise interfere with its operation. For example, you want your new rule to be above the "Default allow LAN to any rule"

Create an outbound NAT rule

Go to Firewall -> NAT -> Outbound
Select "Hybrid outbound NAT rule generation" if it is not already selected, and click Save and then Apply changes
Click Add to add a new rule
Configure the rule as follows (if an option is not mentioned below, leave it as the default):

   Interface: Select the interface for your WireGuard VPN (eg WAN_VPNProviderName)
   TCP/IP Version: IPv4
   Protocol: any
   Source invert: Unchecked
   Source address: Select the Alias for the hosts/networks that are intended to use the tunnel (eg WG_VPN_Hosts)
   Source port: any
   Destination invert: Unchecked
   Destination address: any
   Destination port: any
   Translation / target: Interface address
   Description: Add one if you wish to

Save then rule, and then click Apply changes

You should now be done!

Note that @Jonny's tutorial (linked above) also include instructions for the optional step of adding a kill switch - so that if the VPN tunnel is down, the relevant hosts will be blocked from using your normal WAN

Great guide best one I've seen yet for WireGuard VPN Providers! I have one recommendation on the firewall rule.

Create a Second ALIAS called RFC1918, and put RFC1918 networks (192.168.0.0/16.10.0.0.0/8,172.16.0.0/12) in it. Then use it in the Firewall rule Destination

Create a firewall rule

   Destination / Invert: Checked
   Destination: RFC1918
   Destination port range: any

This will make it so only internet traffic will go over the VPN, making any other local traffic not be interfered with by the VPN gateway, this can then be blocked or allowed by any rules after it.
Adventuring through internet pipes
My Blog

Thanks. At the bottom of the firewall rule section, I did already include a note in italics to that effect :)

Quote from: Greelan on February 20, 2021, 01:41:32 AM
Thanks. At the bottom of the firewall rule section, I did already include a note in italics to that effect :)

I did read that bit but this negates the need, but only a suggestion.

Ideally you should use the same idea for the invert destination of RFC1918 for your normal WAN rule too. So it saves you having to create block rules for internal networks, just allow internet only by default then put the cross network allow rules in :-). Easiest way to lock down the rules.
Adventuring through internet pipes
My Blog

That's fair. It is probably the main use case, so I will update the firewall rule section to exclude local IPs by default

I will leave out the WAN side of things as that's really out of scope for this tutorial

Quote from: Greelan on February 20, 2021, 01:54:54 AM
That's fair. It is probably the main use case, so I will update the firewall rule section to exclude local IPs by default

I will leave out the WAN side of things as that's really out of scope for this tutorial

Yeah that makes sense 🙂
Adventuring through internet pipes
My Blog

So I have now updated the firewall section. I have left the outbound NAT section as is - although on its face it has a broader operation than the firewall rule, I figured that if the firewall rule is not sending local traffic through the WG gateway then the outbound NAT rule for the WG interface won't impact it. Let me know if you disagree. Thanks again for the input

Quote from: Greelan on February 20, 2021, 02:29:45 AM
So I have now updated the firewall section. I have left the outbound NAT section as is - although on its face it has a broader operation than the firewall rule, I figured that if the firewall rule is not sending local traffic through the WG gateway then the outbound NAT rule for the WG interface won't impact it. Let me know if you disagree. Thanks again for the input

Looks good 😊
Adventuring through internet pipes
My Blog

I'm not sure how much of what I was having trouble with was due to actual "wireguard issues" vs the manual gateway configuration process but either way this was finally the tutorial that I needed to really understand what was going on.


This should really be cleaned up and added as one of the official VPN walkthroughs in the manual.

Quote from: TheChickenMan on March 01, 2021, 09:17:15 PM
This should really be cleaned up and added as one of the official VPN walkthroughs in the manual.
Not sure what "clean ups" you had in mind, but a PR has now been submitted for this tutorial to be added as a how-to in the OPNsense documentation: https://github.com/opnsense/docs/pull/317

Quote from: Greelan on March 03, 2021, 04:31:36 AM
Quote from: TheChickenMan on March 01, 2021, 09:17:15 PM
This should really be cleaned up and added as one of the official VPN walkthroughs in the manual.
Not sure what "clean ups" you had in mind, but a PR has now been submitted for this tutorial to be added as a how-to in the OPNsense documentation: https://github.com/opnsense/docs/pull/317


Oh sorry I just meant added to the official documentation page and not that there was anything wrong with the content. I appreciated the info very much and helped to get my Wireguard working.

Any idea how to do this same thing, but only route via VPN based on specific
destinations?

For example I want PC1, PC2, PC3 to use VPN only when trying to access YouTube.com, but all other traffic from these systems (that is NOT going to YouTube.com) would go out the regular WAN interface.

I created a hosts alias for both group of PCs (PC1, PC2, & PC3), and another host alias for destinations that should be routed via VPN (e.g. UseVPN alias has YouTube.com, Amazon.com, google.com).

I setup rule same as listed in main post, but instead of !RFC, I have UseVPN as destination... but this is not working. All traffic continues to go out WAN interface. But works fine when I revert back to !RFC

Quote from: djronh1 on March 13, 2021, 05:13:50 PM
I created a hosts alias for both group of PCs (PC1, PC2, & PC3), and another host alias for destinations that should be routed via VPN (e.g. UseVPN alias has YouTube.com, Amazon.com, google.com).

I setup rule same as listed in main post, but instead of !RFC, I have UseVPN as destination... but this is not working. All traffic continues to go out WAN interface. But works fine when I revert back to !RFC

because your only looking at the hosts in the main URL of the webpage, when you load youtube.com you make many other requests to domains other than youtube.com

If you press F12 on a blank tab, click the network tab on the newly popped up window. Then browse to youtube, you will see other domains other than youtube.com being loaded. For example "i.ytimg.com".
Trying to VPN certain websites via their base url domain, isn't always possible, when they use a CDN or a CDN of their own, because usually many other domains get used.

One way around it is to VPN the IP blocks that YouTube uses, but even this isn't always a fool proof way.
So you could get an IP for youtube.com which for me is "216.58.210.206", then lookup what ASN the IP is apart of which is AS15169, then get all the IPs owned by this ASN number. Which is a lot of ranges, because its Google's ASN, so any traffic going to Google, in these ranges would get VPN'd.

https://traceroute-online.com/ip-asn-lookup/

What your trying to do if not easy...
Adventuring through internet pipes
My Blog

I'm only using YouTube.com as an example .... the destinations I'm trying to force thru VPN are simple websites that resolve to a single IP.

I was able to setup rules easily on my old Asus router (via AsusWrt-Merlin firmware).

So I'm hoping that achieving same thing with OPNSesne should be doable.

Quote from: djronh1 on March 13, 2021, 06:05:46 PM
I'm only using YouTube.com as an example .... the destinations I'm trying to force thru VPN are simple websites that resolve to a single IP.

I was able to setup rules easily on my old Asus router (via AsusWrt-Merlin firmware).

So I'm hoping that achieving same thing with OPNSesne should be doable.

Oh then if they're simple websites, this is very doable.

Have you made sure your SitesToVPn rule is above your rule that allows traffic to the internet?
Also you need to make sure the Outbound NAT rule is there.
Adventuring through internet pipes
My Blog