Migrating to pfSense!

Blocking the bad guys, and letting the memes through!

Published: 2025-01-27 | Updated: 2025-02-24

Project Overview

 pfSense is a free and open-source platform for network security. We'll explore the initial configuration, firewall rules on different interfaces, VPNs, and unbelievably useful inbuilt troubleshooting tools. We will also take a look at segmenting your network for your Internet of [Unsecure] Things (IoT) devices. The hardware used in this project includes a Netgate firewall, a home built virtualization server (for demonstration using virtualized pfSense rather than my production environment), a GL.iNet router running OpenWRT, and a Ubiquit Flex Mini [Managed, Layer 2] switch.

Key Features

Procedures

Step-by-step instructions or processes involved in this project:

The Set-Up/Initial Configuration:

  Preface: pfSense natively supports Netgate appliances, and x86 / AMD64 hardware. On non-Netgate appliances, pfSense Community Edition can be downloaded, and installed via VM or installed bare metal. I recommend bare metal or Netgate. That being said, I will be using a VM for the purposes of demonstration, so let's proceed with the setup.

  1. If you have a Netgate appliance, connect it to the WAN (your ISP connection), then connect your computer to the LAN port of the Netgate device. If you're using an x86/AMD bare metal deivce, like an Intel based computer with multiple ports on a Network Interface Card (NIC), you would have selected a WAN and LAN during your set up. Once connected to the LAN, head over to 'https://192.168.1.1'. Let's login with the default username: 'admin', and password: 'pfSense'. Immediately we will be entered into the setup Wizard.
    hostname and domain name picture

  2. For Step 2, enter a hostname, and a domain name (the latter can be left as 'local').

  3. For Step 3, we can leave the default Network Time Protocol server unless you have personal preference.

  4. For Step 4, assuming our WAN address is connected to your Internet Service Provider, and that we are not paying for a permanent (static) address, select 'DHCP' for configuring the WAN interface.
    bogon and private networks pictue

    At the end of the page (see image above) of Step 4, since the WAN is exposed to the Internet, ensure both 'Block RFC1918 Private Networks' and 'Block bogon networks' are checked.

    * Take a read of RFC 1918. You'll see mention of this RFC many times throughout your career. We block them on the WAN because according to RFC1918, "private addresses have no global meaning" - at best, they're on the Internet by error, at worst, they might come from illicit threat actors. As for Bogon addresses (Bogus addresses), they're also usually the result of misconfiguration or a threat actor.

  5. At Step 5, we define our LAN network's IP and Subnet. Depending on the size of your network, you may need a larger address sace (A class A network 10.0.0.0/8, for example) -- if you're an Enterprise. I may make a lesson on subnetting sometime in the future. I defined my LAN as 192.168.32.1/24 (Subnet Mask 255.255.255.0).
    There we go.
    Accept the terms of service, then we can follow through with changing our default password using the banner/warning on the home page.

Firewalls, Firewalls, Walls of Fire

  Let's talk firewalls - if you are familiar with the OSI model, firewalls are packet filters are wedged in between the data-link layer and the network layer. That is a convenient place since it is the first place Layer 3 routing information (IP addresses) are seen when a packet is received on a device (our firewall in this case). This is an ideal place to begin inspecting the packet, before it gets to its destination.
