Set Up a Basic Firewall on FreeBSD
I use Packet Filter (PF) to set up a basic firewall on FreeBSD. To start, the only service I’m running that requires inbound access is SSH. SSHGuard is added for an additional layer of security.
Start Here
PF requires a ruleset configuration file to operate. Example rulesets are found in /usr/share/examples/pf/.
I implement a basic ruleset by creating the /etc/pf.conf file with:
tcp_services = "{ ssh }"
scrub in all
block in all
pass out all keep state
pass in proto tcp to port $tcp_services
This blocks all inbound traffic by default, with the exception of ssh, and allows all outbound traffic to pass.
Enable PF
Set the pf kernel module to be loaded by adding to /etc/rc.conf:
doas sysrc pf_enable=YES
Add a line which specifies the path to the ruleset file:
doas sysrc pf_rules=/etc/pf.conf
Enable logging support, provided by pflog(4), by adding:
doas sysrc pflog_enable=YES
Reboot.
SSH connections are allowed in. Everything else is blocked. Try running ping directed at the firewalled FreeBSD machine and observe that all packets are dropped.
Use the pfctl(8) command to manage pf. Example: Check /etc/pf.conf for errors, but do not load ruleset:
-> doas pfctl -nvf /etc/pf.conf
tcp_services = "{ ssh }"
scrub in all fragment reassemble
block drop in all
pass out all flags S/SA keep state
pass in proto tcp from any to any port = ssh flags S/SA keep state
To view a dynamic snapshot of traffic that passes through the firewall, install the pftop(8) package:
doas pkg install pftopSSHGuard
Install sshguard to block bad ssh login attempts:
doas pkg install sshguard
Add to rc.conf:
doas sysrc sshguard_enable=YES
Set the appropriate firewall BACKEND for sshguard by modifying /usr/local/etc/sshguard.conf:
# Full path to backend executable (required, no default)
BACKEND="/usr/local/libexec/sshg-fw-pf"
SSHGuard works alongside pf by adding attackers to the table <sshguard>. Create this table and block attackers by adding the following lines to the evolving /etc/pf.conf:
table <sshguard> persist
.
.
.
block in proto tcp from <sshguard>
NOTE
Ensure the line table <sshguard> persist comes before filtering rules to prevent syntax errors.
See sshguard-setup(7) for more details.
Reload the rules:
doas pfctl -f /etc/pf.conf
Start the service:
doas service sshguard start
To inspect the contents of the block table, run:
doas pfctl -t sshguard -T showICMP
Blocking ping can make troubleshooting network issues more difficult. Let’s give it a pass, and allow destination unreachable messages. Add to /etc/pf.conf:
icmp_types = "{ echoreq, unreach }"
.
.
.
pass in inet proto icmp icmp-type $icmp_typesFinal Config
The complete /etc/pf.conf:
tcp_services = "{ ssh }"
# 'echoreq' allows inbound ping requests, and 'unreach'
# allows destination unreachable messages
icmp_types = "{ echoreq, unreach }"
# table added before filtering rules to prevent syntax errors
table <sshguard> persist
scrub in all
block in all
block in proto tcp from <sshguard>
pass out all keep state
pass in inet proto icmp icmp-type $icmp_types
pass in proto tcp to port $tcp_services
Reload the rules:
doas pfctl -f /etc/pf.confResources
- FreeBSD Handbook: PF
- YouTube: Setting up a basic firewall with pf on FreeBSD
- SSHGuard Manual: sshguard-setup
- SSH key-based authentication
You can like, share, or comment on this post on the Fediverse 💬
« Previous: Configure SSH for Passwordless Logins to FreeBSD Servers