Small server IPv4/IPv6 pf.conf

I’m deploying IPv6 for my employer. While getting corporate servers up on IPv6 is nice and all, of course I put priority on my own personal Web server.

Just because IPv6 is still populated mostly by early adopters, doesn’t mean we can neglect basic system security. That means that the server needs a packet filter for both IPv4 and IPv6. PF supports filtering both protocols in one ruleset.

The following is a unified IPv4/IPv6 PF ruleset for a small server. It:

  • Allows all traffic from management addresses
  • Allows all ICMP and ICMPv6 traffic
  • Allows traffic to specific ports where we choose to provide service — in this case, SMTP, DNS, and HTTP.
  • Blocks everything else.

    Fill in the IPv4 and IPv6 external addresses, define your interface, adjust the permitted services to match your environment, and you’re ready to go.

    ext_if="em0"
    ext_addr="{192.0.2.40, 192.0.2.41}"
    ext_v6="2001:db8:0:12::2"

    table <mgmt_hosts> const {172.16.0.0/24, 172.16.5.0/24}
    table <v6_mgmt_hosts> const {2001:db8:1:4::2}

    set block-policy return
    set loginterface $ext_if
    set skip on lo0

    scrub in all no-df

    block in all

    pass in on $ext_if proto icmp all
    pass in on $ext_if proto icmp6 all

    pass in on $ext_if inet from <mgmt_hosts>
    pass in on $ext_if inet6 from <v6_mgmt_hosts>

    pass out on $ext_if inet from $ext_addr to any
    pass out on $ext_if inet6 from $ext_v6 to any

    #services we permit
    pass in on $ext_if proto tcp from any to $ext_addr port {25,53,80}
    pass in on $ext_if proto udp from any to $ext_addr port 53
    pass in on $ext_if inet6 proto tcp from any to $ext_v6 port {53,80}
    pass in on $ext_if inet6 proto udp from any to $ext_v6 port 53