Ok, so here's my working final ruleset. Unfortunately, I wasn't able to get port redirection working (port forwarding is working fine). As a benefit, pf seems to be much more efficient, I get double the LAN <-> LAN and WAN <-> LAN traffic throughput/bandwidth vs old IPFW and NATd - its actually pretty amazing. So here's the sanitized ruleset:





# /etc/pf.anchors/org.example.my.pf.conf









# Version 2014-11-07









##########

# Macros - User-defined variables may be defined and used later, simplifying the configuration file.

# Macros must be defined before they are referenced in pf.conf.

##########









# don't nest macros as it can lead to invalid ruleset









# external interface (WAN)

ext_if = "en4"









# internal interface (LAN)

int_if = "en0"









# static IP for external interface, from ISP

ext_ip = "xxx.xxx.xxx.xxx"









# LAN IP address for email and web server

email_web_server_ip = "xxx.xxx.xxx.xxx"









# tcp ports to open for dns and alt ssh for firewall_dns_server

firewall_dns_server_services = "{ 53, xxxxx }"









# tcp ports to open for email and web traffic for email_web_server

email_web_server_services = "{ 25, 80, 587, 995, xxxxx }"









# udp ports to open for dns traffic on firewall_dns_server (dns server)

udp_services = "{ 53 }"









# icmp types to allow echo reply, destination unreachable, echo, time exceeded

icmp_types= "{ 0, 3, 8, 11 }"









##########

# Tables - Tables provide a mechanism for increasing the performance and

# flexibility of rules with large numbers of source or destination addresses.

##########









# emerging threats, ips for the firewall to block

table <emerging_block_ips> persist file "/etc/pf.anchors/emerging-Block-IPs.txt"









# table to conatin automatic entries for IPs which try to brute force (to be implemented later)

# table <bruteforce> persist









# table to list hosts that I want to ban manually

table <bad_hosts> persist









# Sun Microsystems cluster interconnect (should not be coming in over the internet)

# table <sunmicro> const { 204.152.64.0/23 }









# Special Use Addresses (should not be coming in over the internet)

# table <rfc5735> const { 0.0.0.0/8, 10.0.0.0/8, \

# 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, \

# 192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, \

# 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, \

# 224.0.0.0/4, 240.0.0.0/4, 255.255.255.255/32 }





# Do not block 192.88.99.0/24 RFC3068









# Special Use Addresses (should not be coming in over the internet)

table <blocked_net_addr> const { 0.0.0.0/8, 10.0.0.0/8, \

127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, \

192.0.0.0/24, 192.0.2.0/24, 192.168.0.0/16, \

198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, \

224.0.0.0/4, 240.0.0.0/4, 204.152.64.0/23 }









##########

# Options - Options tune the behavior of the packet filtering engine.

##########









# urgent -debug messages generated for serious errors

# misc - debug messages generated for various errors

# (e.g., to see status from the packet normalizer/scrubber and for state creation failures)

# loud - debug messages generated for common conditions

# (e.g., to see status from the passive OS fingerprinter)

set debug urgent









# Enable collection of packet and byte count statistics for the given interface.

set loginterface $ext_if









# A TCP RST is returned for blocked TCP packets,

# an ICMP UNREACHABLE is returned for blocked UDP packets,

# and all other packets are silently dropped.

set block-policy return









# list interfaces for which packets should not be filtered.

set skip on lo0









# Enable basic ruleset optimization. This is the default behaviour.

# Basic ruleset optimization does four things to improve the performance of ruleset evaluations:

# 1. remove duplicate rules

# 2. remove rules that are a subset of another rule

# 3. combine multiple rules into a table when advantageous

# 4. re-order the rules to improve evaluation performance

set ruleset-optimization basic









# Load fingerprints of known operating systems from the given filename.

set fingerprints "/etc/pf.os"









# Optimize state timeouts for a normal network environment.

set optimization normal









# if-bound - states are bound to the interface they're created on.

# If traffic matches a state table entry but is not crossing

# the interface recorded in that state entry, the match is rejected.

# The packet must then match a filter rule or will be dropped/rejected altogether.

# floating - states can match packets on any interface. As long as the packet matches

# a state entry and is passing in the same direction as it was on the interface

# when the state was created, it does not matter what interface it's crossing, it will pass

set state-policy floating









##########

# Traffic Normalization (e.g. scrub) - Traffic normalization protects internal machines against inconsistencies in Internet protocols and implementations.

##########









# normalize all incoming traffic

scrub in on $ext_if all no-df









# Replace IP identification fields with random values.

# Makes it more difficult to count costs hidden behind a NAT box.

scrub out on $ext_if all random-id









##########

# Queueing - Queueing provides rule-based bandwidth control.

##########









##########

# Translation (Various forms of NAT) - Translation rules specify how addresses are to be mapped or redirected to other addresses.

##########









# general nat rule

nat on $ext_if from $int_if:network to any -> ($ext_if)









# redirect email_web_server services to email_web_server

rdr on $ext_if proto tcp to any port $email_web_server_services -> $email_web_server_ip









# DENY rouge redirection

no rdr









##########

# Packet Filtering - Packet filtering provides rule-based blocking or passing of packets.

##########









# block and log everything by default

block log all









# spoofed address protection for external interface

antispoof for $ext_if









# spoofed address protection for internal interface

antispoof for $int_if









# block anything coming from source we have no back routes for

block quick from no-route









# block packets whose ingress interface does not match the one in

# the route back to their source address

block quick from urpf-failed









# silently drop broadcasts (ISP noise)

block in log quick on $ext_if to 255.255.255.255









# silently drop IGMP (ISP noise)

block in quick on $ext_if proto IGMP from xxx.xxx.xxx.xxx to 224.0.0.1









# block and log incoming packets from emerging threats

block in log quick from <emerging_block_ips>









# block and log incoming packets from spoofed or misconfigured addresses

block in log quick on $ext_if from <blocked_net_addr>









# block and log incoming packets from hosts trying to brute force attack - not yet implemented

# block in log quick from <bruteforce>









# Do not allow Windows 9x SMTP connections since they are typically

# a viral worm. Alternately we could limit these OSes to 1 connection each.

block in log quick inet proto tcp from any os {"Windows 95", "Windows 98"} to any port smtp









# block and log outgoing packets that do not have our address as source,

# they are either spoofed or something is misconfigured (NAT disabled,

# for instance), we want to be nice and do not send out garbage.

block out log quick on $ext_if from ! $ext_ip









# once the traffic is permitted into an interface, we won't try to obstruct it leaving

# don't use modulate state, it messes up the firewall_dns_server's ability to connect to the internet

pass out quick on $ext_if









# allow local traffic

pass quick on $int_if









# open the tcp ports used by those network services that will be available to the Internet

pass in proto tcp to any port $firewall_dns_server_services









# open the tcp ports used by those network services that will be available to the Internet

pass in proto tcp to any port $email_web_server_services









# open the udp ports used by those network services that will be available to the Internet

pass in proto udp to any port $udp_services









# allow ICMP traffic

pass in inet proto icmp to any icmp-type $icmp_types