Sample PF for Testing
NOT DONE DO NOT USE
NOTE: This guide is no substitute for reading the Packet Filter Guide. In particular, you must read the Basic Configuration section and the NetAdmin Code.
Here's a sample /etc/pf.conf for testing servers (do NOT use this for stable, znc-only servers). See the Comments section below for a rule-by-rule explanation:
ExtIf = "vio0" IP4 = "10.0.0.1" IntIP4 = "192.168.0.1" IP6 = "2001:db8::/80" FlushUDP = "max-pkt-rate 10000/10 keep state (max 1000, source-track rule, max-src-nodes 200, max-src-states 200)" Flush = "keep state (max 1000, source-track rule, max-src-nodes 200, max-src-conn-rate 500/10 overload <badhosts> flush global)" FlushStrict = "keep state (max 100, source-track rule, max-src-nodes 20, max-src-conn-rate 50/10 overload <badhosts> flush global)" set skip on lo0 set loginterface $ExtIf #set ruleset-optimization profile set syncookies adaptive (start 25%, end 12%) table <ilines> persist file "/etc/pf/ilines" table <badhosts> persist file "/etc/pf/badhosts" # udp and icmp block in log quick from <badhosts> pass in log quick proto udp to {$IP4 $IP6} port domain $FlushUDP pass in log quick proto udp to {$IntIP4 $IP6} port ntp $FlushUDP pass in log quick proto udp to {$IP4 $IP6} port {isakmp ipsec-nat-t} $FlushUDP block in log quick proto udp to {$IP4 $IP6} block in log quick from urpf-failed match in log all scrub (no-df random-id max-mss 1440) pass in log quick on $ExtIf inet proto icmp icmp-type 8 code 0 $FlushUDP # icmp packets pass in log quick on $ExtIf inet proto icmp icmp-type 3 code 4 $FlushUDP # icmp needfrag (MTU) pass in log quick on $ExtIf proto ipv6-icmp $FlushUDP # tcp pass in log quick proto tcp to {$IP4 $IP6} port domain $Flush pass in log quick proto tcp to {$IP4 $IP6} port auth $Flush pass in log quick proto tcp to {$IP4 $IP6} port {smtp submission smtps imap imaps pop3 pop3s} $Flush pass in log quick proto tcp to {$IP4 $IP6} port {gopher http https} $Flush pass in log quick proto tcp from <ilines> to {$IP4 $IP6} port { 6660:6669 6697 6997 7000 9999 16667 16697 } #irc pass in log quick proto tcp to {$IP4 $IP6} port { 6660:6669 6697 6997 7000 9999 16667 16697 } $Flush #irc pass in log quick proto tcp to {$IP4 $IP6} port { 1314 13140 1337 31337 } $Flush #bnc pass in log quick proto tcp to {$IP4 $IP6} port 29173 $Flush #wraith pass in log quick proto tcp to {$IP4 $IntIP4 $IP6} port ssh $FlushStrict # road warrior vpn pass in log inet proto udp to {$IP4 $IP6} port {isakmp, ipsec-nat-t} tag IKED pass in log inet proto esp to {$IP4 $IP6} tag IKED pass log on enc0 inet tagged ROADW match out log on $ExtIf inet tagged ROADW nat-to $IP4 match in log quick on enc0 inet proto { tcp, udp } to port 53 rdr-to 127.0.0.1 port 53 block in log all block out log on $IntIP4 pass out quick from {$IP4 $IP6} # allow non-spoofed packets pass out quick proto tcp from $IntIP4 to port ssh pass out quick proto udp from $IntIP4 to port ntp pass out quick proto {udp tcp} from $IntIP4 to port {domain} pass out quick inet proto icmp from $IntIP4 # allow ICMP
You will then need to create a folder:
$ doas mkdir /etc/pf/
Then, add the list of ilines to /etc/pf/ilines.
198.251.89.130 198.251.83.183 209.141.39.184 209.141.39.228 198.251.84.240 198.251.80.229 198.251.81.119 209.141.39.173 198.251.89.91 198.251.81.44 209.141.38.137 198.251.81.133 2605:6400:0030:f8de::/64 2605:6400:0010:071b::/64 2605:6400:0020:0434::/64 2605:6400:0020:00b4::/64 2605:6400:0010:05bf::/64 2605:6400:0030:fc15::/64 2605:6400:0020:1290::/64 2605:6400:0020:0bb8::/64 2605:6400:0030:faa1::/64 2605:6400:0010:069d::/64 2605:6400:0020:05cc::/64 2605:6400:0010:00fe::/64
Afterwards, any badhosts can be added to /etc/pf/badhosts.
To load the new configuration:
$ doas pfctl -f /etc/pf.conf
Troubleshooting
WARNING: When you apply new firewall rules, make sure to test that all services are working after the rules have been applied. If you do not test, you might break something for users and not notice it for days or weeks!
To test, connect to each and every one of the services you provide, both from your home IP address and another proxy (vpn, vps) that you have.
Please also set aside 24-48 hours to monitor any bug reports from users.
Comments
ExtIf = "vio0" IP4 = "10.0.0.1" IntIP4 = "192.168.0.1" IP6 = "2001:db8::/80"
ExtIf is the external interface (vio0 for BuyVM and VMM? users), IP4 is your DDoS-filtered IPv4 address, IntIP4 is your secret unfiltered IPv4 address, and IP6 is your IPv6 subnet range.
FlushUDP = "max-pkt-rate 10000/10 keep state (max 1000, source-track rule, max-src-nodes 200, max-src-states 200)" Flush = "keep state (max 1000, source-track rule, max-src-nodes 200, max-src-conn-rate 500/10 overload <badhosts> flush global)" FlushStrict = "keep state (max 100, source-track rule, max-src-nodes 20, max-src-conn-rate 50/10 overload <badhosts> flush global)"
This defines 3 macros.
For FlushUDP, if the packet rate exceeds 10000 packets per 10 seconds, PF will refuse to process any further packets. It will keep track of state for ICMP and UDP packets; if there are more than 1000 state entries, it will stop accepting new packets. If there are more than 200 unique IPs in the state entry table, or if a single IP has more than 200 entries, it will stop accepting new connections.
For Flush, if there are 1000 state entries, it will stop accepting new connections. If there are more than 200 unique IPs in the state entry table, or if a single IP makes more than 500 connections in 10 seconds, it will disconnect all connections from this user and add them to the table badhosts.
FlushStrict is the same but more strict. If there are 100 state entries, it will stop accepting new connections. If there are more than 20 unique IPs in the state entry table, or if a single IP makes more than 50 connections in 10 seconds, it will disconnect all connections from this user and add them to the table badhosts.
set skip on lo0 set loginterface $ExtIf #set ruleset-optimization profile set syncookies adaptive (start 25%, end 12%)
We skip filtering on loopback (localhost). We are going to log all packets that pass through the external interface vio0. You can view these using tcpdump in /var/log/pflog*. You can optionally optimize the ruleset based on the profile, but I have not yet tested to see if the optimization is intelligent, so I left it commented out. We will use syncookies to defend against synflood? attacks.
table <ilines> persist file "/etc/pf/ilines" table <badhosts> persist file "/etc/pf/badhosts"
We load two tables, one with ilines (with IRCNow-approved IPs), and another with a list of badhosts (known criminals and enemies).
block in log quick from <badhosts> pass in log quick proto udp to {$IP4 $IP6} port domain $FlushUDP pass in log quick proto udp to {$IntIP4 $IP6} port ntp $FlushUDP pass in log quick proto udp to {$IP4 $IP6} port {isakmp ipsec-nat-t} $FlushUDP block in log quick proto udp to {$IP4 $IP6} block in log quick from urpf-failed match in log all scrub (no-df random-id max-mss 1440)
We immediately block all packets from badhosts. We pass in all DNS UDP packets on the DDoS-filtered IPv4 and IPv6 subnet (but not the secret unfiltered IPv4 address). We pass in all NTP UDP packets for the unfiltered IPv4 address and IPv6 subnet, but not the DDoS-filtered IPv4 address because NTP packets get mangled by DDoS-filtering.
WARNING: Please follow the ntpd guide to set it up properly -- if you do not, your system's time will be wrong, causing all sorts of hard to troubleshoot problems like issues with nsd.
pass in log quick on $ExtIf inet proto icmp icmp-type 8 code 0 $FlushUDP # icmp packets pass in log quick on $ExtIf inet proto icmp icmp-type 3 code 4 $FlushUDP # icmp needfrag (MTU) pass in log quick on $ExtIf proto ipv6-icmp $FlushUDP
We allow in ICMP and ICMPv6 packets passing through our external interface. NOTE: Do not block ICMP packets, or else strange and hard to diagnose problems can occur. For example, blocking ICMPv6 packets can interfere with proper IPv6 routing.
pass in log quick proto tcp to {$IP4 $IP6} port domain $Flush pass in log quick proto tcp to {$IP4 $IP6} port auth $Flush pass in log quick proto tcp to {$IP4 $IP6} port {smtp submission smtps imap imaps pop3 pop3s} $Flush pass in log quick proto tcp to {$IP4 $IP6} port {gopher http https} $Flush pass in log quick proto tcp from <ilines> to {$IP4 $IP6} port { 6660:6669 6697 6997 7000 9999 16667 16697 } #irc pass in log quick proto tcp to {$IP4 $IP6} port { 6660:6669 6697 6997 7000 9999 16667 16697 } $Flush #irc pass in log quick proto tcp to {$IP4 $IP6} port { 1314 13140 1337 31337 } $Flush #bnc pass in log quick proto tcp to {$IP4 $IP6} port 29173 $Flush #wraith pass in log quick proto tcp to {$IP4 $IntIP4 $IP6} port ssh $FlushStrict
We immediately pass in all TCP packets for the public IPv4 address and IPv6 subnet if it's for DNS (domain), ident (auth), sending mail (smtp submission smtps), reading mail (imap imaps pop3 pop3s), gopher, the web (http https).
If the sender is present on our ilines, we pass in all IRC traffic without normal Flush limits. If not, we have normal Flush limits.
We immediately pass in all TCP packets for the public IPv4 address and IPv6 subnet if it's headed for the bouncer or wraith.
For ssh, we allow incoming packets to the secret, unfiltered IPv4 address and we apply more strict rules to prevent bruteforce attacks. The unfiltered IPv4 address will provide a hidden backdoor to access the server in case of a DDoS attack.
# road warrior vpn pass in log inet proto udp to {$IP4 $IP6} port {isakmp, ipsec-nat-t} tag IKED pass in log inet proto esp to {$IP4 $IP6} tag IKED pass log on enc0 inet tagged ROADW match out log on $ExtIf inet tagged ROADW nat-to $IP4 match in log quick on enc0 inet proto { tcp, udp } to port 53 rdr-to 127.0.0.1 port 53
This section is for IPSec VPNs using iked.
block in log all block out log on $IntIP4 pass out quick from {$IP4 $IP6} # allow non-spoofed packets pass out quick proto tcp from $IntIP4 to port ssh pass out quick proto udp from $IntIP4 to port ntp pass out quick proto {udp tcp} from $IntIP4 to port {domain} pass out quick inet proto icmp from $IntIP4 # allow ICMP
We block all incoming packets but not immediately. By default, if the quick keyword is missing, the last rule that matches applies to a packet. We then block all outgoing packets from the secret, unfiltered IPv4 address except whitelisted traffic for ssh, ntp, dns, and ICMP packets.
See Also
PF Guide | DDoS Filtering Guide | tcpdump |