Let's set up a few rules, and discuss their impact on our network. Note: if a rule does not match existing rules, it is blocked by default.

  1. WAN default rules

    Navigate to Firewall >> Rules, where the default page is the 'WAN' rules. Here we see a few important columns and some default rules that we set up by checking Block RFC1918, and Bogon networks earlier.
    Let's briefly discuss the columns:
    • States: pfSense is a stateful firewall. A stateful firewall stores metadata about active connections in the firewall's memory. It uses this memory to intelligently allow traffic to go back and forth between the devices/communicators, without having a rule for each specific ingress (traffic entering) or egress (traffic exiting) flow.
    • A few things the pfSense firewall stores in memory include the protocol being used, the ports, the source and destination addresses, and even Transmission Control Protocol (TCP) flags: a [SYN,ACK] packet is typically expected in response to a [SYN] flagged packet. Impressive, right? A stateful firewall relies on fixed rules describing exactly what to allow, based on what the administrator expects on the network.


    • Protocol: IP (OSI Layer 3) packets have this thing called a protocol field. It's an 8-bit section of the IP header that indicates the type of packet. For example, 1 indicates ICMP, and 6 indicates a TCP packet, where as 17 indicates UDP.
    • pfSense only asks for the name of the protocols we wish to allow: TCP, UDP, GRE, etc.

    • Source & Destination: These fields identify from where the packet should originate, and to where it should be marked to arrive, respectively.

    • Port: Ports are OSI Layer 4 information that also has significance at the Session layer (OSI Layer 5). They represent somewhat distinct channels of communication between two hosts, and the particular services on each host.

    • Description: A good way to let our future selves know what we were thinking/intending when we made the rule that accidentally blocked all of Europe. Otherwise known as short hand documentation.

    • Queue & Schedule: These are nifty features in the advanced section of pfSense firewall a rule used for prioritizing traffic, and chronologizing your rules.


  2. Let's make some rules. Navigate to 'LAN' instead of 'WAN'. Here we should see some default rules including an Anti-Lockout rule which prevents us from locking yourself out of pfSense. Of course, this can be turned off if we wish.
    LAN default rules

    Hit the green 'Add to the Top' button. Our objective is to block all DNS traffic directed at anywhere else, but our pfSense device. I should mention that pfSense uses Unbound and performs its own recursive DNS lookups, so we do not need devices on our network attempting to have other DNS providers perform those queries on behalf of our network.

    Of course, this rule only only protects rouge devices or applications from using alternative DNS servers since the DNS server is usually defined by the Dynamic Host Configuration Protocol (DHCP) Server.

    Why would we do this? Privacy. No one (public DNS service providers) needs to know which websites we are looking for.
    making a firewall rule

    Action => Block
    Interface => LAN
    Address Family => IPv4
    Protocol => UDP
    Source => LAN Subnets
    Destination => check 'Invert match' This Firewall (self)
    Destination Port Range => DNS(53)

    Key notes:
    • I prefer to block rather than reject. Rejecting a packet lets the sender know that your firewall exists, and it uses more bandwidth and resources. Rejecting can be useful for internal troubleshooting, making it more applicable on the LAN. Blocking silently drops the packet.
    • DNS usually uses UDP. DNS request can use TCP if a request returns larger amounts of traffic (greater than 512 bytes), such as the more secure DNSSEC traffic, etc.
    • I kept the address family as IPv4 as I disable IPv6 on all my homelab devices as of this publishing.
    • We invert the match to say that anything to port 53 that is not (!) destinated for This Firewall (self), we block.
    a DNS rule



  3. Here are some of the old firewall rules on the LAN side of my home lab. It's a little messy, and has since improved. I share the old setup because I rather not equip a penetration tester with enough knowledge for a Grey Box pentest, haha.
    old firewall rules



