network, security

pf, OpenBSD’s [p]acket [f]ilter (1)

When anyone has asked me in the last two years about installing a firewall at his LAN’s border, I have always recommended them OpenBSD‘s packet filter pf. I discovered OpenBSD a couple of years ago while designing a hall of residence‘s local network and firewall. I was by then quite tired of Linux’s netfilter/iptables and the first time I read about pf, I fell in love with it.

We are now more accustomed to this, but when I saw that you could write firewall rules like

pass in on $ext_if proto tcp from any port 80 to $ext_if

I knew I had found my firewall. No more -j or -l or whatever. Just names, words and eventually numbers.

And, yes, I know there are wrappers in Linux doing the very same thing. But guess what? I just do not like wrappers for this kind of job. Moreover, after you install OpenBSD for the first time and realize you have done it in about 2 minutes with no stupid questions (that is, à la slackware) and have a complete, working, no-hassle, simple operating system just ready to filter your network traffic… You simply never leave it. I wonder how I had not found it before.

To keep this first post simple, just assume you are for whatever strange reason running a web server on an OpenBSD system which you want to be able to manage remotely via ssh.

From the firewall point of view, you just want to block all incoming traffic except to ports 80 and 22, and to allow only outgoing packets which arise from external connection on those ports. Technically, you want to allow incoming connections and keep there state (which means all the traffic related to a previously allowed packet is allowed through).

Fortunately, pf keeps state by default. The pf.conf file describing the above set up might perfectly look like:

# pf configuration file for an ssh managed web server
ext_if="vr0"
ext_ip=1.2.3.4
allowed={http, ssh}

block all
pass in on $ext_if proto tcp from any to $ext_ip port $allowed

And that is all. Let me explain:

  • The assignments create macros. Macros are substituted pretty much like shell variables (a=’hi’, $a is ‘hi’).
  • Any collection of identifiers, macros or lists between curly brackets is a list (recursive definition). As can be seen, lists can be assigned to macros.
  • Rules are applied in a last match hierarchy. Whenever a packet arrives, pf keeps reading rules and applies to the packet the last matching one. The above block all and then pass means exactly: block by default (both incoming and outgoing packets) and let pass only what comes to ports either 80 or 22.
  • The fact that pf keeps state by deafult makes the firewall configured with the above file ‘let the TCP handshake and later packets’ go through into and out of the firewall, once a valid packet has passed through. One does not need to specify that ‘packets related to an allowed packet have to be let through’.
  • The use of $ before the names of macros is clear.

To enable the firewall,

# pfctl -e /etc/pf.conf

In the documentation you can find how to enable it at boot time.

I hope to delve somewhat more in forthcoming articles. As with all things OpenBSD the FAQ is a good starting point.

1 Comment

speak up

Add your comment below, or trackback from your own site.

Subscribe to these comments.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*Required Fields