Hi there,
I'm playing around with pf() and IPv6 on my VPS. I drop here my working /etc/pf.conf, so that everyone who is looking for a similar solution has a baseline to start (I found sparse examples with IPv6).
Some remarks about the assumptions I made for my configuration:
Of course, feel free to share your suggestions and improvements
Thank for your suggestions.
Best Regards,
Nicholas
I'm playing around with pf() and IPv6 on my VPS. I drop here my working /etc/pf.conf, so that everyone who is looking for a similar solution has a baseline to start (I found sparse examples with IPv6).
Some remarks about the assumptions I made for my configuration:
- the system has no jails;
- the public services are only ssh and a static web site (provided by nginx) with TSL enabled;
- ntp is used as client to keep the clock synced;
- the system is automatically provisioned, so I avoided to write fixed addresses in the rules.
freebsd-update
or pkg
.Of course, feel free to share your suggestions and improvements
Code:
# PF rules
#### MACRO SECTION
ext_if = "vtnet0" # external interface
int_if = "lo0" # internal interface
public_ip = "{" $ext_if "}" # this machine public IP (IPv4 and IPv6)
local_ip = $int_if:0 # home
# ssh: ssh connections
# www: the http services
# domain: DNS resolution
# ntp: the ntp daemon
in_tcp_services_restricted = "{ ssh }" # only for in rules (restrictive)
in_tcp_services = "{ http, https }" # only for in rules (non restrictive)
out_tcp_services = "{ ssh, http, https, domain }" # only for out rules
out_udp_services = "{ domain, ntp }" # only out rules
# only ping and traceroute ICMP messages allowed
# unreach allow to probe for MTU discovery
icmp_types = "{ echoreq, unreach }"
# specific ICMP for IPv6
icmp6_types = "{ echoreq, routersol, routeradv, neighbradv, neighbrsol }"
#### TABLE SECTION
# ssh bruteforce protection (table definition)
table <bruteforce> persist
# MARTIANS
# non-routables addresses as defined by stantards
table <martians> 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 255.255.255.255/32 \
::/128 ::1/128 ::ffff:0:0/96 ::/96 100::/64 2001:10::/28 2001:db8::/32 \
fc00::/7 fe80::/10 fec0::/10 ff00::/8 }
#### OPTIONS SECTION
# Enabling scrub provides a measure of protection against certain kinds of attacks
# based on incorrect handling of packet fragments
scrub in all
#### RULE SECTION
# block all traffic by default
# THIS MUST BE THE FIRST RULE
block log all
# The antispoof mechanism protects against activity from spoofed or forged IP
# addresses, mainly by blocking packets appearing on interfaces and in directions
# which are logically not possible.
antispoof log quick for $ext_if
antispoof log for $int_if # no quick, we need internal for ntp
# block non-routables addresses (quick: immediate effect, no further rules checked)
# only ext_if because we use "martians" in loopback interface (it's correct)
block drop in log quick on $ext_if from <martians> to any
block drop out log quick on $ext_if from any to <martians>
# block anything coming form source we have no back routes for
# block packets whose ingress interface does not match the one the route back to
# their source address
block in log quick from {no-route urpf-failed} to any
# rule to block bruteforce on all connections (both in and out)
# quick: if the rule apply, no other rules are checked!
block log quick from <bruteforce>
block log quick from <fail2ban>
# max-src-conn is the number of simultaneous connections allowed from one host.
# max-src-conn-rate is the rate of new connections allowed from any single host
# per number of seconds (4 connection every 30 seconds).
# overload <bruteforce> means that any host which exceeds these limits gets its
# address added to the bruteforce table. The ruleset blocks all traffic from
# addresses in the bruteforce table. Finally, flush global says that when a host
# reaches the limit, that all (global) of that host's connections will be terminated (flush).
#
pass in on $ext_if inet proto tcp from any to $public_ip port $in_tcp_services_restricted \
flags S/SA keep state \
(max-src-conn 8, max-src-conn-rate 4/30, \
overload <bruteforce> flush global)
pass in on $ext_if inet6 proto tcp from any to $public_ip port $in_tcp_services_restricted \
flags S/SA keep state \
(max-src-conn 8, max-src-conn-rate 4/30, \
overload <bruteforce> flush global)
# HTTP and HTTPS (no bruteforce check)
pass in on $ext_if inet proto tcp from any to $public_ip port $in_tcp_services keep state
pass in on $ext_if inet6 proto tcp from any to $public_ip port $in_tcp_services keep state
# allows connections created by this system to pass out, while retaining state
# information on those connections. This state information allows return traffic
# for those connections to pass back and should only be used on machines that can be trusted.
# we allow only the specified services
pass out on $ext_if inet proto tcp from $public_ip to any port $out_tcp_services keep state # out only
pass out on $ext_if inet6 proto tcp from $public_ip to any port $out_tcp_services keep state # out only
pass out on $ext_if inet proto udp from $public_ip to any port $out_udp_services keep state # out only
pass out on $ext_if inet6 proto udp from $public_ip to any port $out_udp_services keep state # out only
# allow ntpq for local interface
pass on $int_if inet proto udp from $local_ip to $local_ip port ntp
pass on $int_if inet6 proto udp from $local_ip to $local_ip port ntp
# ICMP
# allow only specified icmp_types (both in and out)
pass on $ext_if inet proto icmp all icmp-type $icmp_types keep state
pass on $ext_if inet6 proto icmp6 all icmp6-type $icmp6_types keep state
# allow out the default range for traceroute(8):
pass out on $ext_if inet proto udp from $public_ip to any port 33433 >< 33626 keep state # out only
pass out on $ext_if inet6 proto udp from $public_ip to any port 33433 >< 33626 keep state # out only
Thank for your suggestions.
Best Regards,
Nicholas