pfSense & Wireguard:

  The Netgate pfSense equipped hardware comes pre-packaged with Wireguard. For pfSense Community Edition, we get to download the package! We will also discuss generating public and private keys for your 'client'/peer. We will also create the two firewall rules necessary for Wireguard.

  1. Navigate to System >> Package Manager >> Available Packages. Search for 'Wireguard'.
    installing Wireguard

    Let's install it.
    Once that is done, we will see Wireguard under the dropdown VPN menu on our pfSense Web GUI. Let's navigate there.
    installing Wireguard

    Firstly, navigate to VPN >> Wireguard >> Settings, and Enable Wireguard.
    enabling Wireguard

    Now, lets make a tunnel! Select 'Add Tunnel'. We will see that by default, our tunnel is named 'tun_wg0'. Enable the tunnel, describe it, and input a listen port! Ideally, the listen port should be somewhere in the emphemeral port range for Windows (49152–65535). I went with 59090.
    creating a tunnel

    Now, we generate some keys for the 'server' (the peer that is your pfSense). Click 'Generate'.
    Now this is important: for 'Interface Addresses', we must define and select a network that is not already in use within our home lab.

    Ideally, it should be a subnet not used at other places you frequently use your VPN, so try to avoid 192.168.0.0/16 or even 10.0.0.0/8. Less commonly used is 172.16.0.0/12, so we can use that.

    I used 172.16.24.1/24 to define the network space for peers using this tunnel.

  2. Now, let's define a peer. It's a tasty fruit native to Asia, Europe, and North Africa. Okay, seriously now, let's configure the other peer or multiple peers (each device you wish to use the pfSense wg tunnel.)

    We need keys. The best way I know how to do that is to use the wg-quick tools. Run this command on your debian based system like the Raspberry Pi we set up a while ago:
    sudo apt install Wireguard-tools
    Once installed, let us run the following:
    wg genkey > priv.key

    wg pubkey < priv.key > pub.key

    cat pub.key
    So, Wireguard uses public keys to identify peers. We just used to wg-tool to generate a private key expressed in base64, the we used the private key to derive the public key.
    Wiregaurd uses a 32 byte Curve25519 key for the Elliptic Curve Diffie-Hellman (ECDH) key exchange - a way to exchange keys confidentially over a public (unsecure) medium like the Internet.
    We 'cat' the public key to use it to configure the peer on the pfSense firewall. We will use the private key later.

    Now let us configure the peer on pfSense:
    creating a peer

    Enable the peer, select the tunnel we just created, describe it, leave the endpoint as dynamic, paste the public key we created using the wg-tool, and generate a Pre-shared Key. According to the whitepaper, Pre-Shared Keys are a good defense against Harvest Now, Decrypt Later Tactics, Techniques & Procedures, and even Quantum Computing advances.
    Keep the Pre-Shared Key on standby (copy it).

    Now, for 'Allowed IPs', give the peer an IP address from within the tunnel's network: 172.16.24.2/32 for example. Unless the peer (your laptop/phone) pulls an IP from a subnet of available IPs, a specific fixed /32 address will suffice!

  3. Now we configure the peer that is our computer, or phone, etc. Though this example will illustrate a Windows computer's confiuration, the idea is the same on iOS, Mac, and Android devices. Note: Never make your private keys public. I left it here for the example, and replaced it immediately.
    connecting a peer

    This Configuration File Format Example can also befound using the command:
    man wg
    But I will paste the outline here for you as well:

    [Interface]
    PrivateKey =
    Address =
    DNS =

    [Peer]
    PublicKey =
    PresharedKey =
    AllowedIPs = 0.0.0.0/0
    Endpoint = PUBLIC ADDRESS:59090

    Start off with pasting the private key, which we get from our wg-tools earlier. Use the command where your private key we generated earlier is located:
    cat priv.key
    Then populate the other fields, including the Pre-Shared Key we generated earlier. Save your tunnel.

    Note that I used the pfSense firewall as my DNS server. (192.168.32.1)

  4. Now before we activate our tunnel, we need to establish some firewall rules to allow this otherwise strange Wireguard VPN web traffic attempting to connect to our network.
    Navigate to Firewall >> Rules >> Wireguard. Here we will see no rules for the Wireguard interface. Remember, if a rule does not match any existing rules, it is blocked byuu default. Something like a fail-safe measure.
    Create a new rule:
    Wireguard any rule

    Action => Pass
    Interface => Wireguard
    Address Family => IPv4
    Protocol => Any
    Source => Any
    Destination => Any
    Description => XYZ

    Next, we need to allow inbound traffic on our WAN interface for the specific WireGuard port we configured in our tunnel earlier. This ensures remote devices can establish a VPN connection.
    wan Wireguard rule

    Action => Pass
    Interface => WAN
    Address Family => IPv4
    Protocol => UDP
    Source => Any
    Destination => WAN Address
    Port => (other) 59090
    Description => ZXY

    Wireguard traffic from the WAN explicitly uses UDP. Save the rule, and there you have it. Your Wireguard tunnel acan be activated, but you will only have access to your internal (home lab) network.

  5. So, we need to NAT all Wireguard traffic on the network, that wants to access the Internet. To do so, navigate to Firewall >> NAT >> Outbound.
    Wireguard NAT

    Switch the Outbound NAT Mode to 'Hybrid Outbound NAT rule generation'. That way, we can include specific rules for outbound NAT configuration.
    As for the the rule itself:
    Wireguard NAT2

    Interface => WAN
    Address Family => IPv4
    Protocol => Any
    Source => Network or Alias => 172.16.24.0/24
    Destination => Any
    Translation Address => WAN Address

    Now save your rule, and connect to the web using your own personal VPN!


Network Segmentation:

  Segmentation is a wonderful way to manage different portions of your homelab. For example, we may want to assign priority to traffic from a segment that we dedicate to a home media server, or even even an incredibly popular self-hosted website. At the same time, it allows you to apply rules to secure vulnerable portions of your network: that very vulnerable self-hosted website that you really should be proxying.

Segmenting a network usually entails VLANs (OSI Layer2 segmentation), and IP subnetting (OSI Layer 3 segmentation). I find it incredibly useful for limiting Internet of [unsecure] Things (IoT) devices fingerprint/reach in your home. For this section, let's just discuss some basics with examples using my old set up. For the sake of brevity, I will put links to resources, rather than expounding on every new term.

