This section deals primarily with introductory firewall concepts and lays the ground work for understanding how to configure firewall rules using pfSense® software.
Rule and ruleset are two terms used throughout this chapter:
Refers to a single entry on the Firewall > Rules screen. A rule instructs the firewall how to match or handle network traffic.
Refers to a group of rules collectively. Either all firewall rules as a whole, or a set of rules in a specific context such as the rules on an interface tab. The complete firewall ruleset is the sum of all user configured and automatically added rules, which are covered further throughout this section.
Rulesets on the Interface tabs are evaluated on a first match basis. This means that reading the ruleset for an interface from top to bottom, the first rule that matches will be the one used by the firewall. Evaluation stops after reaching this match and then the firewall takes the action specified by that rule. Always keep this in mind when creating new rules, especially when crafting rules to restrict traffic. The most permissive rules should be toward the bottom of the list, so that restrictions or exceptions can be made above them.
pfSense software is a stateful firewall, which means it remembers information about connections flowing through the firewall so that it can automatically allow reply traffic. This data is retained in the State Table. The connection information in the state table includes the source, destination, protocol, ports, and more: Enough to uniquely identify a specific connection.
Using this mechanism, traffic need only be permitted on the interface where it enters the firewall. When a connection matches a pass rule the firewall creates an entry in the state table. Reply traffic to connections is automatically allowed back through the firewall by matching it against the state table rather than having to check it against rules in both directions. This includes any related traffic using a different protocol, such as ICMP control messages that may be provided in response to a TCP, UDP, or other connection.
This does not apply to Ethernet (Layer 2) Rules which do not keep state, or to rules which have manually disabled the keep state option.
State table size¶
The firewall state table has a maximum size to prevent memory exhaustion. Each state takes approximately 1 KB of RAM. The default state table size in pfSense is calculated by taking about 10% of the RAM available in the firewall by default. On a firewall with 1GB of RAM, the default state table size can hold approximately 100,000 entries.
See Large State Tables for more information on state table sizing and RAM usage.
Each user connection typically consists of two states: One created as it enters the firewall, and one as it leaves the firewall. Therefore, with a state table size of 1,000,000, the firewall can handle approximately 500,000 user sessions actively traversing the firewall before any additional connections will be dropped. This limit can be increased as needed so long as it does not exceed the available amount of RAM in the firewall.
To increase the state table size:
Navigate to System > Advanced on the Firewall & NAT tab
Enter the desired number for Firewall Maximum States, or leave the box blank for the default calculated value. See Figure Increased State Table Size to 2,000,000
Historical state table usage is tracked by the firewall. To view the graph:
Navigate to Status > Monitoring
Click to expand the graph options
Set Category for the Left Axis to System
Set the Graph for the Left Axis to States
Click Update Graphs
Block vs. Reject¶
There are two ways to disallow traffic using firewall rules on pfSense: Block and reject.
A rule set to block will silently drop traffic. A blocked client will not receive any response and thus will wait until its connection attempt times out. This is the behavior of the default deny rule in pfSense software.
A rule set to reject will respond back to the client for denied TCP and UDP traffic, letting the sender know that the connection was refused. Rejected TCP traffic receives a TCP RST (reset) in response, and rejected UDP traffic receives an ICMP unreachable message in response. Though reject is a valid choice for any firewall rule, IP protocols other than TCP and UDP are not capable of being rejected; These rules will silently drop other IP protocols because there is no standard for rejecting other protocols.
Deciding Between Block and Reject¶
There has been much debate amongst security professionals over the years as to the value of block vs. reject. Some argue that using block makes more sense, claiming it “slows down” attackers scanning the Internet. When a rule is set to reject, a response is sent back immediately that the port is closed, while block silently drops the traffic, causing the attacker’s port scanner to wait for a response. That argument does not hold water because every good port scanner can scan hundreds or thousands of hosts simultaneously, and the scanner is not stalled waiting for a response from closed ports. There is a minimal difference in resource consumption and scanning speed, but so slight that it shouldn’t be a consideration.
If the firewall blocks all traffic from the Internet, there is a notable difference between block and reject: Nobody knows the firewall is online. If even a single port is open, the value of that ability is minimal because the attacker can easily determine that the host is online and will also know what ports are open whether or not the blocked connections have been rejected by the firewall. While there isn’t significant value in block over reject, the best practice is to use block on WAN rules. There is some value in not actively handing information to potential attackers, and it is also a bad practice to automatically respond to an external request unnecessarily.
For rules on internal interfaces the best practice is to use reject in most situations. When a host tries to access a resource that is not permitted by firewall rules, the application accessing it may hang until the connection times out or the client program stops trying to access the service. With reject the connection is immediately refused and the client avoids these hangs. This is usually nothing more than an annoyance, but it is still a good idea to use reject to avoid potential application problems induced by silently dropping traffic inside a network.