#
# Default PF configuration file.
#
# This file contains the main ruleset, which gets automatically loaded
# at startup.  PF will not be automatically enabled, however.  Instead,
# each component which utilizes PF is responsible for enabling and disabling
# PF via -E and -X as documented in pfctl(8).  That will ensure that PF
# is disabled only when the last enable reference is released.
#
# Care must be taken to ensure that the main ruleset does not get flushed,
# as the nested anchors rely on the anchor point defined here. In addition,
# to the anchors loaded by this file, some system services would dynamically 
# insert anchors into the main ruleset. These anchors will be added only when
# the system service is used and would removed on termination of the service.
#
# See pf.conf(5) for syntax.
#

# References for modifications:
# The Book of PF by Peter N.M. Hansteen, p. 21
# http://ikawnoclast.com/security/mac-os-x-pf-firewall-avoiding-known-bad-guys/
# http://support.apple.com/kb/HT5519?viewlocale=en_US&locale=en_US
# http://blog.scottlowe.org/2013/05/15/using-pf-on-os-x-mountain-lion/
# http://krypted.com/mac-security/a-cheat-sheet-for-using-pf-in-os-x-lion-and-up/


# Internal interface; use the command `ifconfig -a` or:
# $ ifconfig | pcregrep -M -o '^[^\t:]+:([^\n]|\n\t)*status: active' | egrep -o -m 1 '^[^\t:]+'
int_if = "@INTERFACE@"

# VPN network (uncomment '#vpn#' comment lines)
# $vpn_net == utun0/24 when Tunnelblick creates utun0
#vpn# vpn_net = "10.8.0/24"   # utun0 interface doesn't exist at boot time


# Options
set block-policy return
set fingerprints "/etc/pf.os"
set ruleset-optimization basic
set skip on lo0

 
# Normalization
# Scrub incoming packets
scrub in all no-df
 
#
# com.apple anchor point
#
scrub-anchor "com.apple/*"


# Queueing
 

# Translation

# OpenVPN Server NAT
# 
# The Book of PF, p. 21
# Allow VPN connections to the VPN host:
# http://serverfault.com/questions/555594/troubleshoot-broken-tcp-from-openvpn-client-to-server-but-ping-traceroute-work
#tun_if = "utun0"
#no nat on ! $tun_if from $vpn_net to ($int_if)
#nat on ! $tun_if from $vpn_net to ! ($int_if) -> ($int_if)
# Use a list in case Tunnelblick creates multiples utun interaces
#tun_if = "{ utun0, utun1, utun2, utun3, utun4, utun5, utun6, utun7, utun8, utun9 }"
#vpn# not_tun_if = "{ !utun0, !utun1, !utun2, !utun3, !utun4, !utun5, !utun6, !utun7, !utun8, !utun9 }"
#vpn# no nat on $not_tun_if from $vpn_net to ($int_if)
#vpn# nat on $not_tun_if from $vpn_net to ! ($int_if) -> ($int_if)
# This rule must be included below BEFORE these packets are passed by other rules:
# pass in quick on $tun_if reply-to $tun_if from $vpn_net to $int_if

nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"
dummynet-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"

# macOS Server Adaptive Firewall
# Comment out for non-macOS Server instances
# anchor "com.apple.server-firewall/*"
# load anchor "com.apple.server-firewall" from "/etc/pf.anchors/com.apple.server-firewall"

# Filtering

# Block by default
block all

# Debugging:
#pass quick log (all, to pflog0) all
#block log (all, to pflog0) all

# debugging rules
# $ sudo ifconfig pflog0 create 
# $ sudo tcpdump -n -e -ttt -i pflog0
# $ sudo ifconfig pflog0 destroy 
# block log (all, to pflog0) all

# Allow VPN connections to the VPN host:
# http://serverfault.com/questions/555594/troubleshoot-broken-tcp-from-openvpn-client-to-server-but-ping-traceroute-work
# pass in quick on $tun_if reply-to $tun_if from $vpn_net to $int_if
# Rule for a lot of utun interfaces in case Tunnelblick creates extras
#vpn# pass in quick on utun0 reply-to utun0 from $vpn_net to $int_if
#vpn# pass in quick on utun1 reply-to utun1 from $vpn_net to $int_if
#vpn# pass in quick on utun2 reply-to utun2 from $vpn_net to $int_if
#vpn# pass in quick on utun3 reply-to utun3 from $vpn_net to $int_if
#vpn# pass in quick on utun4 reply-to utun4 from $vpn_net to $int_if
#vpn# pass in quick on utun5 reply-to utun5 from $vpn_net to $int_if
#vpn# pass in quick on utun6 reply-to utun6 from $vpn_net to $int_if
#vpn# pass in quick on utun7 reply-to utun7 from $vpn_net to $int_if
#vpn# pass in quick on utun8 reply-to utun8 from $vpn_net to $int_if
#vpn# pass in quick on utun9 reply-to utun9 from $vpn_net to $int_if

