OPNsense - zbrewer/homelab GitHub Wiki

Hardware/Prerequisites

I use a virtualized OPNsense instance as my primary router/firewall. Virtualization allows me to utilize the hardware as more than just a firewall; for example, I can run a DNS server and UniFi controller on it as well. In addition, this allows me to take snapshots of the OPNsense VM for easy restore-ability if something goes wrong when changing configurations. That being said, there are downsides to this approach well documented on the internet such as taking down the entire network (router) whenever the host needs to reboot.

Specifically, I am running OPNsense on a Project TinyMiniMicro node: a Lenovo M920q. Inside I have 16GB of RAM, an Intel i7-8700T processor (6c, 12t), and a 512 GB m.2 SSD. This is not only a physically compact machine but its power draw is relatively low and, under normal circumstances, it is relatively quiet.

In addition to the basic components, I chose the M920q (or the M720q) due to the fact that it can support up to a PCIe 3.0 x8 slot. This requires a proprietary riser (P/N 01AJ940) but it is readily available on eBay. Details are given on the STH forums here. This allowed me to install a Mellanox ConnectX-3 dual 10G SFP+ NIC and use one port for the LAN connection and one for WAN. Specifically, I went with an MCX312B (the pro model) but a MCX312A or any of the vendor-branded versions of either would also work fine. These cards both require custom baffles to secure to the back of the chassis but STL files for 3D printed parts are available on Thingiverse (MCX312A, MCX312B).

The Mellanox (and other) 10G NICs do tend to run a bit hot so following the instructions for temperature monitoring on the Telegraf page is recommended. That being said, the maximum junction temperature for these NICs is 105dC so as long as they are operating below that they should be fine. Do note too that copper transceivers will generate more heat than DACs or fiber.

Finally, I use OPNsense to segment my network into multiple VLANs so the LAN interface on the Mellanox card must be VLAN aware in Proxmox. This requires a bit of extra configuration to make sure that the correct VLAN IDs are available. See the instructions on the Proxmox page for more details.

Installation

Numerous guide such as this, this, or this exist for installing OPNsense in a VM. In order to avoid rehashing the instructions, the basic steps are this:

  1. Create virtual (bridge) interfaces for the LAN and WAN ports, tying them to the correct physical adapters.
  2. Set the LAN bridge interface to VLAN aware per the instructions on the Proxmox page.
  3. Download the ISO file from the OPNsense website and upload it to Proxmox, as normal.
  4. Create a VM for OPNsense. Give it at least 8GB of storage (ideally more like 32GB), 4 CPUs (all available if the machine is primarily a router/firewall), set the CPU type to host, 8GB of RAM, and select one of the bridge interfaces (set the model to VirtIO).
  5. After creating the VM, go to its hardware page and select add to add the other (WAN or LAN) interface in the same way.
  6. Start the machine and follow the installation prompts. ZFS can be selected as the filesystem type and Stripe for its configuration.

After installation, connect to the LAN port and visit 192.168.1.1 to access the web UI. Login with the username root and the password opnsense (this should be changed after initial login).

Setup

DNS

OPNsense includes an Unbound DNS server that is enabled by default. That being said, a bit of extra configuration is required.

First, go to Services > Unbound DNS > General and ensure that the Enable, DNSSEC, DHCP Registration, and DHCP Static Mappings options are all enabled.

From there, go to Services > Unbound DNS > DNS over TLS to set up Cloudflare as the upstream DNS provider and to connect over TLS. Add 4 servers, each with the hostname cloudflare-dns.com and the port 853. The IP addresses of the servers should be 1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, and 2606:4700:4700::1001.

After all settings are saved and applied, setting the OPNsense machine as the DNS server from a client computer (port 53) and making DNS queries should work, with the queries logged in OPNsense. In addition, visiting 1.1.1.1/help should show that the Cloudflare DNS servers are being used over TLS.

VLANs

VLANs can be created by visiting Interfaces > Other Types > VLAN and pressing the plus button. The VLAN name must meet some naming guidelines and it is easiest to include the VLAN tag in the name. For example, vlan0.100 for VLAN 100. Select the appropriate parent interface (usually LAN) and set the tag, priority, and description.

After saving and applying, go to Interfaces > Assignments to create a new interface for the VLAN that was just created. The description should be a meaningful name for the VLAN, such as "DMZ" or "Guest". Save and apply again and you should be automatically taken to the Interfaces > <interface_name> page. Here the Enable check box should be ticked and Static IPv4 should be selected for IPv4 Configuration Type. Finally, the IPv4 Address under Static IPv4 configuration should be set for the subnet desired. For example, setting it to 192.168.86.1 with the mask 24 will create a typical subnet with OPNsense available on 192.168.86.1 and the additional addresses from 192.168.86.2 to 192.168.86.254 available for use (192.168.86.255 is reserved for broadcast use).

At this point, the VLAN is set up and ready for use. Assigning the VLAN tag downstream (such as to another VM/CT on the host Proxmox machine, attached to the LAN bridge interface) and a static IP in the subnet should allow a link to be established. That being said, the firewall by default will not permit any traffic. To allow outbound connections to anything (and therefore general internet access), navigate to Firewall > Rules > <vlan_interface_name> and create a new rule with * as the source and destination. Save and apply.

Web UI Access

Before setting up the WAN connection, it is important to realize that the web UI is available on all interfaces by default. This means that, once the WAN connection is set up, the configuration page could be accessed via your public IP address. This could be stopped with a firewall rule or, under System > Settings > Administration the Listen Interfaces option can be changed so that it just listens on one of the LAN interfaces (such as the interface for a management VLAN).

CenturyLink PPPoE

CenturyLink fiber internet can be configured by connecting the WAN port directly to the ISP provided ONT. In order to do this, some extra steps are required. These are the steps necessary in my area to set up a PPPoE connection but details in other areas may vary.

CenturyLink connects over a VLAN, 201 in my case, so a new VLAN must be created per the steps above with this tag. The interface it is assigned to should be WAN. Then, on the Interfaces > WAN page, the IPv4 Configuration Type should be set to PPPoE and the username and password provided by CenturyLink should be entered under PPPoE configuration (note that the username should include the @centurylink.net suffix).

At this point, a reboot may be required but an IP address should be pulled by the WAN interface and populated on the Lobby > Dashboard page (under Interfaces).

Because PPPoE is single-threaded on BSD (which OPNsense is built on), WAN performance is directly tied to single-threaded performance of the host's CPU. On a modern x86 machine (like the i7-8700T I'm using) the performance isn't a problem and getting 1G down/up should be doable with no further tweaks. That being said, some options do exist if necessary to try to improve performance. These tweaks are documented across the internet with a good reference source here; however, the basics are:

  1. Set Multiqueue to 8 under the Proxmox advances settings for the WAN bridge interface.
  2. Make sure all hardware offloading is disabled in OPNsense.
  3. Add three tunables under System > Settings > Tunables:
    1. net.isr.bindthreads with a value of 1.
    2. net.isr.maxthreads with a value of -1.
    3. net.isr.dispatch with a value of deferred.

DHCP

For any interfaces where its use is desired, the DHCP server can be configured under Services > DHCPv4 > <interface_name>. Check the enable box and set the DHCP range for the address range that should be assigned automatically. Then specify the DHCP server (if desired) and add any static mappings.

Inter-VLAN Access/Firewall Rules

By default, the OPNsense firewall doesn't include any firewall rules for a new interface meaning that all traffic is denied. Rules must be configured to permit the desired traffic while keeping in mind that they are generally evaluated top to bottom and the first rule that matches is applied. This means that, if a rule that allows the traffic is followed by one that would block it, the traffic is allowed.

