PF A baseline configuration for a web server with IPv6 and TSL\SSL

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:
  • 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.
Furthermore, the provisioned system is already updated with all the packages I need. In other words, is kinda of an "immutable server": no need to run 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
 
Back
Top