# Local net
table <lan_inet> const { 10/8, 172.16/12, 192.168/16 }
table <lan_inet6> const { ::1, fe80::/10 }

pass quick inet from <lan_inet> to any keep state
pass quick inet6 from <lan_inet6> to any keep state

# Antispoof
antispoof log quick for $int_if inet

# Block to/from illegal destinations or sources
block drop in log quick from no-route to any
block drop in log quick from urpf-failed to any
# This is observed on macOS
#block drop in log quick on $int_if from any to 255.255.255.255

# Whitelist
# Hardcoded IPs
#mydomainname_com = "xxx.xxx.xxx.xxx"
#table <whitelist> const { $mydomainname_com }
#pass in quick from <whitelist>
#pass out quick to <whitelist>

# Block brute force attacks
table <bruteforce> persist
block drop log quick from <bruteforce>

# Allow application-specific traffic over these interfaces
# multicast DNS
pass on $int_if proto { udp, tcp } to { 224.0.0.2, 224.0.0.18, 224.0.0.251 } port mdns
pass on $int_if proto igmp to { 224.0.0.1, 224.0.0.22, 224.0.0.251 }

# quick pass of Tor relay ports to avoid blocks below
#tor_relay = "{ 9001, 9030 }"
#pass in quick proto tcp from any to $int_if port $tor_relay
#pass out quick proto tcp from $int_if port $tor_relay to any

# Open Source IP blocks
# Refresh with pfctl -a blockips -T load -f @PREFIX@/etc/@NAME@/blockips.conf
anchor 'blockips' label "Open Source IP Blocks"
load anchor 'blockips' from '@PREFIX@/etc/@NAME@/blockips.conf'

# ICMP
icmp_types = "echoreq"
pass inet proto icmp from $int_if:network to any icmp-type $icmp_types
pass inet proto icmp from any to $int_if icmp-type $icmp_types

# allow out the default range for traceroute(8):
# "base+nhops*nqueries-1" (33434+64*3-1)
pass out on $int_if inet proto udp from any to any port 33433 >< 33626

# Allow critical system traffic
pass in quick inet proto udp from port bootps to port bootpc
pass out quick inet proto udp from port bootpc to port bootps

# LAN services: block access, except from localnet
lan_udp_services = "{ domain, 5001, postgresql }"
lan_tcp_services = "{ domain, auth, nntp, www,	\
	311, 3128, 5001, 5900:5909, privoxy, postgresql, 	\
	8123, 8180, 8181, 9150, 9151 }"
block in proto tcp from any to $int_if port $lan_tcp_services
block in proto udp from any to $int_if port $lan_udp_services

pass in inet proto udp from $int_if:network to $int_if port $lan_udp_services
pass in inet proto tcp from $int_if:network to $int_if port $lan_tcp_services
pass out proto udp from $int_if port $lan_udp_services to $int_if:network
pass out proto tcp from $int_if port $lan_tcp_services to $int_if:network

# Add vpn_net if running OpenVPN
#vpn# pass in inet proto udp from $vpn_net to $int_if port $lan_udp_services
#vpn# pass in inet proto tcp from $vpn_net to $int_if port $lan_tcp_services
#vpn# pass out proto udp from $int_if port $lan_udp_services to $vpn_net
#vpn# pass out proto tcp from $int_if port $lan_tcp_services to $vpn_net

# Internet services
internet_udp_services = "{ https, 500, openvpn,	\
	1701, 4500, 5060, 5190, 5297, 5298, 5678, 16384 }"
internet_tcp_services = "{ 995, 1640, 1723, 2195,	\
	2196, 4190, 5218, 5223, 5190, 5220, 5222, 5298,		\
	8008, 8443, 8800, 8843, 9001, 9030 }"
pass in proto udp from any to $int_if port $internet_udp_services
pass in proto tcp from any to $int_if port $internet_tcp_services
pass out inet proto udp from $int_if to any port $internet_udp_services
pass out inet proto tcp from $int_if to any port $internet_tcp_services

#apns_services = "{ 2195, 2196 }"
#pass in proto tcp from any port $apns_services to <lan_inet>
#pass out inet proto tcp to any port $apns_services from <lan_inet>

# ssh really restrictive
pass in inet proto tcp from any to $int_if port ssh	\
	keep state (max-src-conn 5, max-src-conn-rate 5/2,		\
	overload <bruteforce> flush global)
pass out inet proto tcp from $int_if port ssh

# web, mail more restrictive
pass in inet proto tcp from any to $int_if	\
	port { smtp, https, imap, submission, imaps }		\
	keep state (max-src-nodes 50, max-src-conn 200, max-src-conn-rate 100/10,	\
	overload <bruteforce> flush global)
pass out inet proto tcp from $int_if to any     \
        port { smtp, imap4-ssl, imap, submission, imaps }

# I2P
#i2p_port = "65530"
#pass in inet proto { udp, tcp } from any to $int_if port $i2p_port
#pass out inet proto { udp, tcp } from $int_if port $i2p_port to any