On a workstation where you are the only user, you can use a very simple /etc/pf.conf:
set skip on lo0 # don't filter localhost packets ext_if = "em0" # replace em0 with your external interface set block-policy drop # by default, drop packets. You can also set block-policy reject set loginterface $ext_if # log that interface block all # block all traffic by default pass in inet proto icmp icmp-type 8 code 0 # icmp packets pass in inet proto icmp icmp-type 3 code 4 # icmp needfrag (MTU) pass in inet6 proto ipv6-icmp icmp6-type {2 128} keep state pass out all # pass all outgoing traffic
This will allow the necessary ICMP traffic (useful for network diagnosis) while blocking all other incoming connections.
(As a general rule, the last matching rule determines the action.)
I generally don't whitelist by IP addresses because I've had times where I needed to access a system from a different IP. I also avoid OS fingerprinting because, although it is available, it's not 100% accurate.
To load the ruleset once you've edited it, run:
$ doas pfctl -f /etc/pf.conf
To disable the firewall (useful for diagnosing the network), run:
$ doas pfctl -d
To enable it again:
$ doas pfctl -e
For a server, you will want to, at a minimum, allow incoming ssh packets:
set skip on lo0 # don't filter localhost packets ext_if = "em0" # my external interface is em0 set block-policy drop # by default, drop packets. You can also set block-policy reject set loginterface $ext_if # log that interface pass in proto tcp from 192.168.1.1 to port ssh pass in inet proto icmp icmp-type 8 code 0 # icmp packets pass in inet proto icmp icmp-type 3 code 4 # icmp needfrag (MTU) pass in inet6 proto ipv6-icmp icmp6-type {2 128} keep state pass out all # pass all outgoing traffic
Replace 192.168.1.1 with your IP.
As a general rule, your servers should also accept incoming http and https connections. This is necessary for running a web server and also for acquiring a properly signed SSL certificate. Here is the /etc/pf.conf:
set skip on lo0 # don't filter localhost packets ext_if = "em0" # my external interface is em0 set block-policy drop # by default, drop packets. You can also set block-policy reject set loginterface $ext_if # log that interface pass in proto tcp from 192.168.1.1 to port ssh pass in inet proto icmp icmp-type 8 code 0 # icmp packets pass in inet proto icmp icmp-type 3 code 4 # icmp needfrag (MTU) pass in inet6 proto ipv6-icmp icmp6-type {2 128} keep state pass in proto tcp to port {http https} pass out all # pass all outgoing traffic