How to Set Up and Manage PF Firewall on FreeBSD 14
pf or Packet Filter is a stateful firewall software for BSDs operating system. Originally written for OpenBSD, but has been ported to other BSD systems like FreeBSD. pf also powered firewall operating system “pfSense”, which is also based on FreeBSD.
In this guide, you will learn the following about pf:
- How to enable and manage pf on FreeBSD.
- Creating the boilerplate/template of pf rules
/etc/pf.conf
. - The basic usage of the
pfctl
utility for managing pf. - How to monitor pf using
pflog
andtcpdump
.
Prerequisites
Before proceeding, ensure you have the following:
- FreeBSD 14 server.
See more: FreeBSD 14: Step-by-Step Installation (With Screenshot) - A root user or non-root user with sudo privileges.
- Text editor like
vim
,nano
, orpico
installed.
See more: How to Set Up FreeBSD Server (16 Things to do)
How to Enable PF on FreeBSD?
To get started with pf, let’s now enable it using the following steps:
1. Add the pf_enable="YES"
option to /etc/rc.conf
using the sysrc
command below. In this case, you will also specify the pf configuration to /etc/pf.conf
via the pf_rules
option.
sysrc pf_enable="YES"
sysrc pf_rules="/etc/pf.conf"
2. Now run the following command to enable the pflog
service and specify the pflog log file to /var/log/pflog
.
By enabling pflog
, you will create the pflog0
interface that will monitor traffic in the pf
firewall. This allows you to monitor pf
in real-time via tcpdump
and store logged packets to disk.
sysrc pflog_enable="YES"
sysrc pflog_logfile="/var/log/pflog"
3. Lastly, run the following sysrc
command to verify both pf
and pflog
services on your FreeBSD system.
sysrc -a | grep pf
You will see both pf
and pflog
is enabled like the following:
Applying PF (Packet Filter) on FreeBSD
Before starting pf, you must create pf rules on /etc/pf.conf
and populate services and ports for your applications.
Complete the following tasks to create a basic pf rule:
1. To configure pf, open/create the configuration /etc/pf.conf
using vim
.
vim /etc/pf.conf
2. Insert the following configuration to populate basic services via Macros.
Macro is similar to a variable in programming language which allows you to store multiple items on a single macros. The naming of a macro must start with a letter and then must contain a digit, later, or underscore.
tcp_services = "{ ssh, http, https, domain}"
udp_services = "{ domain }"
tcp_custom = "{ 3896, 8080 }"
udp_custom = "{ 5353 }"
icmp_types = "{ echoreq, unreach }"
In this case, you will create macros with the following purposes:
- tcp_services: This will be used to populate services running on TCP protocol, such as HTTP, HTTPS, and FTP. See
/etc/services
file to get the list services name. - udp_services: Populating services that run on UDP protocols, such as domain/dns (53) and syslog (514).
- tcp_custom: Custom ports for TCP protocol. If you’re running SSH on a custom port, add your port to this macros.
- udp_custom: Custom ports for UDP protocol.
- icmp_types: Populate ICMP types.
3. Add the following lines to skip operation on localhost. Packets within the localhost lo0 interface will not be filtered by pf.
set skip on lo0
4. Now, add the following configuration to set up the default rule on your FreeBSD system.
In this example, you will block all incoming traffic and allow all outgoing traffic. Also, you will enable traffic normalization for incoming traffic via the scrub
option.
scrub in
block in all
pass out all keep state
5. Next, insert the following configuration to allow incoming traffic within the macros tcp_services, udp_services, tcp_custom, and udp_custom.
pass in proto tcp to port $tcp_services keep state
pass in proto udp to port $udp_services keep state
pass in proto tcp to port $tcp_custom keep state
pass in proto udp to port $udp_custom keep state
6. Then, add the configuration below to allow ping or ICMP traffic to the server. In this case, only icmp_types macros will be allowed.
pass in inet proto icmp icmp-type $icmp_types keep state
Save the file and exit the editor.
Below is the complete basic pf template:
tcp_services = "{ ssh, http, https, domain}"
udp_services = "{ domain }"
tcp_custom = "{ 3896, 8080 }"
udp_custom = "{ 5353 }"
icmp_types = "{ echoreq, unreach }"
set skip on lo0
scrub in
block in all
pass out all keep state
pass in proto tcp to port $tcp_services keep state
pass in proto udp to port $udp_services keep state
pass in proto tcp to port $tcp_custom keep state
pass in proto udp to port $udp_custom keep state
pass in inet proto icmp icmp-type $icmp_types keep state
7. Lastly, run the pfctl
command below to verify the pf syntax and rules.
pfctl -vnf /etc/pf.conf
If no error, you should get a similar output like the following:
Managing PF Firewall Service
At this point, you’ve enabled pf and created basic rules in /etc/pf.conf file. Let’s start pf on your FreeBSD system.
1. Execute the service
command below to start the pflog
and pf
services.
service pflog start
service pf start
Note: After pf
starts, your current SSH connection will be disconnected. Reconnect to your server to continue.
2. Now run the command below to verify both the pflog
and pf
services status.
service pflog status
service pf status
In the following example, you can see that pflog
is running with PID (Process ID) 2853 and the pf
with status Enabled and has been running for 2 minutes.
Basic pfctl Command
pfctl is a utility for managing pf (packet filter) on FreeBSD. With pfctl, you can easily enable/disable pf, checking pf status such as current rule, and connection state, and also allows you to manage anchor, macro, and table on pf.
Follow these steps to learn the basic usage of pfctl on FreeBSD:
1. To enable pf, run the pfctl
command below.
pfctl -e
2. To disable pf, execute the pfctl
command below. With this, you can easily enable or disable pf when configuring FreeBSD without touching the pf service.
pfctl -d
3. Run the command below to verify the detailed status of pf. The output of this command is similar to the service pf status
command.
pfctl -s info
4. Now, run the command below to show enabled rules in numbered format.
pfctl -gsr | grep ^@
In the following example, you can see pf rules in numbered format.
5. Lastly, run the command below to check the state connections on your FreeBSD system.
pfctl -s state
pfctl -vs state
As seen in the following example, there are established connections from host 10.0.2.2
, which are allowed via pf rule 1 and the connection has been going for 14 minutes.
How do I Monitor PF on FreeBSD?
To monitor pf on FreeBSD, use the following tools:
- pftop: package utility for monitoring pf in real-time.
- tcpdump: allows you to monitor pf via the pflog interface and log file.
Let’s explore those two tools.
Monitoring PF via pftop
pftop
is available by default on the FreeBSD repository, you can install it via the pkg package manager. Follow these sections to monitor pf with pftop in a real-time.
1. First, install the pftop
package using the pkg
command below.
pkg install -y pftop
2. Once installed, run the pftop
command to start real-time monitoring of pf firewall.
pftop
On pftop
, you can see the active protocol, direction (in/out), source and destination, age of established connections, and also packet size in bytes.
Press q
to exit from the pftop
screen.
Monitoring PF via tcpdump
The second way to monitor pf
is by utilizing tcpdump
. The tcpdump
will monitor pf through pflog interface or log file. But, to achieve that, you must enable logging in the pf.conf file.
Complete these sections to monitor pf via tcpdump:
1. To start, let’s enable logging in the pf rule /etc/pf.conf
with the log
option.
As an example below, logging will be enabled for incoming traffic to macros tcp_services and udp_services.
pass in log proto tcp to port $tcp_services keep state
pass in log proto udp to port $udp_services keep state
You can also configure logs to specific pflog interface like the following.
pass in log (all, to pflog0) proto tcp to port $tcp_services keep state
pass in log (all, to pflog0) proto udp to port $udp_services keep state
2. Once logging is enabled on pf, run the tcpdump
command below to monitor pf via the pflog file /var/log/pflog
.
tcpdump -n -e -ttt -r /var/log/pflog
3. Or, you can monitor pf traffic via the pglog0
interface using the tcpdump
command below.
tcpdump -n -e -ttt -i pflog0
You can press Ctrl+c
to exit from the tcpdump
screen.
FAQ (Frequently Asked Question)
Below are some FAQs related to pf on BSD systems.
How to reload PF without disconnecting?
To reload pf without disconnecting, run the command service pf reload
.
How to add port range in PF?
To add port range in pf, use the format like 60000:65535
in the /etc/pf.conf
file.
How to enable logging in PF?
To enable logging in pf, add the option log
to the pass in
or pass out
line in /etc/pf.conf
file. You can also log packets to specific pflog interface log (all, to pflog0)
.
How to allow traffic from a specific network or subnet?
To allow traffic from a specific network or subnet on pf, use the following rule.
In this example, we’ll allow HTTP traffic (running on em1) from specific subnet 192.168.5.0/24.
pass in on em1 proto tcp from 192.168.5.0/24 to port http keep state
Conclusion
In summary, you have now enabled and configured pf (Packet Filter) on the FreeBSD server. You’ve also created the boilerplate/template of pf rules in the pf.conf
file, which includes macros, default pf rules (block by DROP), and basic in/out rules for TCP, UDP, and ICMP protocols.
From here, you’re ready to install applications, such as Apache or Nginx web server, MySQL or MariaDB database, and PHP or PHP-FPM, and fail2ban.