Site Navigation

Building an IP-less Bridging Firewall with OpenBSD

[2006-05-29] < Back

Recently at work I had a need to put a firewall in front of a few Win32 web servers. The number of IP addresses assigned to these servers made putting a NAT device in front of them a daunting task. This led me to create an ipless bridging firewall using OpenBSD. The bridge allows firewalling without having to change the IP addresses of your hosts, as it just passes the traffic along filtering according to your rules. This article details how I went about setting up the bridge I created at work.

Host Machine

I had an IBM x305 1u server I used for this project. It was nice that it had two NICs built in, so I didn't need to add any to the box. The specs on this box are overkill for it's purpose, with a 2.4Ghz Pentium 4 processor and 1GB of RAM. You could really get by with any Pentium series processor and 32MB of RAM, and that may even be overkill.

Installing OpenBSD

Installing OpenBSD is really beyond the scope of this article, but I do have a few tips. Make sure when you slice your disk you make the var and usr partitions a decent size. A few GB each should do the trick. When you choose installation sets, I would just accept the defaults. Answer no to running sshd, ntpd, installing X, and putting the default console on com0. When you are done with the installation, enter "halt" and reboot.

Configuring OpenBSD

When the system has finished rebooting, log in as root. The first thing we want to do is make sure all network services are shut down. Create an /etc/rc.conf.local file for this, and add the following lines.

sshd_flags=NO
portmap=NO
inetd=NO
pf=YES
pf_rules=/etc/pf.conf

Next edit root's crontab to disable the maintenance emails. Your crontab should look like the following one.

SHELL=/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin
HOME=/var/log
#
#minute	hour	mday	month	wday	command
#
*/10	*	*	*	*	/usr/libexec/atrun
#
# sendmail clientmqueue runner
*/30	*	*	*	*	/usr/sbin/sendmail -L sm-msp-queue -Ac -q
#
# rotate log files every hour, if necessary
0	*	*	*	*	/usr/bin/newsyslog
# send log file notifications, if necessary
#1-59	*	*	*	*	/usr/bin/newsyslog -m
#
# do daily/weekly/monthly maintenance
30	1	*	*	*	/bin/sh /etc/daily 1>/var/log/daily.out 2>&1
30	3	*	*	6	/bin/sh /etc/weekly 1>/var/log/weekly.out 2>&1
30	5	1	*	*	/bin/sh /etc/monthly 1>/var/log/monthly.out 2>&1

Next, use ifconfig to see what NICs you have. In my case I had two Broadcom NICs, which use the bge driver. The BSD operating systems use the driver to name the interfaces, so, as you can see below, my interfaces are bge0 and bge1.

# ifconfig -a
lo0: flags=8049 mtu 33224
        inet 127.0.0.1 netmask 0xff000000
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x5
lo1: flags=8008 mtu 33224
bge0: flags=8843 mtu 1500
        address: 00:0d:60:0f:35:cc
        media: Ethernet autoselect (100baseTX full-duplex)
        status: active
        inet 192.168.0.10 netmask 0xffffff00 broadcast 192.168.0.255
        inet6 fe80::204:acff:fedd:396a%bge0 prefixlen 64 scopeid 0x1
bge1: flags=8843 mtu 1500
        address: 00:0d:60:0f:35:cd
        media: Ethernet autoselect (100baseTX full-duplex)
        status: active
        inet 192.168.0.11 netmask 0xffffff00 broadcast 192.168.0.255
        inet6 fe80::204:acff:fedd:396b%bge1 prefixlen 64 scopeid 0x2
pflog0: flags=141 mtu 33224

Now we need to configure the interfaces and the brige interface. Since we are create an ipless bridge, we don't need to specify anything for the interfaces except to bring them up.

# echo up > /etc/hostname.bge0
# echo up > /etc/hostname.bge1
# cat > /etc/bridgename.bridge0
add bge0
add bge1
blocknonip bge0
blocknonip bge1
up
Ctrl-D

I have seen opposing examples on the net about whether or not the net.inet.ip.forwarding sysctl should be enabled. I enabled it just to be sure, as a few websites say it greatly improves performance. If you want to enable this, just find the following line in /etc/sysctl.conf and uncomment it.

net.inet.ip.forwarding=1
Configuring the Firewall

The only thing left to do now is configure pf, the OpenBSD Packet Filter. This is what is going to do our firewalling. Below is an example /etc/pf.conf file. You will want to edit your rules to fit your individual needs.

int_if = "bge1"
ext_if = "bge0"

localnet = "192.168.0.0/24"
trustednets = "{ 192.168.0.0/24, 192.168.1.0/24 }"


# we only want to filter one interface, so pass everything on the inside interface
pass in quick on $int_if all
pass out quick on $int_if all

# block everything by default on the external interface
block in log on $ext_if all
block out log on $ext_if all

# allow UDP DNS traffic
pass out log quick on $ext_if proto udp from $localnet to $trustednets port 53 keep state

# allow FTP, SSH, DNS and HTTP traffic to trusted networks
pass out log quick on $ext_if proto tcp from $localnet to $trustednets \
port { 20, 21, 22, 53, 80, 81, 443 } modulate state # allow incomming FTP, SSH, and HTTP traffic pass in log quick on $ext_if proto tcp from any to $localnet \
port { 20, 21, 22, 80, 81, 443 } modulate state # allow pings pass in log on $ext_if proto icmp from any to $localnet icmp-type 8 code 0 keep state pass out log on $ext_if proto icmp from $localnet to any icmp-type 8 code 0 keep state

These rules are fairly restrictive, and the comments should explain pretty well what each rule does.

Phyically Putting the Bridge in Place

Now that the all the software was configured, we need to actually put the bridge in it's place on the network. Here is how I did it.

 Internet Router
       |
Unprotected Switch
       |
  OpenBSD Bridge
       |
 Protected Switch
  |      |      |
Server Server Server
Troubleshooting

Most likely you will find something that does not work. In these instances you can use tcpdump to watch the pflog0 device and see what is being blocked and passed. This is the reason I put "log" on so many of my rules in the pf.conf file.

# tcpdump -enttti pflog0

If you want to limit the results to only packets coming from or to a specific host, you could do the following.

# tcpdump -enttti pflog0 host 192.168.0.25

You could also limit to specific ports on that host.

# tcpdump -enttti pflog0 host 192.168.0.25 and tcp port 80

Once you are done troubleshooting any problems you encounter, it would be a good idea to remove all the log keywords from the file. I would keep a copy of the pf.conf file with the logging enabled in case you need to debug something in the future. To load a config file while pf is running, you can use this command:

# pfctl -f /etc/pf.conf
Conclusion

Setting up an ipless bridging firewall with OpenBSD isn't an easy task, but isn't extremely difficult either. The hardest part is learning how pf works and creating a good rule set that strikes the right balance between security and usefulness.

< Back