PF is a powerful packet filter included in the base system. This starter ruleset blocks unsolicited incoming traffic, allows SSH and essential ICMP, and lets all outgoing traffic out.
1Write /etc/pf.conf
ext_if = "em0"
tcp_in = "{ ssh }"
icmp_types = "{ echoreq unreach }"
table <sshguard> persist
set skip on lo0
set block-policy return
scrub in all
block in log all
pass out quick keep state
block in quick on $ext_if from <sshguard>
antispoof quick for $ext_if
pass in on $ext_if inet proto icmp icmp-type $icmp_types
pass in on $ext_if inet6 proto icmp6
pass in on $ext_if proto tcp to port $tcp_in keep state
Allowing ICMPv6 is essential: IPv6 neighbour discovery and path MTU discovery depend on it. The <sshguard> table is used by the SSH hardening tutorial.
2Check the syntax
pfctl -nf /etc/pf.conf
3Enable and start PF
Starting PF over SSH can drop your session if the ruleset is wrong. Do it from the console if you can. Over SSH, a safety timer that disables PF after two minutes gives you a way back.
sysrc pf_enable=YES pflog_enable=YES
service pflog start
(sleep 120 && pfctl -d) & # safety net
service pf start
# still connected? cancel the safety net:
kill %1
4Day-to-day commands
pfctl -sr # show loaded rules
pfctl -ss # show the state table
pfctl -si # statistics
pfctl -f /etc/pf.conf # reload after editing
tcpdump -n -e -ttt -i pflog0 # watch blocked packets
5Opening another port
Add the service name or port number to tcp_in, for example tcp_in = "{ ssh 8080 }", then check and reload.