This section uses the [physical] NetGate pfSense firewall.

  1. Here's my old set up using a pfSense firewall, a Unifi Flex Mini Switch, an OpenWRT router, and a virtualization server.
    segmented homelab

    Let's define a DMZ NET using a VLAN on pfSense:
    segmented image 1

    Navigate to Interfaces >> VLANs, and hit 'Add'. Input '90' for your VLAN tag. Also, notice how there are only two interfaces on this device. Again, I used the NetGate firewall for this section. It will look different for the virtualized pfSense. See the Problems section for more information.
    segmented image 2

    Now, let's navigate to back to Interfaces >> Interface Assignments. Under the drop down menu for 'Available network ports', select the VLAN we just created. Hit 'Add'.
    segmented image 4

    Great! We can see we have a new [virtual/logical] interface to mess around with. This interface will have similar properties to our LAN and WAN interface. So, we'll be able to make firewall rules, and such.
    Now, let's click on the new interface. By default, it is called 'OPT3' for my device.
    segmented image 5

    Under 'General Configuration', let's enable the interface, give it a description, and set the IPv4 Configuration Type as Static IPv4, since we will be defining this IP subnet directly.
    segmented image 6

    Under 'Static IPv4 Configuration', we can define our network. For my DMZ, I decided to keep the network space pretty small by using the 192.168.90.192/29 subnet, or 255.255.255.248 Subnet Mask. I do not expect many devices in the DMZ, but I can always supernet if needed.
    If you prefer something simpler or larger, feel free to use a /24, for example: the 192.168.90.0/24 network.

    Ensure that you use the first available host address in your subnet and not the network address itself! 192.168.90.1/24 would be the first address for the 192.168.90.0/24 network.
    Save the Interface configuration! Apply the changes. It will take a while!

  2. Now, this is important, and I will explain why in the Problems section: The NetGate 1100 pfSense firewall only has 1 real physical interface(as indicated by the one real MAC address that the firewall has). WAN, LAN, and OPT all share the same chip and MAC Address. However, these interfaces are separated logically using VLANs by default.
    We need to specify which VLANs are tagged on which interface (LAN/OPT, or both). Tagging the interface allows VLAN traffic for that VLAN on the interface.
    segmented image 7

    In the image above, Port 0 is the system chip itself. OPT, LAN, and WAN use ports 1, 2, and 3 respectively. Also, note the Virtual IDs (VIDs). These are the default system VLANs to define traffic belonging to the physical ports.

    Notice that the default tags are applied to the system chip (0) but not to the port. This is because traffic for the LAN, on the actual physical LAN port, does not need to be tagged. It is said to be native to that port.
    However, as soon as that traffic is sent to the system chip 0, a tag is applied to indicate where it came from using the OSI Layer 2 802.1Q VLAN tagging.

    segmented image 8

    Navigate to Interfaces >> Switch >> VLANs.
    VLANs 10 and 20 are VLANs made by me for another project. Everything else is default. Click 'Add Tag' to add our new VLAN 90 for the DMZ NET VLAN that we just made.

    segmented image 9

    Enter the VLAN number for the DMZ NET VLAN we made. Let's enter a description. Then, we tag traffic for VLAN 90 for the system chip always, then the interface that expects this tagged traffic. In our case, LAN.
    LAN uses port number 2. Be sure to check 'tagged'. Save your configuration
    segmented image 10

    There we have it. The VLAN has been completely configured on the LAN port.

  3. Next, we can enable DHCP on the interface rather than statically asssigning IPs to each device using the VLAN 90 tag.
    segmented image 11

    Navigate to Services >> DHCP Server >> DMZ NET.
    Enable DHCP, then under 'Primary Address Pool', input an 'Address Pool Range'. We can use something like 'From' 192.168.90.100 'To' 192.168.90.200 if you used the /24 network described earlier.,
    For my example, I used 192.168.90.194 to 192.168.90.197. My pool is pretty small, and I do not need to keep space for static IPs. Though, for a DMZ based server, you may want to reserve IPs for each device in a DMZ since they are constantly being contacted.

  4. Onto the Ubiquiti Flex Mini!

    This is possibly the most affordable managed switch. (An unamanged switch just forwards traffic — usually an Access Layer switch) One of my favorite features is it's ability to Port Mirror — replicate traffic to a monitoring device for troubleshooting.
    Here's a tutorial for setting up the UniFi Controller which is needed to configure/manage the switch. Only ensure that both the Flex Mini and the Controller are on the same subnet/network.

    Once you have logged in to the controller, and adopted the Flex Mini under the 'UniFi Devices' section, navigate to Settings >> Networks.
    unifi network settings menu

    Select 'New Virtual Network'.
    making a vlan

    Name your VLAN, then assign the same VLAN ID that we used over in our pfSense device. Let's save our work.
    > Now one thing you might notice is that the UniFi controller has options for things that do not pertain to our OSI Layer 2 switch. So, we can ignore any OSI Layer 3 displayed information for now.

    Now navigate to the Ports menu.
    the ports menu

    Select the port physically connected with the server we wish to place in the DMZ NET.
    assigning the VLAN to a port

    Apply the configuration. Getting your device to perform a DHCP DISCOVER request could be done on the CLI, but you can also just unplug then plug it again. That often works.

    Now, here we have it!
    server assigned to VLAN subnet

    I spun this VM up to demonstrate it. The pfSense firewall sees it 802.1Q VLAN 90 tag when the server makes a DHCP Discover request.
    So, it DHCP Offers an address from the DHCP pool we defined for VLAN 90: 192.168.90.194/29.

    Also, notice that we can not ping the Internet, or even the gateway: 192.168.90.193/29.
    no rules

    Remember that pfSense firewall zones follow an implicit deny principle—if no rules are set, all traffic is blocked by default.
    allowing pings

    Now, based on what we learned earlier about making firewall rules, I made a rule to allow ICMP packets of any type.
    pings flowing

    Now we can ping the gateway for that subnet, and even hosts on the Internet. Good work! Now feel free to try making a zone for your WLAN devices using VLANs and Subnets!

