Redundant OpenBSD Firewalls
Part 2 - Routing and Packet Filter
In this part, we will set up fw1 as a stand-alone firewall providing routing and packet filtering. While configuring, we will make some accommodations for Part 4 and Part 5, where we add fw2 to the mix.
First you’ll need to install OpenBSD on
fw1. Don’t forget to install any available binary system patches using
syspatch
.
Now we will configure fw1 so that it can perform routing, NAT, and packet filtering functions. Once we have everything working on a single host, we’ll begin the work of making everything redundant. All of the work for Part 1 will be done on fw1.
My ISP hands out IP addresses via DHCP. CARP was designed to work with static IP addresses. Part 3 of this guide exists to solve this problem, and it’s fairly complex. If your ISP gives static addresses, you can save yourself a bunch of configuration work.
Configure native network interfaces
First we need to set up our native network interfaces. My hardware has native
network interfaces em0
, em1
, and em2
. Yours may differ. I assigned
em0
as the internal network interface, and assigned em1
as the public
internet network interface, which is hooked up to my cable modem.
Create the file /etc/hostname.em0
with these contents:
inet 192.168.13.4 255.255.255.0 192.168.13.255 description "internal interface"
And the file /etc/hostname.em1
with:
dhcp description "external interface"
Now start both interfaces:
fw1# sh /etc/netstart em0 em1
Configure gateway interface
The gateway IP address that all the hosts on the internal network will use to get to the internet is going to be a redundant, virtual IP address managed by CARP. CARP is enabled by default, but this ensures it’s enabled now, and at boot time:
fw1# sysctl net.inet.carp.allow=1
fw1# sysctl net.inet.carp.preempt=1
fw1# echo 'net.inet.carp.allow=1' >> /etc/sysctl.conf
fw1# echo 'net.inet.carp.preempt=1' >> /etc/sysctl.conf
We also turn on preempt, which allows hosts with a better advbase and advskew
to preempt the master. CARP is enabled by default, but specifying these
settings has the benefit of serving as a reminder that CARP is in use when we
view the sysctl.conf
file in the future.
Next, we create a carp0
network interface and assign it the IP address of
192.168.13.1, which is the gateway address all the other hosts on our
internal network will use. Why are we bothering with a CARP interface when we
don’t have two pieces of hardware yet? Because this way we have a CARP
interface with the IP address of our default gateway. When we add our
redundant firewall, we won’t have to reconfigure, we can just add the second
host to the interface.
Put the following into /etc/hostname.carp0
:
inet 192.168.13.1 255.255.255.0 192.168.13.255 vhid 131 carpdev em0 pass vhid131passwd description "internal network gateway"
With the interface defined, we now apply the configuration with:
fw1# sh /etc/netstart carp0
In an effort to make things easier to remember, I have assigned the carp0
interface to the em0
interface. The CARP protocol uses a Virtual Host ID to
identify the redundancy group (or virtual host) to other nodes on the
network. Unfortunatly the Virtual Host ID doesn’t start at 0, it goes from 1
to 255. It would be super confusing to have the carp0
interface in group 1.
So I chose to set the group to 131, which is similar to the last two octets
of the IP address. Finally, the password needs to be the same on all hosts in
the redundancy group. In this example, I’ve chosen a password that makes it
clear that these passwords are shared with other nodes in the group.
Basic firewall and packet forwarding
With our network interfaces configured, we need to enable IP forwarding and network address translation.
fw1# sysctl net.inet.ip.forwarding=1
fw1# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf
Packet filtering is enabled by default, we just need to create some rules in
/etc/pf.conf
:
#
# /etc/pf.conf
#
# remember to set net.inet.ip.forwarding=1
int_if="em0"
# options
set block-policy return
set loginterface egress
set skip on lo
# turn on NAT
match out on egress from ($int_if:network) to any nat-to (egress:0)
#
# filter rules
block in log
pass out quick
antispoof quick for { $int_if }
# pfsync and carp
pass on { $int_if } proto carp keep state (no-sync)
# allow traffic from the internal network
pass in quick on { $int_if } all
# allow icmp traffic
pass in inet proto icmp all
This basic configuration blocks inbound traffic from the internet, and passes outbound traffic while doing Network Address Translation. We also explicitly handle the carp protocol. I think good network citizens should allow ICMP traffic, so that’s enabled too.
Apply these new rules with:
fw1# pfctl -f /etc/pf.conf
That wraps up part 1, we have a basic firewall which will route traffic. In part 2 we’ll set up some additional network services like DNS, DHCP, and NTP.