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 and tcpdump.

Prerequisites

Before proceeding, ensure you have the following:

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"
Enable pf and pflog on FreeBSD
Enable pf and pflog on FreeBSD

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:

Checking if pf and pflog is enabled
Checking if pf and pflog is enabled

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:

Verify pf syntax without loading it via pfctl
Verify pf syntax without loading it via pfctl

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.

Checking pf service status
Checking pf service status

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.

Checking pf rules in numbered format
Checking 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.

Verify connections state via pfctl
Verify connections state via pfctl

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
Installing pftop on FreeBSD
Installing pftop on FreeBSD

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 in real-time with pftop
Monitoring pf in real-time with pftop

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
Monitoring pf using pflog log file via tcpdump
Monitoring pf using pflog log file via tcpdump

3. Or, you can monitor pf traffic via the pglog0 interface using the tcpdump command below.

tcpdump -n -e -ttt -i pflog0
Monitoring pf with pflog0 interface via tcpdump
Monitoring pf with pflog0 interface via tcpdump

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.

System administrator and devops enthusiast, leveraging over 10+ years of Linux expertise to optimize operations. Proficient in FreeBSD, VMWare, KVM, Proxmox, PfSense, Ansible, Docker, and Kubernetes.

Read Also: