pfSense Firewall Best Practices - Rules and Migration
A well-designed firewall policy is the foundation of network security. pfSense provides flexible filtering capabilities built on pf, but the platform alone does not guarantee security - the outcome depends on how the administrator organizes rules, maintains their relevance, and adapts the policy to evolving requirements. This guide covers the principles of building a robust firewall policy, common mistakes to avoid, rule auditing procedures, and detailed migration instructions for Cisco ASA, FortiGate, and MikroTik RouterOS.
Rule Organization
Rule ordering in pfSense directly determines filtering results: the first match wins principle means the first matching rule terminates packet processing. Improper ordering causes traffic to be blocked or permitted contrary to the administrator’s intent. A detailed description of the processing mechanism is available in the pfSense firewall rules section.
Recommended Rule Order Per Interface
For each interface (LAN, DMZ, OPT, etc.), maintain a consistent structure:
Anti-lockout - pfSense creates this rule automatically on the LAN interface. If disabled manually, create an explicit allow rule for administrative access (HTTPS/SSH to the interface IP) and place it first.
Explicit blocks for known threats - block traffic from blacklist sources (bogon networks, GeoIP lists, known malicious address lists). Use URL Table aliases for these rules, as they update automatically.
Allow rules for legitimate traffic - specific rules permitting required protocols and destinations. Each rule should be as specific as possible: define the protocol, destination port, source address, and destination address.
Implicit deny (default deny) - pfSense automatically drops all traffic that does not match any rule. Creating an explicit block rule at the bottom of the list is not required but recommended for two purposes: logging denied traffic and providing visual confirmation that the default deny policy is in effect.
Using Separators
pfSense supports colored separators between rules in the web interface. Separators have no effect on traffic processing but significantly improve navigation when managing large rule sets.
Recommended separator naming scheme:
| Separator | Purpose |
|---|---|
--- ADMIN ACCESS --- | Management rules |
--- BLOCK LISTS --- | Blacklist-based block rules |
--- SERVERS --- | Server access (grouped by function) |
--- USER TRAFFIC --- | Workstation rules |
--- DEFAULT DENY --- | Final deny rule |
Rule Descriptions
Every firewall rule must contain a meaningful description in the Description field. The description is the only way to understand a rule’s purpose without analyzing its parameters. Recommended format:
[TICKET/CR] Purpose - expiration (if temporary)Examples:
CR-2024-031 VPN access for partner ExampleCorp - until 2024-12-31Allow DNS to internal resolversBlock outbound SMTP from workstations (relay prevention)
Rules without descriptions turn the rule set into an unreadable list that becomes impossible to maintain when administrators change.
Minimizing Rule Count Through Aliases
A large number of rules complicates auditing and increases the probability of errors. Instead of creating a separate rule for each server or port, use aliases :
Suboptimal - three separate rules:
| Action | Source | Destination Port | Description |
|---|---|---|---|
| Pass | LAN net | 80 | HTTP to web server 1 |
| Pass | LAN net | 443 | HTTPS to web server 1 |
| Pass | LAN net | 8443 | Alt HTTPS to web server 2 |
Optimal - a single rule with aliases:
| Action | Source | Destination | Port | Description |
|---|---|---|---|---|
| Pass | LAN net | Alias: WebServers | Alias: WebPorts | Access to web servers |
The WebServers alias contains server IP addresses, and the WebPorts alias contains ports 80, 443, 8443. When a new server or port is added, only the alias needs updating - no new rule is required.
Common Mistakes
Overly Permissive Rules (any/any)
A rule Pass * * * * * (allow everything from all sources to all destinations on all protocols) is the most prevalent and dangerous mistake. These rules are created for quick debugging and left in the production configuration. pfSense creates a default allow rule on LAN - after initial setup, this rule must be replaced with a set of specific permits.
Warning:
An any/any rule on the LAN interface permits any device on the network to reach arbitrary addresses over any protocol, including direct internet access bypassing proxy servers and security controls.
No Egress Filtering
Administrators often focus exclusively on inbound traffic and completely ignore outbound filtering. Lack of egress filtering allows compromised hosts to freely establish reverse shells, exfiltrate data, and communicate with command-and-control (C2) servers. Minimum egress restrictions:
- Allow DNS only to internal resolvers or specific public resolvers
- Allow HTTP/HTTPS through a proxy server (when available)
- Allow SMTP only from the mail server
- Block direct outbound connections on non-standard ports (IRC, Tor, P2P)
Missing Logging on Deny Rules
pfSense logs traffic matching the implicit deny rule by default. However, when adding explicit block rules (for example, blacklist rules), logging must be enabled manually - it is disabled by default for explicit rules. Without logs, the administrator cannot see blocked attacks or assess policy effectiveness.
Shadowed Rules
A shadowed rule is a rule that never matches traffic because a broader rule placed above it intercepts all qualifying traffic. Example:
| # | Action | Source | Destination | Port |
|---|---|---|---|---|
| 1 | Pass | LAN net | * | * |
| 2 | Block | LAN net | 10.0.0.5 | 22 |
Rule 2 never fires because Rule 1 permits all traffic from LAN, including SSH to 10.0.0.5. To fix this, move the block rule above the allow rule:
| # | Action | Source | Destination | Port |
|---|---|---|---|---|
| 1 | Block | LAN net | 10.0.0.5 | 22 |
| 2 | Pass | LAN net | * | * |
Not Using Aliases
Hardcoding IP addresses and ports directly in rules leads to data duplication: when a server address changes, every rule referencing it must be updated individually. Aliases solve this problem - a detailed description of their capabilities is available in the pfSense aliases section.
Forgotten Temporary Rules
Temporary rules created for diagnostics or short-term tasks remain in the configuration indefinitely. Recommendations:
- Include the expiration date in the rule description (e.g.,
until 2024-06-30) - Enable logging on temporary rules to monitor their usage
- Conduct monthly reviews for the presence of temporary rules
Migration from Cisco ASA
Migrating from Cisco ASA to pfSense requires understanding fundamental differences in architecture and terminology. Both firewalls operate on stateful inspection and first match wins, but their approaches to rule organization, objects, and NAT differ substantially.
Terminology Mapping
| Cisco ASA | pfSense | Notes |
|---|---|---|
| Access Control List (ACL) | Firewall Rules (per interface) | In pfSense, rules are bound to interfaces directly; no separate ACL-to-interface binding step |
| Object Group (network) | Alias (Host / Network) | Functionally identical - named address groups |
| Object Group (service) | Alias (Port) | Port groups |
| Security Level (0-100) | No equivalent | pfSense does not use security levels; everything is determined by rules |
| Security Zone | Interface | Each pfSense interface is effectively a separate zone; zone-based firewall is not supported |
| Global ACL | Floating Rules | Floating rules apply to multiple interfaces |
| NAT exemption (identity NAT) | Manual Outbound NAT (no NAT rule) | Configured in Firewall > NAT > Outbound |
| Twice NAT | Manual Outbound NAT + Port Forward | pfSense separates inbound and outbound NAT |
| Packet Tracer | Packet Capture + States | Diagnostics via Diagnostics > Packet Capture |
Converting ACLs to pfSense Rules
ACLs on Cisco ASA are ordered lists of ACEs (Access Control Entries) bound to an interface via the access-group command. In pfSense, each rule is already bound to an interface - the intermediate binding step does not exist.
Cisco ASA:
object-group network WEBSERVERS
network-object host 10.0.1.10
network-object host 10.0.1.11
object-group service WEBPORTS tcp
port-object eq www
port-object eq https
access-list INSIDE_IN extended permit tcp any object-group WEBSERVERS object-group WEBPORTS
access-group INSIDE_IN in interface insidepfSense - equivalent configuration:
- Create alias
WEBSERVERS(type Host):10.0.1.10,10.0.1.11 - Create alias
WEBPORTS(type Port):80,443 - Create a rule on the LAN tab:
| Action | Protocol | Source | Destination | Port | Description |
|---|---|---|---|---|---|
| Pass | TCP | LAN net | WEBSERVERS | WEBPORTS | HTTP/HTTPS to web servers |
NAT Processing Differences
On Cisco ASA, NAT and filtering rules (ACLs) are processed as related but separate constructs. Processing order depends on the ASA version and NAT type (auto NAT, manual NAT, twice NAT). In pfSense, NAT and firewall rules are processed sequentially:
- Inbound packet passes through NAT rules (Destination NAT / Port Forward)
- After address translation, the packet is evaluated by firewall rules using the already-translated addresses
This means that pfSense firewall rules must reference the translated (internal) destination addresses, not the public ones. This difference frequently causes errors during migration.
Example - port forward with filtering:
NAT: WAN:443 -> 10.0.1.10:443
Rule: Pass TCP * -> 10.0.1.10:443 (on WAN interface)On Cisco ASA, the ACL rule references the public address (unless nat control is used); in pfSense, it references the internal server address.
Security Levels vs. Interface Rules
Cisco ASA assigns each interface a security level (0–100). Traffic from a higher security level interface to a lower one is permitted by default. Traffic in the reverse direction is denied without an explicit ACL.
pfSense does not support the security level concept. Each interface is processed independently: only traffic explicitly permitted by rules is allowed (except for the default allow rule on LAN, which should be replaced after initial setup). This simplifies the security model but requires creating explicit rules for every traffic direction.
Step-by-Step ASA Migration Plan
- Inventory objects - export all object-groups using
show running-config object-groupand create corresponding pfSense aliases - Export ACLs - export ACLs using
show access-listand convert each ACE to a pfSense rule on the appropriate interface - Verify ordering - confirm that rule order is preserved (first match wins on both platforms)
- NAT - convert the NAT configuration, accounting for processing order differences
- Testing - verify critical traffic flows using Diagnostics > Packet Capture and firewall logs
- Remove the default allow rule on LAN - replace it with a set of specific permits
Migration from FortiGate
FortiGate uses a zone-based firewall with security policies linking source and destination zones. pfSense uses a per-interface model without zones. Migration requires redesigning the rule structure to account for this difference.
Terminology Mapping
| FortiGate | pfSense | Notes |
|---|---|---|
| Security Policy | Firewall Rule | FortiGate policies contain more parameters (UTM profiles, schedules) |
| Address Object | Alias (Host / Network) | Functionally identical |
| Address Group | Alias (containing nested aliases) | pfSense supports nested aliases |
| Service Object | Alias (Port) | Port groups |
| Zone | Interface / Interface Group | pfSense does not support zones; interfaces are used instead |
| VDOM | Separate pfSense instance | VDOMs have no equivalent - isolation requires a separate installation |
| Security Profile (AV, IPS, Web Filter) | Packages: Suricata/Snort, pfBlockerNG, SquidGuard | Implemented via additional packages |
| Central NAT | Firewall > NAT | Similar structure |
| FortiView | Status > Dashboard + pfSense logs | Limited analytics without additional tools |
Converting Policies to Rules
A FortiGate policy defines: source zone, destination zone, addresses, services, action, UTM profiles, and schedule. The pfSense equivalent is a rule on the interface tab specifying protocol, addresses, and ports.
FortiGate:
config firewall policy
edit 10
set name "LAN to WAN Web"
set srcintf "lan"
set dstintf "wan1"
set srcaddr "LAN_SUBNET"
set dstaddr "all"
set action accept
set schedule "always"
set service "HTTP" "HTTPS" "DNS"
set utm-status enable
set av-profile "default"
set ips-sensor "default"
set ssl-ssh-profile "certificate-inspection"
set logtraffic all
next
endpfSense - equivalent configuration:
- Create alias
WebAndDNS_Ports(type Port):80,443,53 - Create a rule on the LAN tab:
| Action | Protocol | Source | Destination | Port | Gateway | Description |
|---|---|---|---|---|---|---|
| Pass | TCP/UDP | LAN net | * | WebAndDNS_Ports | * | LAN to WAN Web + DNS |
- Install Suricata or Snort packages to replace UTM profiles (AV, IPS)
- Install SquidGuard or pfBlockerNG for web filtering
FortiGate Zones vs. pfSense Interfaces
FortiGate allows combining multiple interfaces into a zone and creating policies between zones. This abstraction does not exist in pfSense. Available alternatives:
- Interface Groups - pfSense allows grouping interfaces and creating shared rules for the group. This is the closest equivalent to zones, but with limitations: group rules are processed before individual interface rules and cannot be overridden at the interface level.
- Floating Rules - allow applying a single rule to multiple interfaces. Suitable for global policies (e.g., blocking specific protocols on all interfaces).
VDOMs and Multi-Tenancy
FortiGate supports Virtual Domains (VDOMs) - virtual firewall instances within a single device. Each VDOM has its own interfaces, routing table, policies, and administrative access.
pfSense does not support a VDOM equivalent. For multi-tenant scenarios, the options are:
- Separate pfSense instances (physical or virtual)
- VLAN separation with distinct rule sets for each VLAN interface (limited isolation, shared routing table)
Security Profiles and pfSense Equivalents
FortiGate offers integrated security profiles (UTM) applied directly within a policy. pfSense achieves similar functionality through separate packages:
| FortiGate Function | pfSense Package | Notes |
|---|---|---|
| Antivirus Profile | ClamAV (via Squid) | HTTP traffic scanning through proxy |
| IPS Sensor | Suricata / Snort | Full IDS/IPS; requires rule configuration |
| Web Filter | SquidGuard / pfBlockerNG-devel | Category and list-based filtering |
| Application Control | Suricata (Application Layer rules) | Limited support compared to FortiGate |
| SSL Inspection | Squid (SSL Bump) | Requires root certificate deployment to clients |
| DNS Filter | pfBlockerNG-devel (DNSBL) | DNS-based blocklist filtering |
Warning:
FortiGate UTM functions are deeply integrated into policy processing and operate at the hardware level (ASIC). pfSense packages run in software and require additional CPU/RAM resources. When migrating high-throughput installations, load testing is mandatory.
Step-by-Step FortiGate Migration Plan
- Export configuration - export the full configuration via
execute backup full-config - Map zones to interfaces - determine which pfSense interfaces correspond to FortiGate zones; use Interface Groups where appropriate
- Create aliases - convert Address Objects and Address Groups to pfSense aliases
- Create rules - convert each Security Policy to a pfSense rule on the appropriate interface
- Install security packages - install Suricata/Snort, pfBlockerNG, and other packages to replace UTM profiles
- Migrate NAT - convert VIPs and Central NAT to Port Forward and Outbound NAT
- Testing - verify all traffic flows, especially cross-zone traffic that in pfSense is implemented through rules on multiple interfaces
Migration from MikroTik RouterOS
MikroTik RouterOS uses chains to organize filtering, NAT, and packet manipulation rules. The model differs fundamentally from pfSense: in RouterOS, the administrator works with filter, NAT, and mangle tables, each containing chains (input, forward, output). In pfSense, all rules are bound to interfaces and processed on ingress.
Terminology Mapping
| MikroTik RouterOS | pfSense | Notes |
|---|---|---|
/ip firewall filter | Firewall > Rules | Primary filtering rules |
| Chain: input | Interface rules (traffic to pfSense itself) | Filtering traffic destined for the device |
| Chain: forward | Interface rules (transit traffic) | pfSense does not separate input/forward - all rules are on the interface tab |
| Chain: output | Floating Rules (outbound) | Outbound traffic from pfSense is filtered via floating rules |
| Address List | Alias | Functionally identical |
/ip firewall nat | Firewall > NAT | Split into Port Forward and Outbound NAT |
/ip firewall mangle | No direct equivalent | Partially replaced by floating rules with DSCP/Queue |
| Connection Tracking | State Table | pf uses a state table analogous to conntrack |
| Action: jump (custom chain) | No equivalent | pfSense does not support custom chains |
| Default policy: accept | Default policy: deny | Critical difference during migration |
Critical Difference: Default Policy
Warning:
MikroTik RouterOS permits all traffic by default (default accept). pfSense denies all traffic by default (default deny) on all interfaces except LAN (where an initial allow-all rule is created). During migration, ensure that all required permits are created explicitly, or traffic will be blocked.
Converting Chains to Interface Rules
In MikroTik, rules are distributed across chains (input, forward, output). In pfSense, all rules reside on interface tabs. The mapping principle:
MikroTik - filter/forward:
/ip firewall filter
add chain=forward src-address=192.168.1.0/24 dst-address=10.0.0.0/24 \
dst-port=80,443 protocol=tcp action=accept comment="LAN to DMZ web"
add chain=forward src-address=192.168.1.0/24 dst-address=0.0.0.0/0 \
dst-port=53 protocol=udp action=accept comment="LAN DNS"
add chain=forward src-address=192.168.1.0/24 action=drop \
comment="Block all other LAN forward"pfSense - rules on the LAN tab:
| # | Action | Protocol | Source | Destination | Port | Description |
|---|---|---|---|---|---|---|
| 1 | Pass | TCP | 192.168.1.0/24 | DMZ_SERVERS | 80, 443 | LAN to DMZ web |
| 2 | Pass | UDP | 192.168.1.0/24 | * | 53 | LAN DNS |
| 3 | Block | * | 192.168.1.0/24 | * | * | Block all other LAN |
Address Lists vs. Aliases
MikroTik Address Lists and pfSense aliases serve the same purpose - grouping addresses for use in rules. Key differences:
| Feature | MikroTik Address List | pfSense Alias |
|---|---|---|
| Dynamic addition | Yes (action=add-src-to-address-list) | No (URL Table only) |
| Timeout (entry TTL) | Yes (timeout=1h) | No |
| Nested lists | No | Yes (aliases can reference other aliases) |
| URL sources | No (scripting only) | Yes (URL Table, scheduled updates) |
| FQDN | No (scripting only) | Yes (automatic DNS resolution) |
Dynamic address addition via action=add-src-to-address-list (e.g., for automatically blocking scanners) has no direct equivalent in pfSense. Similar functionality is achieved through packages:
- pfBlockerNG - automatic blocking via IP lists and GeoIP
- Suricata/Snort - automatic blocking upon attack detection
NAT: RouterOS vs. pfSense
MikroTik combines all NAT types in a single /ip firewall nat table with srcnat and dstnat chains. pfSense separates NAT into three sections:
| MikroTik NAT | pfSense NAT | Description |
|---|---|---|
chain=srcnat action=masquerade | Outbound NAT (Automatic) | Masquerading for outbound traffic |
chain=srcnat action=src-nat | Outbound NAT (Manual) | Static Source NAT |
chain=dstnat action=dst-nat | Port Forward | Inbound connection redirection |
chain=dstnat action=redirect | Port Forward (redirect) | Redirect to localhost |
In MikroTik, NAT and filter rules are separate tables processed independently. In pfSense, creating a Port Forward automatically generates an associated firewall rule (unless disabled). During migration, verify that each dstnat rule has a corresponding allow rule in pfSense.
Mangle and Advanced Packet Processing
The MikroTik mangle table is used for marking packets, connections, and routes, as well as modifying header fields (TTL, DSCP, TOS). pfSense has no direct mangle equivalent. Partial replacements:
| Mangle Function | pfSense Solution |
|---|---|
| Connection mark + policy routing | Floating rules with Gateway specified |
| DSCP marking | Floating rules with limiter (traffic shaping) |
| Packet mark for QoS | Traffic Shaper (ALTQ or Limiters) |
| Change MSS | System > Advanced > Firewall/NAT > MSS Clamping |
| Route mark (PBR) | Policy Routing via multiple gateways |
Connection Tracking
MikroTik RouterOS uses the connection tracking module (conntrack) with explicit state matchers:
/ip firewall filter
add chain=forward connection-state=established,related action=accept
add chain=forward connection-state=invalid action=dropIn pfSense, the state table is managed automatically: every allow rule creates a state table entry, and return traffic is passed without explicit rules. An explicit rule for established/related is not required. However, enabling the Scrub option (System > Advanced > Firewall/NAT) is recommended to drop fragmented and malformed packets.
Step-by-Step MikroTik Migration Plan
- Export configuration - execute
/exportto obtain the text configuration - Inventory Address Lists - convert to pfSense aliases
- Map chains:
chain=input- rules on interfaces for access to pfSense (WebGUI, SSH, DNS)chain=forward- rules on interfaces for transit trafficchain=output- floating rules (if needed)
- Remove conntrack rules - in pfSense, established/related handling is automatic
- Convert NAT - split srcnat and dstnat into Outbound NAT and Port Forward
- Replace mangle - identify which mangle functions are required and implement via floating rules, traffic shaper, or system settings
- Verify default policy - ensure all permits are created explicitly (pfSense = default deny)
- Testing - verify all traffic flows; pay special attention to rules that in MikroTik used custom chains (jump), as pfSense does not support this construct
Rule Auditing
Regular firewall rule auditing is a mandatory practice for maintaining security and regulatory compliance. Without auditing, rule sets degrade over time: stale permits accumulate, organizational logic deteriorates, and the attack surface expands.
Hit Counters
pfSense displays hit counters for each rule in the interface rule list. Counters enable:
- Identifying rules with zero counts - potential candidates for removal
- Determining the most heavily used rules for position optimization (moving them higher to accelerate processing)
- Detecting anomalies - a sudden increase in hit counts may indicate an attack or a change in network infrastructure
Warning:
Counters reset on pfSense reboot and when firewall rule changes are applied. For long-term analysis, use an external log collection system.
Identifying Unused Rules
Procedure for identifying unused rules:
- Reset all rule counters: Diagnostics > pfTop > Reset States
- Wait for a full business cycle (minimum 30 days for a typical organization to account for monthly processes)
- Check counters: rules with zero values are candidates for removal
- Before removing, change the rule action to Block with logging and wait an additional period
- If the block did not cause any incidents - remove the rule
Audit Frequency
| Network Type | Recommended Frequency |
|---|---|
| Stable infrastructure, minimal changes | Every 6 months |
| Dynamic environment, frequent changes | Monthly |
| Network undergoing migration | Weekly |
| PCI DSS compliance (Requirement 1.1.7) | Every 6 months (minimum) |
Regulatory Compliance
PCI DSS
PCI DSS Requirement 1 mandates:
- Documenting all allowed services, protocols, and ports with business justification (1.1.6)
- Reviewing firewall and router rules at least every six months (1.1.7)
- Prohibiting direct public access to the cardholder data environment (1.3)
- Installing personal firewalls on all mobile and employee-owned devices (1.4)
To satisfy Requirement 1.1.7:
- Maintain a rule registry with a documented purpose for each rule
- Record the date of the last review
- Store audit results as a documented report
CIS Benchmarks
CIS Critical Security Controls (v8) include:
- Control 4.4: Implement and Manage a Firewall on Servers - mandatory server-level filtering
- Control 4.5: Implement and Manage a Firewall on End-User Devices - endpoint filtering
- Control 13.4: Perform Traffic Filtering Between Network Segments - segmentation with traffic control
pfSense supports these controls through VLAN segmentation and firewall rules on each interface.
Audit Checklist
[ ] All rules have descriptions
[ ] No any/any rules (except documented exceptions)
[ ] No rules with zero hit count older than 90 days
[ ] Temporary rules removed or renewed with justification
[ ] Aliases are current (no addresses of decommissioned servers)
[ ] Logging enabled on block rules
[ ] Egress filtering configured
[ ] Default deny rule present explicitly (with logging)
[ ] Separators up to date
[ ] Audit results documentedIDS/IPS Integration
pfSense supports intrusion detection and prevention systems (IDS/IPS) through additional packages - Suricata and Snort. These systems analyze traffic content at the application layer and complement firewall rules, which operate at the network and transport layers.
IDS/IPS and Firewall Interaction
Firewall rules and IDS/IPS operate at different stages of packet processing:
- Firewall (pf) - makes Pass/Block decisions based on packet headers (IP, port, protocol)
- IDS/IPS (Suricata/Snort) - analyzes the payload of traffic that passed through the firewall
If the firewall blocked a packet, IDS/IPS never sees it. This means IDS/IPS protects only against threats in permitted traffic - for example, exploits in HTTP requests, SQL injections, malicious DNS queries.
Automatic Blocking
Suricata and Snort in IPS mode can automatically block traffic upon threat detection. The blocking mechanism:
- Suricata/Snort adds the attacker’s IP address to the snort2c table
- pf checks the snort2c table before processing firewall rules
- All packets from the blocked IP are dropped until the timeout expires
The administrator controls blocking parameters: ban duration, pass lists (whitelists), and rule categories.
Wazuh Integration
For centralized monitoring of firewall and IDS/IPS events, integrating pfSense with Wazuh is recommended. Wazuh collects pfSense logs (filterlog, Suricata/Snort alerts), correlates events, and generates notifications. Detailed setup instructions are available in the pfSense and Wazuh integration section.
Related Sections
- pfSense Firewall Rules - rule creation and management, processing hierarchy, floating rules
- pfSense Aliases - grouping addresses, networks, and ports to simplify rules
- pfSense and Wazuh Integration - monitoring pfSense security events in SIEM