pfctl cheat sheet
securityfirewallspfctl-cheat-sheet

Generic

Only those commands, which you will probably require for setting pf up.

pfctl -s Tables              ;# lists all tables currently loaded
pfctl -t [TABLENAME] -T show ;# shows content of table [TABLENAME]

tcpdump -n -e -ttt -r /var/log/pflog
tcpdump -n -e -ttt -i pflog0

Anchors

Fail2ban has recently switched to using anchors to avoid unnecessary reloading of the whole rule set. That was the first time I had to do with anchors and since I could not figure out a simple way to display the contents of all private tables (which can be described as subtables of anchors), I came up with this solution for my collectd monitoring:

# display all anchors...
pfctl -a f2b -s Anchors

# display all private tables:
pfctl -a f2b -s Anchors | xargs -I{} pfctl -a '{}' -s Tables

# display the contents of all tables (leave -x if you would like only the IPs to be shown):
printf "pfctl -a %s -t %s -T show\n" $(paste <(pfctl -a f2b -s Anchors) <(pfctl -a f2b -s Anchors | xargs -I{} pfctl -a '{}' -s Tables)) | sh -x -s

Sample configuration

set block-policy drop
# set block-policy return
set skip on lo0
set skip on lo1
set skip on tun0

set skip on pflog0
set loginterface pflog0

set optimization normal
set require-order yes
set fingerprints "/etc/pf.os"
set ruleset-optimization basic

if         = "{ vtnet0 }"
icmp_types = "{ echoreq, unreach }"

open_tcp6  = "{ 25, 80, 443, 1234, 1337 }"
open_udp6  = "{ 80 }"
open_tcp   = "{ 80, 25, 143, 443, 587, 1234 }"
open_udp   = "{ 53, 80, 1194 }"
jails      = "{ 10.0.0.1, 10.0.0.13, 10.0.0.100, 10.0.0.101, 10.0.0.222 }"
jailnet    = "{ 10.0.0.0/24 }"
ip         = "172.31.1.100"
ipv6net    = "{ 2a01:4f8:c17:9b::/64}"

gui        = 10.0.0.1
mail       = 10.0.0.10

chat       = 10.0.0.13
www        = 10.0.0.100
mosh       = 10.0.0.222
openvpn    = 10.0.0.66
opennet    = "{ 10.8.0.0/24 }"

table <rfc1918> persist { 127.0.0.0/8, 192.168.0.0/16,
	172.16.0.0/12, 10.0.0.0/8, 169.254.0.0/16,
	192.0.2.0/24, 0.0.0.0/8, 240.0.0.0/4 }
table <bruteforce> persist
table <sshguard> persist

scrub in all fragment reassemble random-id

rdr on $if inet  proto tcp from any to $if port { 80, 443 }        -> $www
rdr on $if inet  proto udp from any to $if port { 80 }             -> $www
rdr on $if inet  proto tcp from any to $if port { 1234 }           -> $chat
rdr on $if inet  proto tcp from any to $if port { 25,143,587 }     -> $mail
rdr on $if inet  proto udp from any to $if port { 1194 }           -> $openvpn

nat on $if inet  proto {tcp udp icmp} from $jails to any     -> $ip
nat on $if inet  proto {tcp udp icmp} from $jailnet to any   -> $ip

block log all
block drop in  log quick on $if    from <rfc1918> to any
block drop out log quick on $if    from any to <rfc1918>
block drop log     quick           from <bruteforce>
block drop log     quick proto tcp from <sshguard> to any port 22 label "ssh bruteforce"
antispoof quick for $if

pass proto tcp from $ip to $jailnet keep state
pass on $if inet6 proto tcp from any to 2a01:4f8:c17:9b::1 port {80,443} keep state
pass on $if inet6 proto tcp from any to 2a01:4f8:c17:9b::1 port {25,143,587} keep state
pass on $if inet6 proto icmp6 all keep state

pass in on $if inet proto tcp from any to any port $open_tcp keep state
pass in on $if inet proto udp from any to any port $open_udp keep state
pass in on $if inet proto icmp all icmp-type $icmp_types keep state

pass in on $if inet proto { tcp } from any to any port ssh
   flags S/SA keep state
   (max-src-conn 15, max-src-conn-rate 5/3,
   overload <bruteforce> flush global)
pass in on $if inet proto { tcp, udp } from any to $ip port {smtp,submission,imap}
   flags S/SA keep state
   (max-src-conn 15, max-src-conn-rate 5/3,
   overload <bruteforce> flush global)

pass out quick all keep state

Problems and Solutions

/usr/local/etc/pf.conf:76: could not parse host specification

  • check if the interface mentioned in line 76 is up and has the IP-Address assigned
  • in case of a tun interface something like ifconfig tun0 alias 10.8.0.18 10.8.0.255
top