{{example:example.png?nolink&150|}}
====== Setting up PacketFilter (pf) on FreeBSD ======
[[https://www.freebsd.org/cgi/man.cgi?query=pf&apropos=0&sektion=0&manpath=FreeBSD+12.0-RELEASE+and+Ports&arch=default&format=html|PacketFilter (pf)]] is a well known firewall application originally developed by the [[https://www.openbsd.org|OpenBSD project]]. Though the implementation details between the FreeBSD and OpenBSD versions differ each are similar in syntax and common rulesets may be ported between distributions. This tutorial covers buidling a simple ruleset from scratch.
//Default deny// and //default permit// are the two approaches to building a firewall. The default deny approach blocks all traffic and permits only traffic specified by a rule. The default permit does the opposite. It allows all traffic and blocks only traffic specified by a rule. This tutorial uses the default deny approach.
Pf rulesets are stored in a configuration file at ''/etc/pf.conf''. It's okay to store this file elsewhere as long as the location is specified in ''/etc/rc.conf''.
This tutorial adds rules to control SSH traffic. Before starting ensure that you can access your SDF VPS console. It is recommended to connect to your VPS through the console for this tutorial to avoid inadvertently locking yourself out via SSH.
Begin by taking note of the external interface name of your VPS. This can be found using ''ifconfig -a'' and looking for the interface that contains your EXTERNAL_IP. In the example output below the interface is ''xn0''.
xn0: flags=8843 metric 0 mtu 1500
options=3
ether aa:00:00:d1:09:33
inet EXTERNAL_IP netmask 0xffffff00 broadcast 205.166.94.255
media: Ethernet manual
status: active
nd6 options=29
Edit ''/etc/pf.conf'' and begin by adding a few macros.
ext_if="xn0" # Define the interface name
ssh_in="{ 22 }" # Define the inbound service ports
svc_out="{ 22 53 80 123 443 }" # Define the outbound service ports
icmp_types="{ echoreq unreach }" # Define allowed ICMP types
These macros will be expanded where referenced in the rules. If you have set any services to run on non-standard ports make sure that the port numbers are in the appropriate macro. The services defined in ''svc_out'' are SSH, DNS, HTTP, NTP, and HTTPS respectively. Pf supports use of service names such as SSH, DNS, etc. which map to the service's default port. This tutorial sticks to port numbers for consistency.
Next, add some tables to the config. Tables are similar to macros but are designed to hold groups of IP addresses. The first table is '''' which will hold a list of reserved IP addresses. The second is '''' which is defined as empty and will be used to hold rate-limited IP addresses.
table { 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 \
169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 \
192.0.0.0/29 192.0.2.0/24 192.88.99.0/24 \
192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 \
240.0.0.0/4 255.255.255.255/32 }
table
Set the default return policy and logging.
set block-policy return # Set the default return policy on blocks. This will send a RST message to the connecting host
set loginterface $ext_if # Configure logging for the external interface
set skip on lo0 # Don't apply any filtering on the loopback interface
scrub in all fragment reassemble max-mss 1440 # Reassemble all fragmented packets before processing with a max packet size of 1440
Add the rules as the last part of the config.
# Apply the antispoof directive on $ext_if. The automatic
# antispoofing blocks all traffic from the network of that
# interface unless it originates from that same interface.
antispoof quick for $ext_if
# The quick keyword executes a rule immediately without
# considering the rest of the ruleset. The egress keyword
# automatically finds the default route(s) on a given
# interface.
block in quick on egress from
block return out quick on egress to
# The default deny policy
block all
# The inbound SSH rule. This rule allows traffic on
# $ext_if to the $ssh_in port, limiting connections
# to 15 per-host at a rate of 3 connections per-second
# while adding hosts breaking those rules to the
# table.
pass in on $ext_if proto tcp to port $ssh_in \
keep state (max-src-conn 15, max-src-conn-rate 3/1, \
overload flush global)
# Allow all TCP and UDP traffic on the $svc_out ports.
# This permits communication to the defined services.
pass out proto { tcp udp } to port $svc_out
# Allow the defined ICMP types
pass out inet proto icmp icmp-type $icmp_types
Save the ruleset at ''/etc/pf.conf'', enable the services, and start the services.
# Enable the services via rc.conf
sysrc pf_enable="YES"
sysrc pflog_enable="YES"
# Start the services
service pf start
service pflog start
# Load the rules
pfctl -f /etc/pf.conf
Pf is now started and the ruleset is enabled. For good measure reboot the system as well.
After rebooting, test the firewall by attempting to ping or connect to outside hosts and connect to your VPS via SSH. To see pf stats, run ''pfctl -si''
Over time the '''' will fill with IP addresses. It can be inspected by running ''pfctl -t excess -T show''. The table can be cleared by running ''pfctl -t excess -T expire 172800''. That will clear all entries that have aged more than 48 hours in the table. If you notice IP addresses appearing in the table frequently, they can be added as quick blocks in the ruleset config. Adding a daily cronjob to clear the script is a good idea. The script can be added to root's crontab or you can put the script in ''/usr/local/etc/periodic/daily''.
----
$Id: VPS_FreeBSD_Setup_PF.html,v 1.2 2023/09/11 00:09:18 dnielsen Exp $ [[http://sdf.org/?tutorials/VPS_FreeBSD_Setup_PF|Setting up PacketFilter (pf) on FreeBSD]] - traditional link (using [[wp>Revision_Control_System|RCS]])