# OPNsense Baseline Guide with Mullvad VPN Multi-WAN, Guest, and VLAN Support

<div class="post-header" id="bkmrk-2021-11-17-%28updated%3A"><div class="post-meta">2021-11-17 (updated: 2023-07-30)</div><div class="post-meta">  
</div></div>[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/1lMfvl3H7OjWGBCv-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/1lMfvl3H7OjWGBCv-image.png)

<div class="post-header" id="bkmrk-2021-11-17%C2%A0%28updated%3A"><div class="post-meta">  
</div></div>This beginner-friendly, step-by-step guide walks you through the initial configuration of your OPNsense firewall. The title of this guide is an homage to the [pfSense baseline guide with VPN, Guest, and VLAN support](https://nguvu.org/pfsense/pfsense-baseline-setup) that some of you guys might know, and this is an [OPNsense](https://opnsense.org/) migration of it. I found that guide two years ago and immediately fell in love with the network setup. After researching for weeks, I decided to use OPNsense instead of pfSense. I bit the bullet and bought the [Deciso DEC630](https://www.deciso.com/product-catalog/dec630/) appliance. Albeit expensive and possibly overkill for my needs, I’m happy to support the open-source mission of Deciso, the maintainers of OPNsense. The only thing I regret about the purchase is that I now can’t afford the sexier-looking successor model, the [DEC690](https://shop.opnsense.com/dec600-series-opnsense-desktop-security-appliances/).

To configure OPNsense, I followed the instructions of the pfSense guide, taking notes on the differences. Some options moved to different menus or changed. As my notes grew, I decided to publish them as a guide on my website.

My goal was to create a comprehensive guide that’s easy to follow. But I tried to strike a different balance regarding the brevity of the instructions compared to the pfSense guide. It’s a matter of personal taste, but I find the instructions in that guide too verbose. I intentionally omit most of the repetitive “click save and apply” instructions and only list configuration changes deviating from defaults, making exceptions for important settings. I consider the OPNsense defaults stable enough for this approach in the hope of keeping the effort required to maintain this guide to a minimum.

I’m a homelab hobbyist, so be warned that this guide likely contains errors. Please, verify the steps yourself and do your research. I hope this guide is as helpful and inspiring to you as the pfSense guide was to me. Your feedback is always welcome and very much appreciated.

## Overview

### WAN

<div class="post-content" id="bkmrk-dhcp-wan-from-a-sing">- DHCP WAN from a single Internet Service Provider (ISP)
- [Mullvad VPN](https://mullvad.net/) multi-WAN with gateway groups

</div>### LAN

We segregate the local network into several areas with different requirements.

#### Management Network (VLAN 10)

The Management network connects native management interfaces like WiFi access points and IPMI interfaces.

#### VPN Network (VLAN 20)

The primary LAN network uses the WireGuard VPN tunnels for outbound connections, maximizing privacy and security. If the VPN tunnels fail, outbound connections won’t be possible. Exceptions to selectively route traffic through the ISP WAN gateway are possible.

#### “Clear” Network (VLAN 30)

General-purpose web access network that doesn’t use VPN tunnels. All outgoing connections leave through the ISP WAN gateway. It serves as a backup network in case the VPN tunnels fail.

#### Guest Network (VLAN 40)

The network that visitors use. It allows unrestricted internet access. Local networks aren’t accessible.

#### LAN Network

“Native” VLAN, used to debug and test new configurations.

### DNS Services

We’ll configure a DNS resolver (Unbound), as well as a DNS forwarder (Dnsmasq) in OPNsense. Management and VPN networks will use the resolver, the Clear network will use the forwarder, and the Guest network will use Cloudflare as an external DNS resolver. [We’ll dig into the details later](https://schnerring.net/blog/opnsense-baseline-guide-with-vpn-guest-and-vlan-support/#dns).

## Hardware Selection and Installation

The original pfSense guide features a [large section of hardware recommendations](https://nguvu.org/pfsense/pfsense-baseline-setup/#Hardware%20selection) and [installation instructions](https://nguvu.org/pfsense/pfsense-baseline-setup/#Install%20pfSense).

As mentioned earlier, I bought the [Deciso DEC630](https://www.deciso.com/product-catalog/dec630/) appliance, which is why I’m not advising on hardware choices. Have a look at the [official hardware sizing &amp; setup guidelines](https://docs.opnsense.org/manual/hardware.html) for more information. See also [Initial Installation &amp; Configuration](https://docs.opnsense.org/manual/install.html).

I verified this guide with a clean install of OPNsense version `21.7.5`.

## Wizard

Navigate to `192.168.1.1` in your browser and login with default credentials:

<div class="post-content" id="bkmrk-username%3A%C2%A0root-passw">- **Username**: `root`
- **Password**: `opnsense`

</div>Click `Next` to leave the welcome screen and get started with the initial wizard configuration.

### General Information

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/F1Vyg2uCeV2Whf7A-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/F1Vyg2uCeV2Whf7A-image.png)

I prefer using the DNS servers of [Quad9](https://quad9.org/) over the ones of my ISP. Only the Clear network will use these anyway, as secured networks use Unbound instead. The Guest network will use Cloudflare DNS servers.

For the domain, I prefer to use a subdomain of a domain name I own, like `corp.example.com`. I only use this subdomain internally. I consider the `local.lan` pattern a relic of the past. To prevent our local network structure from leaking to the outside world, we’ll later configure Unbound and Dnsmasq to treat the domain as private.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-domain-corp.exam"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Domain</td><td>`corp.example.com`</td></tr><tr><td>Primary DNS Server</td><td>`9.9.9.9`</td></tr><tr><td>Secondary DNS Server</td><td>`149.112.112.112`</td></tr><tr><td>Override DNS</td><td>`unchecked`</td></tr><tr><td>Enable DNSSEC Support</td><td>`checked`</td></tr><tr><td>Harden DNSSEC data</td><td>`checked`</td></tr></tbody></table>

</div>If you prefer using your ISP’s DNS servers, leave the **Override DNS** option checked.

### Time Server Information

Choose the NTP servers geographically closest to your location. I live in Switzerland, which makes the [servers from the `ch.pool.ntp.org` pool](https://www.pool.ntp.org/zone/ch) the natural choice.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-time-server-host"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Time server hostname</td><td>`0.ch.pool.ntp.org 1.ch.pool.ntp.org 2.ch.pool.ntp.org 3.ch.pool.ntp.org`</td></tr><tr><td>Timezone</td><td>`Europe/Zurich`</td></tr></tbody></table>

</div>### Configure Interfaces

By default, the WAN interface obtains an IP address from your ISP via DHCP. DHCP is also configured for the LAN interface by default and has the IP `192.168.1.1`. It works for most people, so we just keep the defaults.

### Set Root Password

Choose a strong root password and complete the wizard.

## General Settings

### Access

Navigate to **System** → **Settings** → **Administration**.

<div class="post-content" id="bkmrk-http-redirect-%C2%A0-disa"><table><thead><tr><th>HTTP Redirect</th><th> </th></tr></thead><tbody><tr><td>Disable web GUI redirect rule</td><td>`checked`</td></tr></tbody></table>

</div>Permitting root user login and password login is a quick and dirty way of enabling SSH access, but I strongly discourage you from doing it. They are disabled for security reasons. I highly recommend using certificate- or [key-based authentication](https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server). If your device has a serial console port, like the Deciso DEC630, enabling SSH is not required.

<div class="post-content" id="bkmrk-secure-shell-%C2%A0-%C2%A0-sec"><table><thead><tr><th>Secure Shell</th><th> </th><th> </th></tr></thead><tbody><tr><td>Secure Shell Server</td><td>`checked`</td><td> </td></tr></tbody></table>

<table><thead><tr><th>Authentication</th><th> </th><th> </th></tr></thead><tbody><tr><td>Sudo</td><td>`Ask password`</td><td>Permit sudo usage for administrators with shell access.</td></tr></tbody></table>

</div>Navigate to **System** → **Access** → **Users** and add a new user.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-username-%3Cchoose"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Username</td><td>`<choose a username>`</td></tr><tr><td>Password</td><td>`<choose a secure password>`</td></tr><tr><td>Login shell</td><td>`/bin/csh`</td></tr><tr><td>Group Memberships</td><td>`admins`</td></tr><tr><td>Authorized keys</td><td>`<valid SSH public key>`</td></tr></tbody></table>

</div>Configuring the SSH client and generating keys is out of scope for this guide, so I’ll just recommend this [DigitalOcean tutorial covering SSH essentials](https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys).

### Miscellaneous

Navigate to **System** → **Settings** → **Miscellaneous**.

<div class="post-content" id="bkmrk-power-savings-%C2%A0-use-"><table><thead><tr><th>Power Savings</th><th> </th></tr></thead><tbody><tr><td>Use PowerD</td><td>`checked`</td></tr><tr><td>Power Mode</td><td>`Hiadaptive`</td></tr></tbody></table>

</div>Choose **Cryptography settings** and **Thermal Sensors** settings compatible with your hardware.

### Firewall Settings

Navigate to **Firewall** → **Settings** → **Advanced**.

Although IPv6 is something I want to use, it’s out of scope for this guide, so we uncheck the following.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-allow-ipv6-unche"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Allow IPv6</td><td>`unchecked`</td></tr></tbody></table>

</div>When a rule uses a specific gateway and goes down, a rule gets created, sending traffic to the default gateway. Checking this option skips the creation of this rule.

<div class="post-content" id="bkmrk-gateway-monitoring-%C2%A0"><table><thead><tr><th>Gateway Monitoring</th><th> </th></tr></thead><tbody><tr><td>Skip rules</td><td>`checked`</td></tr></tbody></table>

</div>Depending on your hardware, you might want to tweak the following settings to improve performance.

<div class="post-content" id="bkmrk-miscellaneous-%C2%A0-%C2%A0-fi"><table><thead><tr><th>Miscellaneous</th><th> </th><th> </th></tr></thead><tbody><tr><td>Firewall Optimization</td><td>`conservative`</td><td>Tries to avoid dropping any legitimate idle connections at the expense of increased memory usage and CPU utilization.</td></tr><tr><td>Firewall Maximum Table Entries</td><td>`2000000`</td><td>default is 1'000'000</td></tr></tbody></table>

</div>We disable the auto-generated anti-lockout rule because we’ll define it manually later.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-disable-anti-loc"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Disable anti-lockout</td><td>`checked`</td></tr></tbody></table>

</div>### Checksum Offloading

For some hardware, checksum offloading doesn’t work, particularly some Realtek cards. Rarely, drivers may have problems with checksum offloading and some specific NICs. If your hardware is incompatible with checksum offloading, disable it.

Navigate to **Interfaces** → **Settings**.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-%C2%A0-hardware-crc-u"><table><thead><tr><th> </th><th> </th><th> </th></tr></thead><tbody><tr><td>Hardware CRC</td><td>`unchecked`</td><td>Disable hardware checksum offload</td></tr></tbody></table>

</div>## VLANs

### Switch Choice

A 802.1Q-capable switch with properly configured VLANs is required. Check my [router on a stick VLAN configuration guide](https://schnerring.net/posts/router-on-a-stick-vlan-configuration-with-swos-on-the-mikrotik-crs328-24p-4s+rm-switch) to see an example setup with a [Mikrotik](https://mikrotik.com/) switch.

### VLAN Definitions

Typically, the `LAN` port also carries the VLAN traffic and functions as [trunk port](https://www.techopedia.com/definition/27008/trunk-port). For me, the default is the `igb0` port. I chose it as the parent interface for all VLANs in the following steps.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/1ycHKtZnDhMLUDeJ-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/1ycHKtZnDhMLUDeJ-image.png)

Navigate to **Interfaces** → **Other Types** → **VLAN** and add the VLANs.

#### Management VLAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-parent-igb0-vlan"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Parent</td><td>`igb0`</td></tr><tr><td>VLAN tag</td><td>`10`</td></tr><tr><td>Description</td><td>`VLAN10_MANAGE`</td></tr></tbody></table>

</div>#### VPN VLAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-parent-igb0-vlan-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Parent</td><td>`igb0`</td></tr><tr><td>VLAN tag</td><td>`20`</td></tr><tr><td>Description</td><td>`VLAN20_VPN`</td></tr></tbody></table>

</div>#### Clear VLAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-parent-igb0-vlan-2"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Parent</td><td>`igb0`</td></tr><tr><td>VLAN tag</td><td>`30`</td></tr><tr><td>Description</td><td>`VLAN30_CLEAR`</td></tr></tbody></table>

</div>#### Guest VLAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-parent-igb0-vlan-3"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Parent</td><td>`igb0`</td></tr><tr><td>VLAN tag</td><td>`40`</td></tr><tr><td>Description</td><td>`VLAN40_GUEST`</td></tr></tbody></table>

</div>### VLAN Interfaces

We add an interface for each VLAN. Navigate to **Interfaces** → **Assignments**.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/wXYSms2TahvUWNkd-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/wXYSms2TahvUWNkd-image.png)

<div class="post-content" id="bkmrk-select%C2%A0vlan-10%2C-ente">- Select `vlan 10`, enter the description `VLAN10_MANAGE`, and click `+`
- Select `vlan 20`, enter the description `VLAN20_VPN`, and click `+`
- Select `vlan 30`, enter the description `VLAN30_CLEAR`, and click `+`
- Select `vlan 40`, enter the description `VLAN40_GUEST`, and click `+`

</div>Click `Save`.

### VLAN Interface IPs

To easier remember which IP range belongs to which VLAN, I like the convention of matching the third octet of the IP with the VLAN ID. I.e., assigning the VLAN with the ID **10** the address 192.168.**10**.0/24.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/YzsFcaStfk9gJy40-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/YzsFcaStfk9gJy40-image.png)

#### Interface: VLAN10\_MANAGE

Select the `VLAN10_MANAGE` interface.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-interface"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable Interface</td><td>`checked`</td></tr><tr><td>IPv4 Configuration Type</td><td>`Static IPv4`</td></tr><tr><td>IPv4 Address</td><td>`192.168.10.1/24`</td></tr></tbody></table>

</div>Click `Save`.

#### Interface: VLAN20\_VPN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-interface-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable Interface</td><td>`checked`</td></tr><tr><td>IPv4 Configuration Type</td><td>`Static IPv4`</td></tr><tr><td>IPv4 Address</td><td>`192.168.20.1/24`</td></tr></tbody></table>

</div>#### Interface: VLAN30\_CLEAR

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-interface-2"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable Interface</td><td>`checked`</td></tr><tr><td>IPv4 Configuration Type</td><td>`Static IPv4`</td></tr><tr><td>IPv4 Address</td><td>`192.168.30.1/24`</td></tr></tbody></table>

</div>#### Interface: VLAN40\_GUEST

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-interface-3"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable Interface</td><td>`checked`</td></tr><tr><td>IPv4 Configuration Type</td><td>`Static IPv4`</td></tr><tr><td>IPv4 Address</td><td>`192.168.40.1/24`</td></tr></tbody></table>

</div>### VLAN Interface DHCP

We need to configure DHCP for each VLAN we created. I use `x.x.x.100-199` for dynamic and `x.x.x.10.10-99` for static IP address assignments. You might want to amend these ranges to your requirements.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/s9nRpcDQNHTjlvKq-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/s9nRpcDQNHTjlvKq-image.png)

Navigate to **Services** → **DHCPv4**.

#### DHCP: VLAN10\_MANAGE

Select `VLAN10_MANAGE`.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-checked-r"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable</td><td>`checked`</td></tr><tr><td>Range</td><td>from `192.168.10.100` to `192.168.10.199`</td></tr></tbody></table>

</div>Click `Save`.

#### DHCP: VLAN20\_VPN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-checked-r-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable</td><td>`checked`</td></tr><tr><td>Range</td><td>from `192.168.20.100` to `192.168.20.199`</td></tr></tbody></table>

</div>#### DHCP: VLAN30\_CLEAR

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-checked-r-2"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable</td><td>`checked`</td></tr><tr><td>Range</td><td>from `192.168.30.100` to `192.168.30.199`</td></tr></tbody></table>

</div>#### DHCP: VLAN40\_GUEST

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-checked-r-3"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable</td><td>`checked`</td></tr><tr><td>Range</td><td>from `192.168.40.100` to `192.168.40.199`</td></tr><tr><td>DNS servers</td><td>`1.1.1.1` `1.0.0.1`</td></tr></tbody></table>

</div>#### DHCP: LAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-range-from%C2%A0192.1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Range</td><td>from `192.168.1.100` to `192.168.1.199`</td></tr></tbody></table>

</div>## WireGuard VPN with Mullvad

In recent years, [Mullvad](https://mullvad.net/) has been my VPN provider of choice. When *That One Privacy Site* was still a thing, Mullvad was one of the top recommendations there. After reading the review, I decided to try it out and haven’t looked back since. No personally identifiable information is required to register, and paying cash via mail works perfectly.

I decided to go with [WireGuard](https://www.wireguard.com/) because I’m fine riding the bleeding edge. 😎 For more detailed steps, check the official OPNsense documentation on setting up [WireGuard with Mullvad](https://docs.opnsense.org/manual/how-tos/wireguard-client-mullvad.html) and [WireGuard selective routing](https://docs.opnsense.org/manual/how-tos/wireguard-selective-routing.html).

Please note that the FreeBSD kernel does not (yet) natively support WireGuard, so you must install it as a plugin. Possibly, this doesn’t meet your stability, security, or performance requirements.

Navigate to **System** → **Firmware** → **Plugins** and install `os-wireguard`. Refresh the browser and navigate to **VPN** → **WireGuard**.

### Remote Peers

Select your preferred WireGuard servers from the [Mullvad’s server list](https://mullvad.net/en/servers/) and take note of their names and public keys. It’s worth spending some time to benchmark server performance before making a choice.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/YRdqmPUVSTdg8TRx-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/YRdqmPUVSTdg8TRx-image.png)

Select the **Endpoints** tab and click **Add**. Here is the configuration for the remote `ch5-wireguard` Mullvad endpoint.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-mullvad-ch5"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`mullvad-ch5-wireguard`</td></tr><tr><td>Public Key</td><td>`/iivwlyqWqxQ0BVWmJRhcXIFdJeo0WbHQ/hZwuXaN3g=`</td></tr><tr><td>Allowed IPs</td><td>`0.0.0.0/0`</td></tr><tr><td>Endpoint Address</td><td>`193.32.127.66`</td></tr><tr><td>Endpoint Port</td><td>`51820`</td></tr><tr><td>Keepalive</td><td>`25`</td></tr></tbody></table>

</div>To mitigate risks against DNS poisoning, resolve the server’s hostname and enter its IP as **Endpoint Address**. You can do this by running `nslookup ch5-wireguard.mullvad.net` in a shell. Make sure to not confuse this address with the SOCKS5 Proxy Address from Mullvad’s server list!

Repeat the steps above to add another server, e.g., `ch6-wireguard`. Note that all endpoint configurations use the **Endpoint Port** `51820`.

### Local Peers

Select the **Local** tab, click `Add`, and enable the `advanced mode`.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-mullvad0-li"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`mullvad0`</td></tr><tr><td>Listen Port</td><td>`51820`</td></tr><tr><td>Tunnel Address</td><td>`<LEAVE EMPTY>`</td></tr><tr><td>Peers</td><td>`ch5-wireguard`</td></tr><tr><td>Disable Routes</td><td>`checked`</td></tr><tr><td>Gateway</td><td>`<LEAVE EMPTY>`</td></tr></tbody></table>

</div>Click `Save` to generate the WireGuard key pair. Click `Edit` and copy the generated **Public Key**.

Next, run the following shell command to get a Mullvad access token:

```shell
access_token=$( \
  curl -X 'POST' 'https://api.mullvad.net/auth/v1/token' \
    -H 'accept: application/json' -H 'content-type: application/json' \
    -d '{ "account_number": "YOUR MULLVAD ACCOUNT NUMBER" }' \
  | jq -r .access_token)
```

<div class="post-content" id="bkmrk-copy"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>Then run the following command to create a *Mullvad Device* with DNS hijacking disabled:

```shell
curl -X POST https://api.mullvad.net/accounts/v1/devices \
  -H "Authorization: Bearer $access_token" -H 'content-type: application/json' \
  -d '{"pubkey":"YOUR PUBLIC KEY","hijack_dns":false}'
```

<div class="post-content" id="bkmrk-copy-1"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>I cover the snippet above and Mullvad’s DNS hijacking in another post: [Use Custom DNS Servers With Mullvad And Any WireGuard Client](https://schnerring.net/posts/use-custom-dns-servers-with-mullvad-and-any-wireguard-client).

```json
{
  "id": "d8f07004-4559-4d19-b58b-985b257cd115",
  "name": "uptown insect",
  "pubkey": "ufO5jCni55uvioHM/eLBgyrrUMocEXsADPc2OvYhF3k=",
  "hijack_dns": false,
  "created": "2023-07-30T02:08:41+00:00",
  "ipv4_address": "10.138.30.139/32",
  "ipv6_address": "fc00:bbbb:bbbb:bb01:d:0:a:1e8b/128",
  "ports": []
}
```

<div class="post-content" id="bkmrk-copy-2"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>Copy the IPv4 IP address to the **Tunnel Address** field of the Wireguard local peer. Subtract one from the **Tunnel Address** and enter the result as **Gateway** IP. E.g., `10.105.248.50` for the example above. It’s just a convention I like, but you can use any arbitrary, unused [private RFC1918 IP](https://datatracker.ietf.org/doc/html/rfc1918).

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/sCSBW690Qpgt0lU3-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/sCSBW690Qpgt0lU3-image.png)

Repeat the steps above to create a second local peer named `mullvad1`. Remember to use a *different* **Listen Port** (e.g., `51821`).

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/ezUFfYAXGByzTio9-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/ezUFfYAXGByzTio9-image.png)

When you finish, select the `General` tab. Check **Enable WireGuard**. You should see a handshake for the `wg0` and `wg1` tunnels on the **Handshakes** tab.

### WireGuard Interfaces

Navigate to **Interfaces** → **Assignments**.

<div class="post-content" id="bkmrk-select%C2%A0wg0%2C-add-the-">- Select `wg0`, add the description `WAN_VPN0`, and click `+`
- Select `wg1`, add the description `WAN_VPN1`, and click `+`

</div>Enable the newly created interfaces and restart the WireGuard service after. It ensures the interfaces get an IP address from WireGuard.

### VPN Gateways

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/YXjfIn6i4WAvrcWE-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/YXjfIn6i4WAvrcWE-image.png)

Navigate to **System** → **Gateways** → **Single** and add the VPN gateways.

#### WAN\_VPN0

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-wan_vpn0-in"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`WAN_VPN0`</td></tr><tr><td>Interface</td><td>`WAN_VPN0`</td></tr><tr><td>Address Family</td><td>`IPv4`</td></tr><tr><td>IP Address</td><td>`10.105.248.50`</td></tr><tr><td>Far Gateway</td><td>`checked`</td></tr><tr><td>Disable Gateway Monitoring</td><td>`unchecked`</td></tr><tr><td>Monitor IP</td><td>`100.64.0.1`</td></tr></tbody></table>

</div>#### WAN\_VPN1

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-wan_vpn1-in"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`WAN_VPN1`</td></tr><tr><td>Interface</td><td>`WAN_VPN1`</td></tr><tr><td>Address Family</td><td>`IPv4`</td></tr><tr><td>IP Address</td><td>`10.109.231.89`</td></tr><tr><td>Far Gateway</td><td>`checked`</td></tr><tr><td>Disable Gateway Monitoring</td><td>`unchecked`</td></tr><tr><td>Monitor IP</td><td>`100.64.0.2`</td></tr></tbody></table>

</div>#### Monitoring IPs

Each VPN gateway requires a unique monitoring IP because setting a monitoring IP installs a static route. Optimally, the monitoring IP should be the least possible amount of hops away from the gateway. For Mullvad specifically, we can “abuse” the local infrastructure that’s available through a Mullvad connection. Any of the following IPs are only *one* hop away from the tunnel exit.

<div class="post-content" id="bkmrk-100.64.0.1%C2%A0to%C2%A0100.64">- `100.64.0.1` to `100.64.0.3` are [Mullvad’s ad-blocking and tracker-blocking DNS service servers](https://mullvad.net/it/blog/2021/5/27/how-set-ad-blocking-our-app/)
- `10.64.0.1` is the local Mullvad gateway

</div>You can easily verify the above by running `traceroute 100.64.0.1` from a host connected to Mullvad.

#### Add Static IPv4 Configuration to the WireGuard Interfaces

OPNsense versions newer than `21.7.3` require adding static IPv4 configuration to the WireGuard interface. Otherwise, Unbound will use the default route despite setting the **Outgoing Network Interfaces** option. Other solutions exist, but I’m not sure which the “best” or most logical one is. As WireGuard integration matures, this section hopefully becomes obsolete. [You can find more information regarding this issue on GitHub](https://github.com/opnsense/core/issues/5329#issuecomment-958397043).

Navigate to **Interfaces** and edit the WireGuard interfaces.

##### IP Configuration: WAN\_VPN0

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-ipv4-configurati"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>IPv4 Configuration Type</td><td>`Static IPv4`</td></tr><tr><td>IPv4 address</td><td>`10.105.248.51/32`</td></tr><tr><td>IPv4 Upstream Gateway</td><td>`WAN_VPN0 - 10.105.248.50`</td></tr></tbody></table>

</div>##### IP Configuration: WAN\_VPN1

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-ipv4-configurati-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>IPv4 Configuration Type</td><td>`Static IPv4`</td></tr><tr><td>IPv4 address</td><td>`10.109.231.90/32`</td></tr><tr><td>IPv4 Upstream Gateway</td><td>`WAN_VPN1 - 10.109.231.89`</td></tr></tbody></table>

</div>#### Gateway Group

Navigate to **System** → **Gateways** → **Group** and click `Add`.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-group-name-wan_v"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Group Name</td><td>`WAN_VPN_GROUP`</td></tr><tr><td>WAN\_VPN0</td><td>`Tier 1`</td></tr><tr><td>WAN\_VPN1</td><td>`Tier 2` (failover)</td></tr><tr><td>Trigger Level</td><td>`Packet Loss or High Latency`</td></tr></tbody></table>

</div>It’s also possible to configure load balancing by putting multiple interfaces into the same tier.

### Static Routes (Optional)

Defining static routes for the tunnel gateways is optional. It would be necessary, for example, if we want to consider the VPN gateways as default gateway candidates. It requires static routes to the ISP WAN gateway to keep the tunnel connections alive.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/6lieJ71tPHp22sz3-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/6lieJ71tPHp22sz3-image.png)

Navigate to **System** → **Routes** → **Configuration** and click `Add`.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-network-address-"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Network Address</td><td>`193.32.127.66/32`</td></tr><tr><td>Gateway</td><td>`WAN_DHCP`</td></tr><tr><td>Description</td><td>`Keep tunnels to mullvad-ch5-wireguard alive`</td></tr></tbody></table>

<table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Network Address</td><td>`193.32.127.67/32`</td></tr><tr><td>Gateway</td><td>`WAN_DHCP`</td></tr><tr><td>Description</td><td>`Keep tunnels to mullvad-ch6-wireguard alive`</td></tr></tbody></table>

</div>## DNS

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/eaIocYX0oePfHVgu-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/eaIocYX0oePfHVgu-image.png)

OPNsense includes a DNS *resolver* (Unbound) and a DNS *forwarder* (Dnsmasq / Unbound in forwarding mode). Simple setups usually use one of either, but we’ll use both. Because we’ll also use Unbound and Dnsmasq for internal DNS resolution, we don’t want to use them for the Guest network, as this would expose our internal network structure. That’s the reason why we earlier configured it to use Cloudflare DNS servers instead.

Like the name suggests, a DNS forwarder forwards DNS requests to an external DNS resolver of an ISP, Quad9, Cloudflare, or similar service provider. We’ll configure the forwarder for the Clear network. In case the primary, secured networks lose connectivity, the Clear network can serve as a backup.

One of the advantages of self-hosting a DNS resolver is improved privacy. A resolver iteratively queries a chain of one or more DNS servers to resolve a request, so there isn’t a single instance knowing all your DNS requests. It comes at the cost of speed when resolving a hostname for the first time. As Unbound’s cache grows, the cost diminishes. We’ll configure our primary networks to use Unbound.

We’ll also keep DNS traffic from Unbound within the VPN tunnels. In the rare case of a VPN outage, we’ll want local DNS services to fail and not leak through the ISP WAN. The reason for this isn’t improved privacy as you might think. In some cases, this might even hurt your privacy. Why? Either your ISP or your VPN provider will see the iterative DNS requests Unbound sends. So it becomes a question of who you rather entrust with this data. But if there are no privacy benefits, why do it? Honestly, I don’t require such a setup. I configured it for educational purposes and fun. Other reasons that don’t affect me but other users are:

<div class="post-content" id="bkmrk-isp-selling-user-dat">- ISP selling user data
- ISP enforcing censorship
- ISP hijacking DNS traffic to redirect it to their DNS resolver; this makes self-hosting a DNS resolver impossible

</div>Let’s summarize our goals:

<div class="post-content" id="bkmrk-use-a-dns-resolver-f">- Use a DNS resolver for the management and VPN networks
- Resolve private domain hostnames for management and VPN networks
- Prevent DNS leaks from Unbound through the ISP WAN gateway
- Use DNS forwarding for the Clear network
- Use external DNS resolvers for the Guest network

</div>### Resolver (Unbound)

Navigate to **Services** → **Unbound DNS** → **General**.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-network-interfac"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Network Interfaces</td><td>`LAN` `VLAN10_MANAGE` `VLAN20_VPN`</td></tr><tr><td>DNSSEC</td><td>`checked`</td></tr><tr><td>DHCP registration</td><td>`checked`</td></tr><tr><td>DHCP static mappings</td><td>`checked`</td></tr><tr><td>Local Zone Type</td><td>`static`</td></tr><tr><td>Outgoing Network Interfaces</td><td>`WAN_VPN0` `WAN_VPN1`</td></tr></tbody></table>

</div>Navigate to **Services** → **Unbound DNS** → **Advanced**.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-hide-identity-ch"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Hide Identity</td><td>`checked`</td></tr><tr><td>Hide Version</td><td>`checked`</td></tr><tr><td>Prefetch Support</td><td>`checked`</td></tr><tr><td>Prefetch DNS Key Support</td><td>`checked`</td></tr><tr><td>Harden DNSSEC data</td><td>`checked`</td></tr></tbody></table>

</div>The final step is to add a custom [SOA record](https://www.cloudflare.com/learning/dns/dns-records/dns-soa-record/) to the local zone making Unbound the authoritative name server for `corp.example.com`. This way, we prevent Unbound from querying external name servers for the internal domain and exposing our network structure to the outside world. For [advanced Unbound configuration like this](https://docs.opnsense.org/manual/unbound.html#advanced-configurations), we use [Templates](https://docs.opnsense.org/development/backend/templates.html).

Connect to OPNsense via serial console or SSH and add a `+TARGETS` file by running `sudo vi /usr/local/opnsense/service/templates/OPNsense/Unbound/+TARGETS` containing:

```text
private_domains.conf:/usr/local/etc/unbound.opnsense.d/private_domains.conf
```

<div class="post-content" id="bkmrk-copy-3"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>Add the template file by running `sudo vi /usr/local/opnsense/service/templates/OPNsense/Unbound/private_domains.conf` containing:

```text
server:
  local-data: "corp.example.com. 3600 IN SOA opnsense.corp.example.com. root.example.com. 2021110201 86400 7200 3600000 3600"
```

<div class="post-content" id="bkmrk-copy-4"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>Here is a translation of what the SOA record means.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-corp.exampl"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`corp.example.com`</td></tr><tr><td>Record Type</td><td>`SOA`</td></tr><tr><td>Primary Name Server</td><td>`opnsense.corp.example.com`</td></tr><tr><td>Administrator Email</td><td>`root@example.com`</td></tr><tr><td>Serial</td><td>`2021110201` (YYMMDDnn)</td></tr><tr><td>Refresh</td><td>`86400` (24 hours)</td></tr><tr><td>Retry</td><td>`7200` (2 hours)</td></tr><tr><td>Expire</td><td>`3600000` (1000 hours)</td></tr><tr><td>TTL</td><td>`3600` (1 hour)</td></tr></tbody></table>

</div>Run the following to verify the configuration.

```shell
# generate template
configctl template reload OPNsense/Unbound
# show generated file
cat /usr/local/etc/unbound.opnsense.d/private_domains.conf
# check if configuration is valid
configctl unbound check
```

<div class="post-content" id="bkmrk-copy-5"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>### Forwarder (Dnsmasq)

Dnsmasq will forward DNS requests to the configured system DNS servers and `127.0.0.1` (Unbound). Earlier, you either explicitly configured them or decided to receive the DNS servers via DHCP from your ISP. Because Unbound already uses port 53, we’ll use port 5335 for Dnsmasq. We’ll later create rules to port forward DNS traffic to this port.

Navigate to **Services** → **Dnsmasq DNS** → **Settings**.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-enable-checked-l"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Enable</td><td>`checked`</td></tr><tr><td>Listen Port</td><td>`5335`</td></tr><tr><td>Do not forward private reverse lookups</td><td>`checked`</td></tr></tbody></table>

</div>Forward reverse DNS lookups in the `192.168.0.0/16` range to Unbound by adding the following **Domain Override**s. We additionally make Unbound the authoritative DNS server for `corp.example.com`.

<div class="post-content" id="bkmrk-domain-ip-descriptio"><table><thead><tr><th>Domain</th><th>IP</th><th>Description</th></tr></thead><tbody><tr><td>`168.192.in-addr.arpa`</td><td>`192.168.20.1`</td><td>Forward reverse lookups of private IP addresses to Unbound</td></tr><tr><td>`corp.example.com`</td><td>`192.168.20.1`</td><td>Make Unbound the authoritative DNS server for private domain</td></tr></tbody></table>

</div>## Firewall

Here is an overview of what we want to implement with firewall rules.

<div class="post-content" id="bkmrk-allow-internet-acces">- Allow internet access for specific ports through WAN and VPN
- Allow intranet communications
- Redirect outbound DNS traffic to either Unbound or Dnsmasq
- Redirect NTP traffic to OPNsense
- Block intranet access for the Guest network

<table><thead><tr><th> </th><th>VLAN10</th><th>VLAN20</th><th>VLAN30</th><th>VLAN40</th><th>LAN</th></tr></thead><tbody><tr><td>**Internet**</td><td>WAN</td><td>VPN + selective WAN</td><td>WAN</td><td>WAN</td><td>WAN</td></tr><tr><td>**Intranet**</td><td>pass</td><td>pass</td><td>pass</td><td>block</td><td>pass</td></tr><tr><td>**ICMP**</td><td>pass</td><td>pass</td><td>pass</td><td>pass</td><td>pass</td></tr><tr><td>**Anti-lockout**</td><td>yes</td><td>no</td><td>no</td><td>no</td><td>yes</td></tr><tr><td>**DNS**</td><td>Unbound</td><td>Unbound</td><td>Dnsmasq</td><td>external</td><td>Unbound</td></tr><tr><td>**NTP**</td><td>local</td><td>local</td><td>local</td><td>external</td><td>external</td></tr></tbody></table>

</div>### Interface Groups

We use [interface groups](https://docs.opnsense.org/manual/firewall_groups.html) to apply policies to multiple interfaces at once and reduce the number of required firewall rules significantly. Do not use them for WAN interfaces because they don’t use the `reply-to` directive!

I’m honestly not sure if I went overboard with interface groups and over-abstracted things. Currently, I’m happy with the configuration, and I guess only time will tell how maintainable this approach is. I’d like to know what you think and would very much appreciate your feedback.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/1TQGD1YFQKFibFte-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/1TQGD1YFQKFibFte-image.png)

Navigate to **Firewall** → **Groups** and add the following interface groups.

#### IG\_LOCAL

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ig_local-de"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`IG_LOCAL`</td></tr><tr><td>Description</td><td>`All local interfaces`</td></tr><tr><td>Members</td><td>`LAN` `VLAN10_MANAGE` `VLAN20_VPN` `VLAN30_CLEAR` `VLAN40_GUEST`</td></tr></tbody></table>

</div>#### IG\_OUT\_WAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ig_out_wan-"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`IG_OUT_WAN`</td></tr><tr><td>Description</td><td>`Interfaces allowing outbound WAN traffic`</td></tr><tr><td>Members</td><td>`LAN` `VLAN10_MANAGE` `VLAN30_CLEAR` `VLAN40_GUEST`</td></tr></tbody></table>

</div>#### IG\_OUT\_VPN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ig_out_vpn-"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`IG_OUT_VPN`</td></tr><tr><td>Description</td><td>`Interfaces allowing outbound VPN traffic and selective outbound WAN traffic`</td></tr><tr><td>Members</td><td>`VLAN20_VPN`</td></tr></tbody></table>

</div>#### IG\_DNS\_RESOLVE

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ig_dns_reso"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`IG_DNS_RESOLVE`</td></tr><tr><td>Description</td><td>`Interfaces forced to use Unbound`</td></tr><tr><td>Members</td><td>`VLAN10_MANAGE` `VLAN20_VPN`</td></tr></tbody></table>

</div>#### IG\_DNS\_FORWARD

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ig_dns_forw"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`IG_DNS_FORWARD`</td></tr><tr><td>Description</td><td>`Interfaces forced to use Dnsmasq`</td></tr><tr><td>Members</td><td>`VLAN30_CLEAR`</td></tr></tbody></table>

</div>#### IG\_NTP

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ig_ntp-desc"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`IG_NTP`</td></tr><tr><td>Description</td><td>`Interfaces forced to use OPNsense as NTP server`</td></tr><tr><td>Members</td><td>`VLAN10_MANAGE` `VLAN20_VPN` `VLAN30_CLEAR`</td></tr></tbody></table>

</div>### Aliases

We define a few reusable [aliases](https://docs.opnsense.org/manual/aliases.html) that help us condense our firewall rules. Some of them might become hard to maintain as they grow, in which case you might want to consider nesting aliases.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/MWrK4oO8LpKx0A7s-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/MWrK4oO8LpKx0A7s-image.png)

Navigate to **Firewall** → **Aliases** and create the following aliases.

#### Selective Routing Addresses

Services like banks might object to traffic originating from known VPN endpoints. We selectively route traffic from the VPN VLAN through the default WAN gateway.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-selective_r"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`SELECTIVE_ROUTING`</td></tr><tr><td>Type</td><td>`Host(s)`</td></tr><tr><td>Description</td><td>`External hosts reachable from IG_OUT_VPN networks through WAN`</td></tr></tbody></table>

</div>If you’re having issues with a service not working due to VPN, add the hostname to this alias, e.g., `netflix.com`.

#### Admin / Anti-lockout Ports

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ports_anti_"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`PORTS_ANTI_LOCKOUT`</td></tr><tr><td>Type</td><td>`Port(s)`</td></tr><tr><td>Content</td><td>`443` (Web GUI) `22` (SSH)</td></tr><tr><td>Description</td><td>`OPNsense admin ports`</td></tr></tbody></table>

</div>#### Ports Allowed To Communicate Between VLANs

Allowed ports for intranet traffic. Amend the list depending on your needs.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ports_out_l"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`PORTS_OUT_LAN`</td></tr><tr><td>Type</td><td>`Port(s)`</td></tr><tr><td>Description</td><td>`Ports allowed for intranet`</td></tr></tbody></table>

</div>Content:

<div class="post-content" id="bkmrk-53%C2%A0dns-5353%3A5354%C2%A0mdn">- `53` DNS
- `5353:5354` mDNS
- `123` NTP
- `21` FTP
- `22` SSH
- `161` SNMP
- `80` HTTP
- `8080`: HTTP alt / UniFi device and application communication
- `443` HTTPS
- `8443` HTTPS alt / UniFi application GUI/API as seen in a web browser
- `8880` UniFi HTTP portal redirection
- `10001` UniFi device discovery
- `5001` iPerf
- `623` IPMI
- `5900` VNC
- `3389` RDP
- `49152:65535` ephemeral ports

</div>#### Ports Allowed to Communicate with the Internet

Allow ports for *egress* internet traffic. Amend the list depending on your needs.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-name-ports_out_w"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Name</td><td>`PORTS_OUT_WAN`</td></tr><tr><td>Type</td><td>`Port(s)`</td></tr><tr><td>Description</td><td>`Ports allowed for internet`</td></tr></tbody></table>

</div>Content:

<div class="post-content" id="bkmrk-21%C2%A0ftp-22%C2%A0ssh-80%C2%A0htt">- `21` FTP
- `22` SSH
- `80` HTTP
- `8080` HTTP alt
- `443` HTTPS
- `8443` HTTPS alt
- `465` SMTPS
- `587`: SMTPS
- `993`: IMAPS
- `49152:65535` ephemeral ports

</div>##### A Fair Warning about Egress Filtering

As you add applications, you will be constantly amending the `PORTS_OUT_WAN` list. Depending on the application, the required ports may be poorly documented, so you’ll have to figure them out by inspecting the firewall logs. As other users have mentioned in the comments, blocking all egress traffic for all VLANs by default is probably not worth the hassle. Personally, I’ve given up on egress filtering altogether because of the administrative overhead that comes with it.

It is useful for high-security VLANs connecting devices such as cash registers in a retail store. Another example is VLANs with many untrusted IoT devices that have noisy telemetry. Putting them into a VLAN with egress filtering prevents them from “calling home”.

If you create the alias `ALL_PORTS` = `1:65535` and add it to the **Content** field of the `PORTS_OUT_WAN` alias, you can disable all egress filtering with the option of re-enabling it again later.

### NAT

Network Address Translation (NAT) is required to translate private to public IP addresses. We have the following requirements.

<div class="post-content" id="bkmrk-translate%C2%A0ig_out_wan">- Translate `IG_OUT_WAN` and `IG_OUT_VPN` network addresses to the `WAN` address range. Translating `IG_OUT_VPN` to `WAN` allows selective routing.
- Translate `IG_OUT_VPN` network addresses to the `WAN_VPN0` address range.

</div>[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/jKFsLOMRgj0yVN7X-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/jKFsLOMRgj0yVN7X-image.png)

Navigate to **Firewall** → **NAT** → **Outbound**.

Select `Manual outbound NAT rule generation` and add the following rules.

#### IG\_OUT\_WAN to WAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-interface-wan-so"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Interface</td><td>`WAN`</td></tr><tr><td>Source address</td><td>`IG_OUT_WAN net`</td></tr><tr><td>Description</td><td>`IG_OUT_WAN to WAN`</td></tr></tbody></table>

</div>#### IG\_OUT\_VPN to WAN

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-interface-wan-so-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Interface</td><td>`WAN`</td></tr><tr><td>Source address</td><td>`IG_OUT_VPN net`</td></tr><tr><td>Description</td><td>`IG_OUT_VPN to WAN`</td></tr></tbody></table>

</div>#### IG\_OUT\_VPN to WAN\_VPN0

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-interface-wan_vp"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Interface</td><td>`WAN_VPN0`</td></tr><tr><td>Source address</td><td>`IG_OUT_VPN net`</td></tr><tr><td>Description</td><td>`IG_OUT_VPN to WAN_VPN0`</td></tr></tbody></table>

</div>#### IG\_OUT\_VPN to WAN\_VPN1

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-interface-wan_vp-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Interface</td><td>`WAN_VPN1`</td></tr><tr><td>Source address</td><td>`IG_OUT_VPN net`</td></tr><tr><td>Description</td><td>`IG_OUT_VPN to WAN_VPN1`</td></tr></tbody></table>

</div>### Rules

Navigate to **Firewall** → **Rules**.

#### Anti-Lockout

Before adding any other rules, we add the anti-lockout ones on the `VLAN10_MANAGE` and `LAN` networks, so we can’t lock ourselves out. 😅

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/p5YRTeP7uWiXnmqT-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/p5YRTeP7uWiXnmqT-image.png)

Select **Floating** and add the following rule.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-pass-inte"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Pass`</td></tr><tr><td>Interface</td><td>`LAN` `VLAN10_MANAGE`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`any`</td></tr><tr><td>Destination</td><td>`This Firewall`</td></tr><tr><td>Destination port range</td><td>`PORTS_ANTI_LOCKOUT`</td></tr><tr><td>Description</td><td>`Anti-lockout`</td></tr></tbody></table>

</div>`This Firewall` is a pre-defined alias representing all interface addresses of OPNsense.

#### Allow Intranet Pings

We allow ICMP pings for the entire local network. Pings are maliciously abusable, so you may want to put stricter rules into place if required.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/kioKj7NJBFVfsrOM-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/kioKj7NJBFVfsrOM-image.png)

Select **IG\_LOCAL** and add the following rule.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-pass-inte-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Pass`</td></tr><tr><td>Interface</td><td>`IG_LOCAL`</td></tr><tr><td>TCP/IP Version</td><td>`IPv4`</td></tr><tr><td>Protocol</td><td>`ICMP`</td></tr><tr><td>ICMP type</td><td>`Echo Request`</td></tr><tr><td>Source</td><td>`IG_LOCAL net`</td></tr><tr><td>Description</td><td>`Allow intranet pings`</td></tr></tbody></table>

</div>#### Reject Intranet Traffic By Default

By default, we *reject* traffic on local interfaces instead of *blocking* it. *Block* drops packets silently. *Reject* returns a “friendly” response to the sender. To be able to override this rule, unchecking **Quick** is crucial! To use [Firewall Logs](https://docs.opnsense.org/manual/logging_firewall.html) to review blocked ports and amend our port list alias if necessary, we enable logging on this rule.

Select **IG\_LOCAL** and add the following rule.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-reject-qu"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Reject`</td></tr><tr><td>**Quick**</td><td>`unchecked`</td></tr><tr><td>Interface</td><td>`IG_LOCAL`</td></tr><tr><td>TCP/IP Version</td><td>`IPv4+IPv6`</td></tr><tr><td>Protocol</td><td>`any`</td></tr><tr><td>Source</td><td>`IG_LOCAL net`</td></tr><tr><td>Destination</td><td>`IG_LOCAL net`</td></tr><tr><td>Log</td><td>`checked`</td></tr><tr><td>Description</td><td>`Reject intranet traffic by default`</td></tr></tbody></table>

</div>#### Allow Intranet Traffic

We only allow intranet traffic on the ports defined in the `PORTS_OUT_LAN` alias. We’ll override this rule for the `VLAN40_GUEST` network later, so we must uncheck the **Quick** option again. For the Management network, you might want to consider stricter rules, as well.

Select **IG\_LOCAL** and add the following rule.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-pass-quic"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Pass`</td></tr><tr><td>**Quick**</td><td>`unchecked`</td></tr><tr><td>Interface</td><td>`IG_LOCAL`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`IG_LOCAL net`</td></tr><tr><td>Destination</td><td>`IG_LOCAL net`</td></tr><tr><td>Destination port range</td><td>`PORTS_OUT_LAN`</td></tr><tr><td>Description</td><td>`Allow intranet traffic`</td></tr></tbody></table>

</div>#### Allow Internet Traffic

We allow internet traffic on `PORTS_OUT_WAN` for `IG_OUT_WAN` networks.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/BQrV96AXmNBSrVxV-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/BQrV96AXmNBSrVxV-image.png)

Select **IG\_OUT\_WAN** and add the following rule.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-pass-quic-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Pass`</td></tr><tr><td>**Quick**</td><td>`unchecked`</td></tr><tr><td>Interface</td><td>`IG_OUT_WAN`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`IG_OUT_WAN net`</td></tr><tr><td>Destination / Invert</td><td>`checked`</td></tr><tr><td>Destination</td><td>`IG_LOCAL net`</td></tr><tr><td>Destination port range</td><td>`PORTS_OUT_WAN`</td></tr><tr><td>Description</td><td>`Allow internet traffic through WAN`</td></tr></tbody></table>

</div>We later want to enable unrestricted internet access on the Guest network, so make sure to uncheck the **Quick** option!

Next, we allow internet traffic on `PORTS_OUT_WAN` for the `IG_OUT_VPN` networks.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/ZmBm0tqtQG6ouXU5-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/ZmBm0tqtQG6ouXU5-image.png)

Select **IG\_OUT\_VPN** and add the following rules to configure selective routing.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-pass-inte-2"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Pass`</td></tr><tr><td>Interface</td><td>`IG_OUT_VPN`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`IG_OUT_VPN net`</td></tr><tr><td>Destination</td><td>`SELECTIVE_ROUTING`</td></tr><tr><td>Destination port range</td><td>`PORTS_OUT_WAN`</td></tr><tr><td>Description</td><td>`Allow selected internet traffic through WAN`</td></tr></tbody></table>

<table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Pass`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`IG_OUT_VPN net`</td></tr><tr><td>Destination / Invert</td><td>`checked`</td></tr><tr><td>Destination</td><td>`IG_LOCAL net`</td></tr><tr><td>Destination port range</td><td>`PORTS_OUT_WAN`</td></tr><tr><td>Description</td><td>`Allow internet traffic through WAN_VPN0`</td></tr><tr><td>Gateway</td><td>`WAN_VPN_GROUP`</td></tr></tbody></table>

</div>#### Restrict Guest Network

Select **VLAN40\_GUEST** and add the following rules.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/rb8JgScGyKy268qq-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/rb8JgScGyKy268qq-image.png)

To block Web GUI and SSH access from the Guest network, we block traffic to any OPNsense interface on the `PORTS_ANTI_LOCKOUT` ports. We enable logging for this rule to be able to see if any guests try to access OPNsense.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-block-int"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Block`</td></tr><tr><td>Interface</td><td>`VLAN40_GUEST`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`VLAN40_GUEST net`</td></tr><tr><td>Destination</td><td>`This Firewall`</td></tr><tr><td>Destination port range</td><td>`PORTS_ANTI_LOCKOUT`</td></tr><tr><td>Log</td><td>`checked`</td></tr><tr><td>Description</td><td>`Block admin ports`</td></tr></tbody></table>

</div>We block access to other local networks and also enable logging for the rule.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-block-int-1"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Block`</td></tr><tr><td>Interface</td><td>`VLAN40_GUEST`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`VLAN40_GUEST net`</td></tr><tr><td>Destination</td><td>`IG_LOCAL net`</td></tr><tr><td>Log</td><td>`checked`</td></tr><tr><td>Description</td><td>`Block traffic to local networks`</td></tr></tbody></table>

</div>Finally, we enable unrestricted internet access on Guest networks.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-action-pass-inte-3"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Action</td><td>`Pass`</td></tr><tr><td>Interface</td><td>`VLAN40_GUEST`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`VLAN40_GUEST net`</td></tr><tr><td>Destination / Invert</td><td>`checked`</td></tr><tr><td>Destination</td><td>`IG_LOCAL net`</td></tr><tr><td>Description</td><td>`Unrestricted internet access`</td></tr></tbody></table>

</div>#### LAN Network For Testing And Debugging

I just keep the pre-defined “LAN to any” rules. I periodically reconfigure this network for testing and debugging and don’t use it for anything else.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/kOf61HotHRVn33Nc-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/kOf61HotHRVn33Nc-image.png)

#### Redirect Outbound DNS Traffic

To prevent clients from explicitly querying outbound DNS and leaking information to the outside, we redirect any outbound DNS traffic to Unbound or Dnsmasq.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/gnnjqQoIQd2dCS5u-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/gnnjqQoIQd2dCS5u-image.png)

Navigate to **Firewall** → **NAT** → **Port Forward** and add the following rules.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-interface-ig_dns"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Interface</td><td>`IG_DNS_FORWARD`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`IG_DNS_FORWARD net`</td></tr><tr><td>Destination</td><td>`any`</td></tr><tr><td>Destination port range</td><td>`DNS`</td></tr><tr><td>Redirect target IP</td><td>`127.0.0.1`</td></tr><tr><td>Redirect target port</td><td>`5335`</td></tr><tr><td>Description</td><td>`Redirect any DNS traffic to Dnsmasq`</td></tr></tbody></table>

<table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Interface</td><td>`IG_DNS_RESOLVE`</td></tr><tr><td>Protocol</td><td>`TCP/UDP`</td></tr><tr><td>Source</td><td>`IG_DNS_RESOLVE net`</td></tr><tr><td>Destination / Invert</td><td>`checked`</td></tr><tr><td>Destination</td><td>`IG_DNS_RESOLVE net`</td></tr><tr><td>Destination port range</td><td>`DNS`</td></tr><tr><td>Redirect target IP</td><td>`127.0.0.1`</td></tr><tr><td>Redirect target port</td><td>`DNS`</td></tr><tr><td>Description</td><td>`Redirect outbound DNS traffic to Unbound`</td></tr></tbody></table>

</div>#### Redirect Outbound NTP Traffic

To sync the time of all our devices on the network to OPNsense, we redirect all NTP traffic.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/UfENcfdwu2rMYXoD-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/UfENcfdwu2rMYXoD-image.png)

Navigate to **Firewall** → **NAT** → **Port Forward** and add the following rule.

<div class="post-content" id="bkmrk-%C2%A0-%C2%A0-interface-ig_ntp"><table><thead><tr><th> </th><th> </th></tr></thead><tbody><tr><td>Interface</td><td>`IG_NTP`</td></tr><tr><td>Protocol</td><td>`UDP`</td></tr><tr><td>Source</td><td>`IG_NTP net`</td></tr><tr><td>Destination / Invert</td><td>`checked`</td></tr><tr><td>Destination</td><td>`IG_NTP net`</td></tr><tr><td>Destination port range</td><td>`NTP`</td></tr><tr><td>Redirect target IP</td><td>`127.0.0.1`</td></tr><tr><td>Redirect target port</td><td>`NTP`</td></tr><tr><td>Description</td><td>`Redirect outbound NTP traffic to OPNsense`</td></tr></tbody></table>

</div>## Test

Now would be a could time to reboot OPNsense to make sure all settings are applied.

### Test DHCP

Connect to a host in each VLAN and verify it receives an IP inside the specified DHCP range. Here is the output of the `ip -4 addr show eth0` command from a Ubuntu host connected to the VPN VLAN.

```text
8: eth0: <BROADCAST,MULTICAST,UP> mtu 1500 group default qlen 1
    inet 192.168.20.106/24 brd 192.168.20.255 scope global dynamic
       valid_lft 7196sec preferred_lft 7196sec
```

<div class="post-content" id="bkmrk-copy-6"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>### Test DNS

We have to verify the following functionality of our DNS architecture:

<div class="post-content" id="bkmrk-vlan20_vpn-unbound%C2%A0r">- `VLAN20_VPN`
    - Unbound *resolves* remote and local hostname lookups
    - Redirect outbound DNS traffic to Unbound
    - Reverse lookups of private IPs
    - Don’t leak lookups for the private `corp.example.com` domain
- `VL30_CLEAR`
    - Dnsmasq *forwards* remote hostname lookups to the system DNS servers like Quad9 *and* Unbound
    - Forward local hostname lookups to Unbound
    - Redirect outbound DNS traffic to Dnsmasq
    - Forward local reverse lookups of private IPs to Unbound
    - Don’t leak lookups for the private `corp.example.com` domain and forward them to Unbound
- `VL40_GUEST`
    - Use external DNS resolvers
    - Allow for clients to override DNS
    - OPNsense lookups are blocked

</div>We’ll use the `dig` tool and the firewall logs under **Firewall** → **Log Files** → **Live View** for testing.

I’ll also skip the Management network because it requires the same testing as the VPN network.

#### VLAN20\_VPN: Test DNS

Connect to `VLAN20_VPN`.

##### VLAN20\_VPN: Remote Hostname Lookups

Run `dig california.gov`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> california.gov
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41004
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;california.gov.                        IN      A

;; ANSWER SECTION:
california.gov.         300     IN      A       63.196.102.29

;; Query time: 36 msec
;; SERVER: 192.168.20.1#53(192.168.20.1)
;; WHEN: Tue Nov 16 22:37:34 CET 2021
;; MSG SIZE  rcvd: 59
```

<div class="post-content" id="bkmrk-copy-7"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>Here are the firewall logs showing the iterative DNS requests Unbound sends.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/MptYCMcLoMsmaSge-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/MptYCMcLoMsmaSge-image.png)

##### VLAN20\_VPN: Local Hostname Lookups

Run `dig opnsense.corp.example.com`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> opnsense.corp.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22291
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;opnsense.corp.example.com.  IN      A

;; ANSWER SECTION:
opnsense.corp.example.com. 3600 IN   A       192.168.1.1
opnsense.corp.example.com. 3600 IN   A       192.168.10.1
opnsense.corp.example.com. 3600 IN   A       192.168.20.1

;; Query time: 0 msec
;; SERVER: 192.168.20.1#53(192.168.20.1)
;; WHEN: Tue Nov 16 21:48:19 CET 2021
;; MSG SIZE  rcvd: 105
```

<div class="post-content" id="bkmrk-copy-8"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>##### VLAN20\_VPN: Redirect Outbound DNS Traffic

Run `dig opnsense.org @8.8.8.8`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> opnsense.org @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17970
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;opnsense.org.                  IN      A

;; ANSWER SECTION:
opnsense.org.           184     IN      A       178.162.131.118

;; Query time: 0 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Tue Nov 16 21:51:15 CET 2021
;; MSG SIZE  rcvd: 57
```

<div class="post-content" id="bkmrk-copy-9"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>`dig` can’t tell that OPNsense hijacked the request and thus displays an incorrect `SERVER` value. If you check the firewall logs, you shouldn’t see any requests to `8.8.8.8`. Instead, you should see iterative root server requests.

##### VLAN20\_VPN: Reverse Lookups of Private IPs

Run `dig -x 192.168.20.1`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> -x 192.168.20.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 9264
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;1.20.168.192.in-addr.arpa.     IN      PTR

;; ANSWER SECTION:
1.20.168.192.in-addr.arpa. 3600 IN      PTR     OPNsense.corp.example.com.

;; Query time: 0 msec
;; SERVER: 192.168.20.1#53(192.168.20.1)
;; WHEN: Tue Nov 16 21:56:14 CET 2021
;; MSG SIZE  rcvd: 96
```

<div class="post-content" id="bkmrk-copy-10"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>If you want, additionally reverse-lookup an IP that doesn’t exist. The firewall logs mustn’t contain requests to external DNS servers.

##### VLAN20\_VPN: Verify `corp.example.com` Is Private

To test whether OPNsense is the authoritative server for `corp.example.com`, we lookup a non-existent hostname in that domain. `dig` should return an authoritative `NXDOMAIN` response with the SOA record we earlier defined earlier in the `AUTHORITY SECTION`.

Run `dig nowhere.corp.example.com`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> nowhere.corp.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 44590
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;nowhere.corp.example.com.   IN      A

;; AUTHORITY SECTION:
corp.example.com.    3600    IN      SOA     opnsense.corp.example.com. root.example.com. 2021110201 86400 7200 3600000 3600

;; Query time: 0 msec
;; SERVER: 192.168.20.1#53(192.168.20.1)
;; WHEN: Tue Nov 16 21:59:58 CET 2021
;; MSG SIZE  rcvd: 110
```

<div class="post-content" id="bkmrk-copy-11"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>##### VLAN20\_VPN: DNS Leak Test

In your browser, navigate to [dnsleaktest.com](https://dnsleaktest.com/) or [mullvad.net/check](https://mullvad.net/en/check). We expect the “leaked” DNS server to match our Mullvad public Mullvad IP. The second leak is from the **Outgoing Interface** we configured for Unbound:

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/n4d7qqjA6PmTHfhj-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/n4d7qqjA6PmTHfhj-image.png)

#### VLAN30\_CLEAR: Test DNS

Connect to `VLAN30_CLEAR`.

##### VLAN30\_CLEAR: Remote Hostname Lookups

Run `dig opnsense.org`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> opnsense.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65053
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;opnsense.org.                  IN      A

;; ANSWER SECTION:
opnsense.org.           596     IN      A       178.162.131.118

;; Query time: 5 msec
;; SERVER: 192.168.30.1#53(192.168.30.1)
;; WHEN: Tue Nov 16 16:45:08 CET 2021
;; MSG SIZE  rcvd: 57
```

<div class="post-content" id="bkmrk-copy-12"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>Check the firewall logs. Enable logging for the port forward rule if you want it to show up.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/f4VVnlL9Q3ixMSG4-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/f4VVnlL9Q3ixMSG4-image.png)

You can see that Dnsmasq forwards to the DNS servers defined under **System** → **Settings** → **General** *and* Unbound.

##### VLAN30\_CLEAR: Local Hostname Lookups

Run `dig opnsense.corp.example.com`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> opnsense.corp.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61385
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;opnsense.corp.example.com.  IN      A

;; ANSWER SECTION:
opnsense.corp.example.com. 1 IN      A       192.168.1.1

;; Query time: 0 msec
;; SERVER: 192.168.30.1#53(192.168.30.1)
;; WHEN: Wed Nov 17 00:45:49 CET 2021
;; MSG SIZE  rcvd: 73
```

<div class="post-content" id="bkmrk-copy-13"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>##### VLAN30\_CLEAR: Redirect Outbound DNS Traffic

Run `dig opnsense.org @8.8.8.8`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> opnsense.org @8.8.8.8
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 34638
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;opnsense.org.                  IN      A

;; ANSWER SECTION:
opnsense.org.           430     IN      A       178.162.131.118

;; Query time: 3 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Tue Nov 16 00:12:15 CET 2021
;; MSG SIZE  rcvd: 57
```

<div class="post-content" id="bkmrk-copy-14"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>We confirm it works by looking at the firewall logs again:

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/Bw7kapI3oiWSTUDJ-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/Bw7kapI3oiWSTUDJ-image.png)

##### VLAN30\_CLEAR: Forward Reverse Lookups of Private IPs to Unbound

Run `dig -x 192.168.20.1`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> -x 192.168.20.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20607
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;1.20.168.192.in-addr.arpa.     IN      PTR

;; ANSWER SECTION:
1.20.168.192.in-addr.arpa. 3600 IN      PTR     OPNsense.home.schnerring.net.

;; Query time: 1 msec
;; SERVER: 192.168.30.1#53(192.168.30.1)
;; WHEN: Wed Nov 17 00:09:05 CET 2021
;; MSG SIZE  rcvd: 96
```

<div class="post-content" id="bkmrk-copy-15"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>The firewall logs confirm it works.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/FbjujbORRIL351qF-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/FbjujbORRIL351qF-image.png)

##### VLAN30\_CLEAR: Verify `corp.example.com` Is Private

Run `dig nowhere.corp.example.com`:

```text
; <<>> DiG 9.16.1-Ubuntu <<>> nowhere.corp.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 7481
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;nowhere.corp.example.com. IN      A

;; AUTHORITY SECTION:
corp.example.com.    3600    IN      SOA     opnsense.corp.example.com. root.example.com. 2021110201 86400 7200 3600000 3600

;; Query time: 0 msec
;; SERVER: 192.168.30.1#53(192.168.30.1)
;; WHEN: Tue Nov 16 20:44:35 CET 2021
;; MSG SIZE  rcvd: 112
```

<div class="post-content" id="bkmrk-copy-16"><div class="highlight"><div class="code-toolbar"><div class="toolbar">  
</div></div></div></div>This time, requests will only be forwarded to Unbound, but not external DNS resolvers.

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/XmlSAhoy44iSSrHt-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/XmlSAhoy44iSSrHt-image.png)

##### VLAN30\_CLEAR: DNS Leak Test

As we saw earlier, we expect the Quad9 *and* the Mullvad public IPs to leak. Here is the result of an extended test from [dnsleaktest.com](https://dnsleaktest.com/):

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/SqzMxp5aoV3FkjXp-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/SqzMxp5aoV3FkjXp-image.png)

#### VLAN40\_GUEST: Test DNS

Connect to `VLAN40_GUEST`.

Verify that `dig opnsense.org @192.168.40.1` times out.

The Cloudflare DNS servers you configured in the DHCP settings of the Guest VLAN should show up when running the leak test:

[![image.png](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/scaled-1680-/vzUbD1XTKJYBLyDT-image.png)](https://wikipedia.mutschlerhome.com/uploads/images/gallery/2025-02/vzUbD1XTKJYBLyDT-image.png)