In general, I typically add five "default" rules to interfaces that are supposed to have limited access (no or restricted access across VLANs). These are all in rules and are (in top-to-bottom order):

  1. Allow access specifically to the DNS server(s). This is necessary if the DNS server is on a different VLAN (for example, a Pi-Hole server) or if it is the firewall itself. The action for this rule is Pass, the source/port is * (any), the destination is the DNS server's IP (setting up an alias for this is nice), and the destination port is 53.
  2. Prevent other access to the firewall (especially if the management application is set to listen on the interface but it shouldn't be accessed). The rule here is Block or Reject and the destination is This Firewall.
  3. Allow (L3) access to other devices on the same VLAN. This uses <interface> net as both the source and destination and the Pass action.
  4. Prevent access to other VLANs. I do this by creating an alias for InternalNetworks that references all private IP ranges. Its type is Network(s) and its content is 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16. I then create a rule on the interface of the VLAN I want to restrict and set the action to Block or Reject, the protocol/source/ports to * (any), and the destination to the InternalNetworks alias. This prevents any L3 access to other VLANs (and L2 traffic is already segmented by the VLANs themselves).
  5. Allow access to all other destinations (namely, those that aren't internal). This rule is at the very bottom of the list and uses * (any) for the protocol, source, destination, and ports.

At this point, access to other VLANs is not allowed except for the DNS server. Further holes can be punched through the firewall for access to devices on other VLANs (such as a reverse proxy) by creating Pass rules before the rule that blocks access to other VLANs. Internet access can be further restricted by adding more Block or Reject rules before the final rule that allows all remaining connections.

Other Firewall Rules

If segmenting Users/Guest from IoT, some additional rules may be needed for devices such as Chromecasts to work.

To start, many of these devices rely on multicast for discovery. Since multicast is not propagated across VLANs, the Multicast DNS Proxy must be installed per the OPNsense docs and configured for the IoT and Users/Guests VLAN(s) (as appropriate).

In addition, the following firewall rules must be added to the client (Users and/or Guest) VLAN(s). Note that firewall aliases are extremely helpful when setting this up.

Service Protocol Source Source Port Destination Destination Port
Chromecast TCP * * 8008, 8009, 8443
Chromecast UDP * * 32768:61000
Plex TCP * * <plex_servers> 32400
Reverse Proxy TCP * * <reverse_proxy> 443
Tablo TCP * * 80, 8887
Samba TCP * * <samba_server(s)> 445
Steam Link TCP <steam_link_hosts> * <steam_link_clients> 27036, 27037
Steam Link UDP <steam_link_hosts> * <steam_link_clients> 27031, 27036

And the following rules must be added to the IoT VLAN:

Service Protocol Source Source Port Destination Destination Port
Plex TCP <plex_players> * <plex_servers> 32400
Steam Link TCP <steam_link_clients> * <steam_link_hosts> 27036, 27037
Steam Link UDP <steam_link_clients> * <steam_link_hosts> 27031, 27036

DDNS Server

OPNsense can also be set up as a DDNS server when using a supported DNS server for your domain. I have this set up using Cloudflare. The DDNS plugin can be installed in OPNsense per the instructions here and then it can be configured as follows:

Got to Services > Dynamic DNS > Settings and click on the plus button. Make sure Enabled is ticked and set the Service to Cloudflare. Set the zone to the base URL (such as example.com) and add the hostname(s) you would like to update to the Hostname(s) field (such as vpn.example.com). Set the Check ip method to Interface and set the Interface to WAN.

Finally, create an API Token for specific use by the DDNS server on the Cloudflare website and give it DNS:edit permissions for the zone (base URL) of the site you want to update with DDNS. While you're there, create A records for the hostname(s) the DDNS server will try to update (such as vpn.example.com). These can point to a dummy IP address or now such as 8.8.8.8 (Google's DNS). Make sure to de-select the Proxy option, otherwise the IP address returned by public nameservers will be a Cloudflare IP and not the IP set by DDNS.

Copy the Cloudflare API token into the password field in OPNsense and leave the username blank. Save and apply. After a second, the A record(s) should be updated with your public IP.

Traffic Shaper

OPNsense has the ability to shape traffic, such as limiting the bandwidth available to some clients. This is useful to, for example, restrict speeds on the guest network to ensure that there is plenty of bandwidth available for servers and privileged users. A comprehensive guide is available here detailing how to set this up; however, below will be a description of how to set up the traffic shaper to limit the guest network to 100Mbps down and 50Mbps up.

First, navigate to the Firewall > Shaper > Pipes page in the OPNsense UI. On the Pipes tab, create one upload pipe and one download pipe. Specify the bandwidth (and unit) for each, make sure they have a good description (for example, 100Mbps_guest_download), and make sure they are enabled.

Now, navigate to the Rules tab and create a new rule for the download limit. Set the Interface to WAN, the Protocol to IP, the Source to any, the Src-port to any, and the Destination to the guest subnet in CIDR notation (for example, 10.0.0.0/24). Set the Dst-port to any and specify the download pipe created in the previous step for the target. Make sure there is a good description and that it is enabled before saving and applying.

An upload pipe can be created in the same way except for the fact that its Target will be the upload pipe and its Source and Destination will be flipped (in other words, the Source will be the subnet and the Destination will be any). Save and apply this pipe as well.

It is important to specify the WAN interface for these rules since they will then control the bandwidth between the WAN (internet) and the guest network (subnet) while not limiting any local throughput.

Wireguard Server (Road Warrior Setup)

OPNsense itself can run a Wireguard server to allow remote connections. The official OPNsense documentation does a good job of walking through this process so I just want to add a few notes here.

The first point worth noting is that the new Wireguard setup will get its own interface in OPNsense. This is a "virtual" interface since its traffic will be tunneled and travel over some other physical interface (typically the WAN). This means that L2 concepts, such as VLANS, don't apply here. If you'd like to configure it to have the same access as the other VPN interface/subnet (which may have its own VLAN), you can create a new group under Firewall > Groups and put both interfaces there. Then move all firewall rules to this group. This will ensure that they have the same access. That being said, additional rules can still be added to each interface independently, if desired.

The next point worth noting is that the new Wireguard interface will need its own address space (subnet) that is independent from any that have already been created. The easiest way to do this is to use an entirely new class C block (/24 aka a subnet mask of 255.255.255.0); however, it could also be done by splitting a class C block and using part for the VPN VLAN/other VPN hosts and part for the Wireguard Road Warrior interface. This could be useful if, for example, you'd like to keep the first two octets of your network addresses consistent and use the third octet to indicate that it is a VPN IP. This is just a matter of preference but see the cheat sheet here for examples of sub-class C blocks.

Finally, the last point worth noting is that reflection NAT is not a problem with this setup; however, you will need to ensure that the firewall is configured to allow connections to the firewall itself on the Wireguard port internally.

⚠️ **GitHub.com Fallback** ⚠️