Problems and Troubleshooting

This section highlights issues encountered during the project and the steps taken to resolve them:

  1. VLAN Configuration:
      The NetGate 1100 pfSense Switch board: I said that I would discuss it in the problem section, but I must have gotten carried away earlier in the Network Segmentation section. A little more discussion, nevertheless:

    only one switch

    We see that the firewall only has one switch board, and one "Burned In MAC Address". Note that we can only have a maximum of 128 VLANs even though the VLAN IDs can range from 1 to 4095, excluding the default VLANs unless you wish to reconfigure those.
    Note also that the 802.1Q VLAN tagging uses the IEEE 802.1Q standard, and so does the Ubiquiti switch. As such, they can understand each other's 802.1Q VLAN tagging standard.

    For some reason, the NetGate pfSense documentation is not very clear on understand the 1100 firewall. Nevertheless, always ensure that the traffic is tagged for that VLAN on the port you expect that tagged traffic to ingress/egress within your network.
    tags again



  2. Wireshark Troubleshooting:
    This project does not go into detail about how 802.1Q VLAN tags are applied and the depth of their operation, but I saw it fit to include a packet capture of 802.1Q VLAN traffic on my lab network from VLAN 70.
    a pcap of VLAN tagged packets

    Context: The ICMP packet was caught from an interface on my pfSense machine before it was stripped of the 70 VLAN tag before being passed on to the Internet. (The VLAN tag would not have any meaning there, so it is removed).
    In a Wireshark packet, a VLAN tag is identified by "802.1Q Virtual LAN"
    Expanding the packet, we see the VLAN tag as the number 70, for VLAN 70. Again, VLAN tags are 12 binary bits long, ranging from 0 to 4095, so there are 4096 VLANs. Most usecases would not require more than that.
    expanded pcap of VLAN tagged packets

    We see the binary number 0000 0100 0110 which equates to 70.

    We also see Priority information, which uses 3 bits, and has 7 levels of priority (excluding 0 default). If we retrace the steps of creating a VLAN on the pfSense device, we would see the option for assinging priority to VLAN tagged traffic.

  3. General Problems:
    • Be sure to enable 802.1Q VLAN tagging for pfSense
      enable 802.1Q VLAN tagging pfsense

    • Enable DHCP! Unless you are statically assigning IPs on each of your devices.
      enable dhcp

    • Enable each of your Wireguard peers if you want to use them!
      enable Wireguard peer

    • Ensure the peer subnet mask is /32, unless otherwise needed.
      correct wg peer mask

      Be sure to use the correct mask for your Wireguard peer (/32) and the tunnel (/24, for our example).

Conclusion

There we have it! We set up and configured some robust hardware. We swapped out our Raspberry Pi Wireguard based VPN peer for the pfSense supported Wireguard peer, which offers us some more control of our network, we discussed firewall rules broadly, and we began to segment our network to reduce the attack surface of our network, while making it more efficient. This guide covers the fundamental aspects of configuring pfSense, but there's always more to explore. Consider the firewall rules, as well. Should your devices really be talking to "Bananaland" servers at 3:62AM at night? Probably not. Do not be afraid of breaking things, unless you're in a production environment, of course.

Happy networking!