Home gateway using OpenBSD and PF

January 2009


This is an annotated example of a home PF setup under OpenBSD (on a Soekris 5501.

###################
### Definitions ###
###################

# Of NICs
ctl_if="sis0"
wan_if="sis1"
intranet_if="sis2"
datanet_if="sis3"
dmz_if="vr0"
localhost_if="lo0"

# Of attached networks
wan=$wan_if:network
datanet=$datanet_if:network
intranet=$intranet_if:network
dmz=$dmz_if:network
localhost=$localhost_if:network

# Some groupings of NICs
local_ifs="{" $ctl_if $intranet_if $datanet_if $dmz_if "}"
dhcp_ifs="{" $intranet_if $datanet_if $dmz_if "}"
ssh_ifs="{" $intranet_if "}"

# Some groupings of ports
client_out_tcp = "{ ftp-data, ftp, http, https, imaps, ntp, pop3, smtp, smtps, ssh }"
client_out_udp = "{ domain, ntp, tftp }"
intranet_windows_ports = "{ microsoft-ds, netbios-ns, netbios-dgm, netbios-ssn }"

# Some settings for port redirections
lenin=10.0.12.200
hosts_allowed_ssh_to_lenin="{ 130.89.40.71 }"

##############
### Tables ###
##############

table <bruteforce> persist

###################################
### Normalization and rewriting ###
###################################

scrub in all

################
### Queueing ###
################

# Define a high-priority queue in addition to a default
altq on $wan_if priq bandwidth 1800Kb queue { q_pri, q_def }
queue q_pri priority 7
queue q_def priority 1 priq(default)


###########
### NAT ###
###########
# SNAT on datanet
nat on $wan_if from $datanet to any -> ($wan_if:0)
# SNAT on intranet too
nat on $wan_if from $intranet to any -> ($wan_if:0)

# Traffic destined for inside the network shouldn't be directed out of course
no nat on $wan_if from $intranet to $intranet
no nat on $wan_if from $intranet to no-route

# FTP and TFTP are proxied, not NATted
no nat on $wan_if to port { tftp, ftp }

# There are proxies running on these ports
rdr on $datanet_if   proto udp from $datanet  to any port tftp -> 127.0.0.1 port 6969
rdr on $intranet_if  proto udp from $intranet to any port tftp -> 127.0.0.1 port 6969
rdr on $datanet_if   proto tcp from $datanet  to any port ftp -> 127.0.0.1 port 8021
rdr on $intranet_if  proto tcp from $intranet to any port ftp -> 127.0.0.1 port 8021

# Redirect ssh incoming from a few permitted hosts to Lenin on intranet
rdr log on $wan_if proto tcp from $hosts_allowed_ssh_to_lenin to $wan_if:0 port ssh -> $lenin

# The anchors for the proxies to insert their own rules
nat-anchor "tftp-proxy/*"
rdr-anchor "tftp-proxy/*"
nat-anchor "ftp-proxy/*"
rdr-anchor "ftp-proxy/*"

#################
### Filtering ###
#################
# No need for localhost traffic to be checked
set skip on $localhost

# No funny packets are allowed to come in with outgoing addresses and the like
antispoof for $wan_if
antispoof for $datanet_if
antispoof for $localhost_if

# Block everything by default
block log all

# Misbehaving machines are blocked by IP
block quick from <bruteforce>

# We trust the loopback
pass log quick on $localhost_if

# Ssh is redirected but blocked unless we allow it in here
# Trial-and-error bruteforcers try about once a second, so will be caught
pass in on $wan_if inet proto tcp from $hosts_allowed_ssh_to_lenin to $lenin port ssh flags S/SA keep state \
(max-src-conn 40, max-src-conn-rate 5/10, overload <bruteforce> flush global)

# And we should allow it through on the inside interface as well
# Note that thsi and the previous rule can be removed if we attach the "pass" keyword to the redirect rule above
pass out on $intranet_if inet proto tcp from $hosts_allowed_ssh_to_lenin to $lenin port ssh


# Allow ping out (but don't allow it in from WAN)
pass out on $wan_if inet proto icmp all icmp-type echoreq queue ( q_def, q_pri )

# Allow some services to be used, but not all
pass out log on $wan_if inet proto tcp to any port $client_out_tcp queue ( q_def, q_pri )
pass out log on $wan_if inet proto udp to any port $client_out_udp queue ( q_def, q_pri )

# Don't allow ssh from intranet to the firewall
#pass in log on $intranet_if inet proto tcp from $intranet to 10.0.12.1 port ssh

# Allow some Windows protocols within intranet
pass log on $intranet_if proto tcp from $intranet to $intranet port $intranet_windows_ports

# Allow any contact within intranet
pass log on $intranet_if from $intranet to $intranet

anchor "tftp-proxy/*"
anchor "ftp-proxy/*"

################
### Comments ###
################