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.
- 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.
- For Step 2, enter a hostname, and a domain name (the latter can be left as 'local').
- For Step 3, we can leave the default Network Time Protocol server unless you have personal preference.
- 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.
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.
- 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.
-
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.
- 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.
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.
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.
- 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.
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.
- Navigate to System >> Package Manager >> Available Packages. Search for '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.
Firstly, navigate to VPN >> Wireguard >> Settings, and Enable 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.
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.
- 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:
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!
- 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.
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)
-
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:
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.
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.
-
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.
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:
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.
-
Here's my old set up using a pfSense firewall, a Unifi Flex Mini Switch, an OpenWRT router, and a virtualization server.
Let's define a DMZ NET using a VLAN on pfSense:
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.
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'.
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.
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.
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!
- 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.
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.
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.
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
There we have it. The VLAN has been completely configured on the LAN port.
- Next, we can enable DHCP on the interface rather than statically asssigning IPs to each device using the VLAN 90 tag.
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.
-
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.
Select 'New Virtual Network'.
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.
Select the port physically connected with the server we wish to place in the DMZ NET.
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!
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.
Remember that pfSense firewall zones follow an implicit deny principle—if no rules are set, all traffic is blocked by default.
Now, based on what we learned earlier about making firewall rules, I made a rule to allow ICMP packets of any type.
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!