My OpenBSD firewall: pf + single ISP + multiple dynamic IPs

This is about a setting up an OpenBSD firewall, which retrieves multiple IP addresses from a single ISP, and then does “1:1 natting” or “binatting” on some of the external interfaces for certain hosts while using one interface to do “standard NAT” for the rest of the hosts. I did not find instructions on how to do exactly this, so once I have had it running for a while, I decided to share it. Hopefully you find it helpful. Thanks to other people who wrote blogs and e-mails which helped me solve some of the subproblems, the URLs of which I have already forgotten.

I am running a PC on my home network which is a dedicated firewall (including a DNS server, a DynDNS client, a DHCP server, etc.). I used to run NetBSD for years, and during the past few years I was running an OpenBSD-originated pf (Packet Filter) as the firewall code. However, I was thinking for quite some time of switching from NetBSD to OpenBSD, as the machine is primarily a firewall, and with OpenBSD I could have the latest and most likely better integrated pf, and probably even more secure OS anyway. I also wanted to see how OpenBSD differs from FreeBSD and NetBSD. So far, I have been happy.

Besides moving to OpenBSD, I updated my old 600 MHz VIA-based Mini-ITX machine to a “more standard” Intel Atom -based one. I also got four gigabit interfaces instead of two 100 Mbps on the old one. My ISP lets me have five IP addresses, but I only needed one as I run NAT. Sometimes, however, it would be good to have 1:1 mapping between an internal IP address and an external one, to get rid of port mapping of NAT. This would help e.g. playing network games with PS3 which use direct connections from peers — I could then dedicate an external address to the PS3 and do “1:1 port mapping”, yet still have the PS3 behind my firewall using an internal address only.

Besides writing out the PF configuration some patches were needed to support multiple default routes which are configured to the routing table by DHCP client. The solution is that the dhclient writes the addresses of default routers to pfs tables. The only drawback with this approach is that every time pf is restarted, dhclient needs to be restarted too to populate the tables, as the tables are flushed. But this happens so rarely elsewhere than during booting, that even I don’t care to seek for a solution to that one..

The most time consuming part was writing a proper pf.conf, so that all the rules are in correct order, and that all rules have correct options, so that all protocols work in and out, including FTP in active and passive mode. I will post my pf.conf here, with some inline comments (some of the IP addresses are changed to fake ones, however the logic is not broken). The configuration and patches have been used with OpenBSD versions 4.7 and 4.8. I have tested the configuration with Nessus and there were no security problems. So, feel free to copy them and modify to your needs.

Patch for /etc/rc
Patch for /sbin/dhclient.c
Patch for /sbin/dhclient-script


Leave a Reply