The impact of CARP and pfsync on Packet Filter rules is really minimal. First, you need to let the PFSYNC and CARP protocols pass on their own interfaces:
pass quick on rl2 proto pfsync keep state (no-sync) pass on { rl0, rl1 } proto carp keep state (no-sync)
Then, when writing firewall rules, keep in mind that, from pf(4)'s point of view, all traffic passes through the physical interface. Thus, in cases like:
pass in on $ext_if [...]
you can keep referring to the physical, not the virtual interface.
On the other hand, the virtual address is associated to the CARP interface; thus, you need to refer to it if the firewall offers any services on its virtual address:
# SSH on the virtual interface pass in on $int_if inet proto tcp from $int_if:network to carp0 port ssh
or on a NATed server, through traffic redirection:
# Mail server accessible from the internet pass in on $ext_if inet proto tcp from any to carp2 port $mail_ports rdr-to $mail_srv
In all other cases, CARP is perfectly transparent to pf(4), as for services offered by the firewall on its physical addresses:
# SSH on the physical address pass in on $int_if inet proto tcp from $int_if:network to $int_if port ssh
or for normal filtering:
# External DNS pass in on $int_if inet proto { tcp, udp } from $int_if:network to $dns_srv \ port domain pass out on $ext_if inet proto { tcp, udp } from $ext_if to $dns_srv \ port domain
As an example, let's see a basic PF ruleset for our external firewalls, Donald and Daisy:
################################################################################ # Macros and lists # ################################################################################ ext_if = rl0 # External interface int_if = rl1 # DMZ interface pfs_if = rl2 # Pfsync interface carp_if = carp1 # External CARP interface mail_srv = "mail.kernel-panic.it" # Mail server web_srv = "{ www1.kernel-panic.it, www2.kernel-panic.it }" # Web servers dns_srv = "{ dns1.isp.com, dns2.isp.com }" # DNS servers int_fw = "{ mickey.kernel-panic.it, minnie.kernel-panic.it }" # Internal fw mail_ports = "{ smtp, submission, imap, imaps }" # Mail server ports web_ports = "{ www, https }" # Web server ports # Allowed incoming ICMP types icmp_types = "{ echoreq, timex, paramprob, unreach code needfrag }" # Private networks (RFC 1918) priv_nets = "{ 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }" ################################################################################ # Options, scrub and NAT # ################################################################################ set block-policy drop set loginterface $ext_if set syncookies always set skip on lo # NAT outgoing connections match out on $ext_if from !$ext_if to any nat-to $ext_if # Redirect web services (with load balancing) match in on $ext_if inet proto tcp from any to $carp_if port $web_ports \ rdr-to $web_srv round-robin sticky-address # Redirect mail services match in on $ext_if inet proto tcp from any to $carp_if port $mail_ports \ rdr-to $mail_srv ################################################################################ # Filtering rules # ################################################################################ block all # Default deny block in quick from urpf-failed # Spoofed address protection # Scrub incoming packets match in all scrub (no-df) pass quick on $pfs_if proto pfsync keep state (no-sync) # Enable pfsync pass on { $int_if, $ext_if } proto carp keep state (no-sync) # Enable CARP block in quick on $ext_if from $priv_nets to any block out quick on $ext_if from any to $priv_nets # Mail server pass in on $ext_if inet proto tcp from any to $mail_srv port $mail_ports pass out on $int_if inet proto tcp from any to $mail_srv port $mail_ports pass in on $int_if inet proto tcp from $mail_srv to any port smtp pass out on $ext_if inet proto tcp from $ext_if to any port smtp modulate state # Web servers pass in on $ext_if inet proto tcp from any to $web_srv port $web_ports \ synproxy state pass out on $int_if inet proto tcp from any to $web_srv port $web_ports # ICMP pass in inet proto icmp all icmp-type $icmp_types pass out inet proto icmp all # DNS pass in on $int_if inet proto { tcp, udp } from $int_if:network to $dns_srv \ port domain pass out on $ext_if inet proto { tcp, udp } from $ext_if to $dns_srv \ port domain # Internet web servers pass in on $int_if inet proto tcp from $int_fw to any port $web_ports pass out on $ext_if inet proto tcp from $ext_if to any port $web_ports \ modulate state