# Double Firewall Hopping with PfSense

**URL:** <https://community.emergingthreats.net/t/double-firewall-hopping-with-pfsense/2800>\
**Category:** Tutorials, Tips & Tricks\
**Created:** [June 7, 2025, 10:15pm UTC](https://community.emergingthreats.net/t/double-firewall-hopping-with-pfsense/2800 "2025-06-07T22:15:10Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![trobinson667](https://sea2.discourse-cdn.com/flex016/user_avatar/community.emergingthreats.net/trobinson667/32/544_2.png) [@trobinson667](https://community.emergingthreats.net/u/trobinson667)\
**Post date:** [June 7, 2025, 10:15pm UTC](https://community.emergingthreats.net/t/double-firewall-hopping-with-pfsense/2800/1 "2025-06-07T22:15:10Z")

</div>

# Double Firewall Hopping with PfSense

Greetings, everyone. I’ve recently had to do some housekeeping in my lab environment as I prepare to expand, and write up some additional chapters for _Suricata: An Operator’s Guide_. My lab environment is kind of sprawling across my home network with little enclaves everywhere, but I figured I would write about my malware detonation lab, and IoT containment zone (if I ever get IoT devices to mess around with), and some new discoveries I made, trying to nest one firewall behind another. The goal of this blog post is to show you how to route through two pfSense firewalls to go from `10.0.0.16` (shadowchamber) to the host `hard-light.kaidacorp.local` at `172.16.50.2`. Please bear in mind that this blog post is pfSense-centric. I would hazard to guess that most of these config settings are applicable to Opnsense, but I’m not really an Opnsense fan, so I don’t use it often. Your mileage may vary. For the visual learners out there, here’s a simplified network diagram made with [draw.io](http://draw.io):

 ![doublefirewall-13v2](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/a/aee135c214149743b0228e699c15cd12e294a02c.png)

> This is a simplified representation of what my home network, and one of my _many_ homelab enclaves looks like. The dotted green line from 10.0.0.16 to 172.16.50.2 depicts our source, and our destination. For this demonstration

# Malware Lab Network (so far)

Our local, physical network being served by a comcast router’s DHCP server (10.0.0.0/24). My local box is a Windows 10 machine at 10.0.0.16. My physical pfSense Gateway is at 10.0.0.253 (`armageddonheart.kaidacorp.local`). `10.0.0.253` is the IP address of the `WAN` interface. This pfSense box, a relatively cheap no-name ali-express box provides DNS (with pfblockerNG), NTP, DHCP (to LAN, OPT1 and OPT2 networks) and also serves to segment my home network from my “malware analysis” network, and all of its supporting infrastructure. I have three subnets that the pfSense router brokers access to: `10.1.0.0/24` (LAN), `10.2.0.0/24` (OPT1), and `10.3.0.0/24` (OPT2).

`10.1.0.0/24` hosts two relatively cheap ali-express **physical** machines that I use occasionally for malware detonation. `10.2.0.0/24` is a “supporting infrastructure” network that hosts a [FOG](https://fogproject.org/) (free open ghost) server, as well as one of my three Proxmox systems, (`nemesis.kaidacorp.local`, `10.2.0.2`), which is the hardware running the FOG VM (`onslaught.kaidacorp.local`, `10.2.0.3`). The FOG VM hosts images of both the Physical Linux (Ubuntu 24.04 – `Liandry`) and Windows (Windows 11 – `Berserk`) system. Both systems have packet capture software, (tshark, tcpdump), and [mitmproxy](https://mitmproxy.org/) running, with [SSL Key Log File generation](https://docs.mitmproxy.org/stable/howto/wireshark-tls/) enabled. I have some customizations in place on my Windows and Linux box, but that’s a story for another time. The baseline images are copied via PXE booting, and fog’s PXE agents. At any time, I can force either physical machine to boot from PXE and grab a clean image after I’m done running malware. I can also restore the “clean” image, update it, and collect a new image at any time as well. It was a little difficult to figure out how to get PXE booting to work across network segments, but with correct DHCP options, and some configurations to change where NFS is binding, and ensuring the boxes can reach the NFS share where the images are at, I managed to make things work.

That leaves `10.3.0.0/24`. I have a gl-iNet box (`redline.kaidacorp.local`) acting in bridge mode behind the pfSense firewall. The device is configured to get its DNS there, and pfblockerNG is in full effect. This is the wi-fi network where I plan to do weird IoT things in the future, but also, I connect my phone to this wi-fi network because DNS-based blocking is extremely effective for Android-based devices. I like reaping those benefits, but I’m not necessarily ready to make every machine on the 10.0.0.0/24 network (that aren’t depicted) do outbound DNS blocking, as it could break a lot of our IoT and smart devices.

## Network Expansions

I’m in the process of expanding `10.2.0.0/24` to contain another subnet through a pfSense VM hosted on nemesis (`anh-5.kaidacorp.local`, `10.2.0.4 -- WAN interface`). the LAN interface (`172.16.50.1`) provides access `172.16.50.0/24`. I’m planning on adding a Windows VM, and three Linux VMs. Two of the Linux VMs and the Windows VM will be to use virtual machines for malware analysis, and exploit dev (one exploit “pitcher” and two “catcher” VMs – One Linux, One Windows), while the final Linux VM will host polarproxy for demonstrating TLS/SSL decryption.

I have a bunch of little enclaves very similar to this all over my home network with different VMs, doing different things, so I’m very familiar with `route -p add x.x.x.x mask x.x.x.x [gateway IP]` that can be used in Windows to persist static routes. In order to add routes in Windows, you’ll need a command prompt that is UAC elevated (“Run as Administrator”). As for Linux, there are a multitude of ways to do this, depending on your distro and what network management suite they use if they’re too good for `/etc/network/interfaces` and `post-up`. So far, I’ve had to work with Raspbians `udhcpd` and ubuntu’s `netplan`. In most cases, this results in dropping the `ip route add x.x.x.x/xx via x.x.x.x [dev xxx]` command into the correct config file somewhere. Note that in most cases with Linux, running `ip route add`, or adding the `ip route add` command to these most network configuration files typically requires `root`, `su` or `sudo` for root privileges to modify the routing table or files necessary.

### Why does any of this Matter? Static Route Limitations

The whole reason I’m bringing any of this up, is that it wasn’t until recently that I realized that static routes have a pretty severe limitation: Without invoking things that shouldn’t be invoked, you _cannot_ and _should not_ try to nest static routes. In most cases, it simply won’t work. Let me give you an example. My ultimate goal is to reach `172.16.50.2`. I need to hop through the gateway at `10.0.0.253`, then the second gateway at `10.2.0.4` to get there. I want to reach `10.2.0.0/24` via `10.0.0.253` in Windows, and I want this to be a permanent static route, because my default gateway is a comcast gateway that can’t do routing protocols. so I run:

`route -p add 10.2.0.0 mask 255.255.255.0 10.0.0.253`

![doublefirewall-1](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/a/a431e63933daf617add787a2ad600c292ce535c3.png)

> No problemo. Here’s your persistent route.

Fun fact: Windows stores `route -p` routes in the registry for persistence:

 ![doublefirewall-2](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/8/8c53eb222fcce66e99c578fc468153dbbd01d10b.png)

> Any ipv4 routes persisted in Windows via `route -p add` are stored in `HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\PersistentRoutes` as `REG_SZ` strings. Know that persistent routes can be added manually without the use of the `route` command.

So we add one static route, but in order to reach 172.16.50.0/24, I have to go through 10.2.0.4 as the gateway to that network. You’d normally think that you’d just do `route -p add 172.16.50.0 mask 255.255.255.0 10.2.0.4`, or `ip route add 172.16.50.0/24 via 10.2.0.4` and that you’d be off and running to the races. But unfortunately, that’s not correct. Static routes can only point to the next network via a _direct_ hop or a _direct route_ to a device on the same subnet as one of your network interfaces unless you want to learn how to do proxy arp. I did _not_ want to learn how to do this, so here’s the solution: You’d have to establish that static route to `172.16.50.0/24` via the firewall/router at `10.2.0.4` on the first-hop router/firewall (in this case, `10.0.0.253`) itself.

## pfSense : Gateways and Static Route Configuration

pfSense has a weird way of establishing static routes, requiring you to define a gateway and a route separately. Here’s how to do it:

Log in to the first pfSense router in the path to your destination (again, in this case, its `armageddonheart.kaidacorp.local (10.0.0.253)`, and once logged in, navigate to `System > Routing`. The default tab you _should_ end up on is the gateways page. Assume that the `anh5_lan` entry isn’t there, and click the `Add` button.

 ![doublefirewall-3](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/4/4d208a8ceaa6848591ad4fe9cf15d535757a1551.png)

> Head over to `System > Routing`. The default tab that should be displayed is the `Gateways` tab. So if that isn’t your landing tab, click on it. Click the `Add` button.

Next, we have to create a gateway. On this page, you’ll want to fill out the `Interface`, `Address Family`, `Name`, `Gateway`, and `Description` input boxes. In my case here is what I entered:

- Interface: OPT 1 – the 10.2.0.0/24 network is allocated to the OPT1 interface.
- Address Family: IPv4
- Name: anh5\_lan – The name doesn’t matter, pick something that defines what this gateway is for.
- Gateway: 10.2.0.4 – This is the IP address on the OPT1 network thats serves as the gateway for the destination we want to reach.
- Description: Gateway to reach ANH-5 firewall’s LAN networks (172.16.50.0/24) – something to remind you as to why you created this gateway in the first place. Be detailed.

Click `Save` at the bottom of the page, followed by `Apply Settings` at the top of the `Gateways` tab that you’re automatically redirected to.

 ![doublefirewall-4](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/9/94f065c50008dd75a68b8d03b8a75ef9372fe9e1.jpeg)

> Pay attention to the fields with red boxes around them. They include: the Interface, Address Family, Name, Gateway, and Description input boxes. At a minimum, I recommend filling all of those boxes out. As for the other settings? You can investigate those on your own if you’d like. My route worked without fiddling with them.

Next, we have to go to the `Static Routes` tab. By default, the page will be empty. Click the `Add` button.

 ![doublefirewall-5](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/4/45dd3e95d425809607ae319d1364ad7a4eacd12d.png)

> Click on the `Static Routes` tab. Ignore the fact that the route I need is already here, and click the `Add` button.

On the `Edit Route Entry` page, All of the drop-downs and input boxes need to be filled out. Here is what I had to enter for my use case:

- Destination Network: 172.16.50.0. and in the subnet mask drop-down: /24.
- Gateway: anh5\_LAN - 10.2.0.4 – Should reflect the name and the IP address of the gateway you just made.
- Description: anh5\_lan – a brief description of the purpose this route serves.

**Do not** tamper with the `Disable this static route` checkbox. It should be _unchecked_ by default. If for some reason it _is_ checked, make sure it is _unchecked_. With all that out of the way, click the `Save` button, and back on the `Static Routes` tab, click `Apply Changes` at the top of the page.

 ![doublefirewall-6](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/9/9d7b8d997a1a0f36c4aedf8e38c5a98d9ef01152.png)

> I’m not bothering with red boxes here. All of the input boxes and drop-downs need to be filled out. The only thing that should **NOT** be touched is the `Disable this static route` checkbox. It _should_ be unchecked. If for any reason it’s checked, make sure to **uncheck it**. `Save` and `Apply Settings`. Almost done with routing.

## Establishing a New Route on your Windows/Linux host

The next step you’ll need to do, is create _another static route_. In my case, I need to use the static route on `10.0.0.253` for me to ultimately reach `172.16.50.0/24`, so this is what my `route -p add` command looked like for windows:

`route -p add 172.16.50.0 mask 255.255.255.0 10.0.0.253`

For linux users, you’ll be using the `ip route` command:

`ip route add 172.16.50.0/24 via 10.0.0.253` (Note: you might need to add `dev [interface name]` to the end of this command to ensure the system doesn’t assign the route to the wrong network interface (e.g. `ip route add 172.16.50.0/24 via 10.0.0.253 dev ens18`))

If you still have the OLD static route to 172.16.50.0/24 (or whatever your target network is) via 10.2.0.4 (or whatever the gateway device’s IP is), you’ll need to delete that route first before you add the correct route.

- Windows: `route -p delete 172.16.50.0`
- Linux: `ip route delete 172.16.50.0/24`

![doublefirewall-7](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/2/2446a1f595de4cd10cf2a60b173d7e6d9d480095.png)

> If you haven’t already, delete the incorrect static route, THEN create the **correct** static route. This output is from the `route print` command on Windows, showing a route to `172.16.50.0/24 via 10.0.0.253`. The _correct_ route, based on the gateway and static route we just made on pfSense.

## Firewall Adjustments

I want to be able to SSH to the VM `hard-light.kaidacorp.local` at `172.16.50.2`. In order to do that, I need to make sure I have firewall rules configured in two places: The “WAN” interface on the `10.0.0.253` pfSense firewall (`armageddonheart.kaidacorp.local`), and on the “WAN” interface of the `10.2.0.4` pfSense firewall (`anh-5.kaidacorp.local`).

I’m not going to give you a full primer on how to add firewall rules on pfSense however I’m going to give you a couple of notes:

- Under `Interfaces > WAN` scroll all the way down and **uncheck** the `Block private networks and loopback addresses` check box at the bottom of the page. As always, click `Save`, then `Apply Changes`. You’ll need to do this for **both** firewalls.

 ![doublefirewall-8](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/b/ba741456e3370cd4d66258568887b3f9f16d8a07.png)

> This setting **must** be unchecked on both pfSense firewalls. If you’re using if you are using other firewall distributions or appliances that are _not_ pfSense, you might need to hunt through the interface or default firewall rules to see if this is a configuration setting that can be modified. Chances are if you’re using a routing device without firewall software or access control lists, this is something you don’t have to care about.
> 
> _How do I reach the pfSense web interface from the WAN interface? Normally you can’t do that._ access the console of the pfsense device. selection option 8 to open up a root shell. run the command `pfctl -d` to temporarily disable the firewall. Create a firewall rule on the WAN interface to allow access to the WAN ip address on port 443 (https) from your workstation’s IP address (or host alias). And while you’re there, disable this checkbox. If at any time you lose connectivity to the web interface before you can put these changes into place, run `pfctl -d` on the console again to temporarily disable the pfSense firewall. Note that every time you `Apply Changes` to the firewall, pfSense reactivates the firewall, requiring you to re-run `pfctl -d` on the console if you’re not done.

- Make use of `Firewall > Aliases` and set up an alias for system(s) you want to have access to your lab network(s). Do this for **both** firewalls you’re creating firewall rules for. In this case, I set up a host(s) alias on both of my firewalls named “admin systems”. For now, it only consists of my Windows box at `10.0.0.16 (shadowchamber)`, but if you’re sharing your lab environment, or want to be able to access it from multiple hosts (e.g. you have to share your VM environment, or maybe you want both your local workstation and/or a jump box to have direct network access). It makes it _much easier_ for granting lab network access to additional hosts.

 ![doublefirewall-9](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/6/6310c35944fed613a06be4a56c7df0a889d9e2c2.png)

> pfSense has the concept of `Aliases` under the `Firewall > Aliases` page. I recommend creating a host(s) alias containing IP addresses of hosts that will need access to the lab environment. Using an alias for multiple IP addresses/hostnames will make rule management _much_ easier. Instead of having to create new rules to allow a new IP addresses into the lab network(s), just modify the aliases for existing rules. Here is my `admin_systems` host alias that I am using on **both** firewalls.

- You’ll need to create **two** firewall rules – one on each firewall you’re using to reach the target host on the target network. Remember that my two firewalls are at `10.0.0.253` (`armageddonheart.kaidacorp.local`), and `10.2.0.4` (`anh-5.kaidacorp.local`).
- On the WAN interface of `armageddonheart` (10.0.0.253), create a firewall rule where the source is the `admin_systems` alias, and the destination is 172.16.50.0/24 on port 22/tcp, ipv4.

![doublefirewall-10](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/optimized/2X/a/a4f1bdb2776d015900da07fc73f354476dce9936_2_690x35.png)

> This is the firewall rule on the `WAN (10.0.0.253)` Interface on `armageddonheart.kaidacorp.local`. It allows `admin_systems` alias (currently just 10.0.0.16) to access 172.16.50.0/24 on port 22/tcp over ipv4.

- On the WAN interface of `anh-5` (10.2.0.4), create an identical firewall rule where the source is the `admin_systems` alias, and the destination can either be the `LAN subnets` pre-built alias, or you can input a network (e.g. `172.16.50.0/24`) on port 22/tcp, ipv4.

> This is the firewall rule on the `WAN (10.2.0.4)` Interface on `anh-5.kaidacorp.local`. Once again, it allows the `admin_systems` alias (just 10.0.0.16 currently) to access the `LAN subnets` pre-built alias on port 22/tcp over ipv4. Note that you can substitute the `LAN subnets` alias for the subnet that needs to be reached (e.g., 172.16.50.0/24)

## Results

Boom SSH connectivity. I’m lazy as can be, so I recommend that once SSH connectivity is established, set up key-based auth, and once you confirm key-based auth has been achieved, reconfigure `/etc/ssh/sshd_config`. Set `PasswordAuthentication` to `no`, add the `AuthenticationMethods` directive to your configuration, and set it to just `publickey`. Restart sshd, reconnect. Now your SSH server will only accept key-based auth.

 ![doublefirewall-12](https://us1.discourse-cdn.com/flex016/uploads/emergingthreats/original/2X/9/9ac26c0bb1c43560dcefccbfe4f654fde021c00f.png)

> And here we are. Access to 172.16.50.2 has been established, and it only took a mountain of firewall and routing work to get here.

If you’re brave and you want to set up something like OSPF, you’ll need the FRR package, and to find a tutorial to set all that up because _I’m out_. Maybe if I set the comcast router to bridge mode, and get another pfSense firewall for my local network, I’ll _consider_ it, but for now, persistent static routes it is.

## Troubleshooting

- Establish where you are trying to go, and all the hops needed to get there. For example: I’m starting at `10.0.0.16 (10.0.0.0/24)`. I want to reach `172.16.50.2 (172.16.50/24)`. I have to go through `10.0.0.253` as the first-hop, **then** `10.2.0.4` as the second-hop to get there.
- You’ll need a minimum of three static routes to get here:
  - Two routes on the windows host:
  - `route -p add 10.2.0.0 mask 255.255.255.0 10.0.0.253` or `ip route add 10.2.0.0/24 via 10.0.0.253`
  - `route -p add 172.16.50.0 mask 255.255.255.0 10.0.0.253` or `ip route add 172.16.50.0/24 via 10.0.0.253`

- A correctly defined gateway **and** static route on the first firewall (10.0.0.253)
  - The gateway must define the gateway IP (10.2.0.4) address **and** correct interface on the first firewall (OPT1) necessary to reach the second firewall.
  - The static route must define the gateway to be used (10.2.0.4), and the correct destination network on the **second** firewall (172.16.50.0/24)

- Ensure the `Block private networks and loopback addresses` checkbox under `Interfaces > WAN` on **both** firewalls is **unchecked**
- Double check the source and dest IP addresses, networks and/or aliases, protocols, port numbers, and address families used in the firewall rules on **both** firewalls are correct
  - Firewall 1 – Interface: WAN source: `admin_systems/10.0.0.16` dest: `172.16.50.0/24` protocol: tcp port: 22 address family: ipv4
  - Firewall 2 – Interface: WAN source: `admin_systems/10.0.0/16` dest: `LAN subnets/172.16.50.0/24` tcp port: 22 address family: ipv4

- Verify the `sshd` service is installed and enabled (`ss -alnt`). Double check the target IP address (`ip -br a` – `172.16.50.2`)
- If you have any reason to suspect them, check to make sure that there are not any host-based firewall rules on either the source host (10.0.0.16) or the destination host (172.16.50.2) that may be blocking the connection
- Verify all systems are powered on, and fully configured
- Verify physical network connectivity (network cables, NICs, etc.)
