Scalable PF Rulesets

From Devpit
Jump to: navigation, search

Writing a PF ruleset that scales linearly when you add more interfaces or policies can be a daunting task. The first-impulse way to write PF rulesets usually results in something that scales quadradically as more interfaces are added. Usually it's possible to rearrange things a little, though, so it scales linearly. The trick is to write each policy with input or output rules based on which requires fewer rules.

Perhaps this is easiest to demonstrate by example. The following is an example PF ruleset with five interfaces.

# SECTION 1:  Macros & Tables

all_ifs = "{ lo0, sf0, sf1, sf2, sf3 }"
external_if = "sf0"
dmz_if = "sf1"
private_if = "sf2"
wireless_if = "sf3"
table <trusted_ips> file "/etc/pf.trusted_ips"

# Necessary for the pass in rules at the bottom
table <self> {self}

my_server = "$my_server"

# SECTION 2:  Options

set block-policy return
set fingerprints "/dev/null" 
set state-policy if-bound

 
# SECTION 3:  Traffic Normalization

scrub in all


# SECTION 4:  Queueing


# SECTION 5:  Translation (first-match)

nat on $external_if from {$sf2, $sf3} to any -> ($ext_if)


# SECTION 6:  Packet Filtering (last-match except quick)

# SECTION 6.1:  General configuration rules for all networks and self

pass quick on lo0 from self to self   # must be before antispoof -- see pf.conf(5)
antispoof log quick for $all_ifs
pass out quick inet proto icmp from any to any icmp-type echoreq keep state


# SECTION 6.2:  Specific policy rules follow

# SECTION 6.2.1:  Traffic for self
# must white list "in" traffic to self as well
pass in quick from any to self port ssh keep state
pass out quick from self to any keep state


# SECTION 6.2.2:  Traffic for External
pass out quick on $external_if inet proto tcp from any to any flags S/SARF modulate state
pass out quick on $external_if inet proto udp from any to any keep state


# SECTION 6.2.3:  Traffic for DMZ

# Primary server on $my_server
pass out quick on $dmz_if inet proto tcp from <trusted_ips> to $my_server flags S/SARF modulate state
pass out quick on $dmz_if inet proto tcp from any to $my_server port 22 flags S/SARF modulate state    # ssh
pass out quick on $dmz_if inet proto tcp from any to $my_server port 25 flags S/SARF modulate state    # smtp
pass out quick on $dmz_if inet proto udp from any to $my_server port 53 keep state
pass out quick on $dmz_if inet proto tcp from any to $my_server port 53 flags S/SARF modulate state    # domain
pass out quick on $dmz_if inet proto tcp from any to $my_server port 80 flags S/SARF modulate state    # http
pass out quick on $dmz_if inet proto tcp from any to $my_server port 113 flags S/SARF modulate state   # ident
pass out quick on $dmz_if inet proto tcp from any to $my_server port 443 flags S/SARF modulate state   # https
pass out quick on $dmz_if inet proto tcp from any to $my_server port 993 flags S/SARF modulate state   # imaps
pass out quick on $dmz_if inet proto tcp from any to $my_server port 995 flags S/SARF modulate state   # pop3s
block out quick on $dmz_if inet from any to $my_server

# All others (block unless passed above)
block out quick on $dmz_if inet from any to any

# SECTION 6.2.4:  Traffic for Private
block out quick on $private_if inet from any to any


# SECTION 6.2.5:  Traffic for Wireless
# The wireless trusts everything except external traffic
pass out quick on $wireless_if inet proto tcp from 10.0.0.0/8 to any flags S/SARF modulate state
pass out quick on $wireless_if inet proto udp from 10.0.0.0/8 to any keep state
block out quick on $wireless_if inet from any to any


# SECTION 6.3:  More generic rules
# XXX: Why is this?  Without these rules, all "pass out keep state" rules seem to break.  It's more efficient to use the state table anyway, but why is this?
pass in log quick proto tcp from any to !<self> all flags S/SARF keep state
pass in log quick proto udp from any to !<self> all keep state
pass in log quick proto icmp all keep state
#pass in log quick all  # XXX: I'd expect this to work just as well as the rules above

# If it hasn't been passed yet, block
block log quick all