pf.conf

After reading all that I could find on pf, including "The Book of PF" and "Building Firewalls with OpenBSD and PF" I have a made my first pf.conf, but I am hoping those with experience with PF would look over my configuration and give me any tips or pointers.

Code:
# Macros
# ext_if -- the interface to the outside world
ext_if="axe0"

# prv_if -- the interface to the private hosts
prv_if="axe1"
localnet = $prv_if:network

# dmz_if -- the interface to the DMZ
dmz_if="axe2"

# prv_hosts -- the list of addresses of hosts on the screened LAN
prv_hosts = "{ 192.168.1.10, 192.168.1.11 }"

# dmz_hosts -- the list of addresses of hosts in the DMZ
dmz_hosts = "{ 192.168.2.2/32, 192.168.2.3/32, 192.168.2.4 }"

# dmz_www -- the address of the WWW server in the DMZ
dmz_www = "192.168.2.2/32"

# dmz_smtp -- the address of the SMTP server in the DMZ
dmz_smtp = "192.168.2.3/32"

# dmz_dns -- the address of the DNS server in the DMZ
dmz_dns = "192.168.2.4/32"

# known ports
www_ports = "{ http, https }"
mail_ports = "{ smtp, pop3, imap, imaps, pop3s }"

# Tables
# Non-routable addresses
table <rfc1918> const { 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 }

# Options
set skip on lo
set reassemble yes
set require-order yes
set block-policy drop
set loginterface $ext_if
set state-policy if-bound
set fingerprints "/etc/pf.os"
set ruleset-optimization none

# Timeout options
set optimization aggressive
set timeout { frag 30, tcp.established 120 }
set timeout { tcp.first 30, tcp.closing 30, tcp.closed 30, tcp.finwait 30 }
set timeout { udp.first 30, udp.single 30, udp.multiple 30 }
set timeout { other.first 30, other.single 30, other.multiple 30 }

# Block spoofed packets: enable "set state-policy if-bound" above
antispoof log quick for { lo0 $dmz_if $prv_if ($ext_if) }

# Block non routable addresses
block drop in quick on $ext_if from <rfc1918> to any
block drop out quick on $ext_if from any to <rfc1918> 
block in quick on $ext_if inet from any to 255.255.255.255
block in log quick on $ext_if inet from urpf-failed to any
block in log quick on $ext_if inet from no-route to any

# Normalize packets
# 4.7 change to scrub
match in all scrub (no-df random-id min-ttl 5 max-mss 1440 reassemble tcp)
match out on $ext_if scrub (no-df random-id)

# Translate packets
# nat for the private hosts
match out on egress from $prv_hosts to any nat-to egress
# nat for the DMZ hosts
match out on egress from $dmz_hosts to any nat-to egress
# redirect connections to port 80 (HTTP) to DMZ
match in on egress inet proto tcp from any to any port $www_ports rdr-to $dmz_www
# redirect connections to port 25 (SMTP) to DMZ
match in on egress inet proto tcp from any to any port 25 rdr-to $dmz_smtp
# redirect connections to port 53 (DNS) to DMZ
match in on egress inet proto { tcp, udp } from any to any port 53 rdr-to $dmz_dns 

# Filter packets
# block all incoming connections sent from the outside
# log all blocked packets
#block all
block log on $ext_if
block return log on $prv_if
block return log on $dmz_if

# pass all connections originating from the firewall
pass out quick on $ext_if inet \
        from ($ext_if) to any flags S/SA modulate state
# pass all connections originating from the screened LAN
pass in quick on $prv_if from $prv_hosts to any flags S/SA
# pass all connections originating from the DMZ
pass in quick on $dmz_if from $dmz_hosts to any flags S/SA
# pass all connections to the WWW host in the DMZ
pass in on $ext_if proto { tcp, udp } from any to $dmz_www \
        port $www_ports flags S/SA synproxy state
# pass all connections to the SMTP host in the DMZ
pass in on $ext_if proto { tcp, udp } from any to $dmz_smtp \
        port $mail_ports flags S/SA synproxy state
# pass all connections to the DNS host in the DMZ
pass in on $ext_if proto { tcp, udp } from any to $dmz_dns \
        port 53 flags S/SA keep state
 
Everything looks fine offhand although you want to keep your block rules all in the same section. You have blocks for non routables, then nat and then block again.
 
Thanks, I appreciate the review, and I'll move the block after NAT to the other section which is before NAT.
 
Just so you know per man pf.conf(5) the order should be Macros, Tables, Options, Traffic Normalization (e.g. scrub), Queueing, Translation (Various forms of NAT), Packet Filtering.

Having read the book of pf myself one thing I found disappointing is you really need to have some solid knowledge before reading the book (it would of been nice if it did more reinforcing of basics). So if you haven't I would suggest reading the handbook section on PF and especially all the man pages it links to in that section.

Also one thing I just noticed is some of the settings you have are default. Like flags S/SA is the default for any stateful connection so you could remove those (same thing with some of the keep state you have). There are a number of ways you can write rules in pf that accomplish the same thing and one of the things I usually do after I have a set of pf rules I like is to go back over and to try and trim things down so everything is as small as possible. Editing the pf.conf and then doing "pfctl -vnf /etc/pf.conf" is invaluable for this. It doesn't really change how PF works but I found it a learning experience doing that and I personally like my firewall rules to be as small as possible (it also helps when you have to go back a month from now and change something).
 
The 'match' and 'nat-to' rules in your pf.conf are not present in FreeBSD's current pf implementation yet (unless it has been ported to -CURRENT, but I don't think so). It belongs in the newer version of PF for OpenBSD. Make sure your rules adhere to pf.conf(5) on FreeBSD.
 
Back
Top