BSD DevCenter
oreilly.comSafari Books Online.Conferences.


Securing Small Networks with OpenBSD Changes in pf: Packet Filtering

by Jacek Artymiak

In previous installments of this series (NAT with pf and More NAT) we examined macros, options, scrub rules, and NAT rules. This time we'll look at packet filtering rules, which kick in after packets have been scrubbed and (optionally) redirected to another IP address or port with NAT rules.

Writing packet filtering rules

Once you get your scrub and NAT rules sorted, it's time to add some packet filtering rules to the mix. There are three kinds of rules:

  • block—block matching packets
  • antispoof—a special case of block rules
  • pass—let matching packets through

Packets are matched against filtering rules after scrubbing and NAT'ing have been done. When you design your ruleset and plan to use NAT or port/interface redirection, remember to design your filtering rules to match packets after NAT'ing and redirection, or you will waste a lot of time debugging the ruleset and scratching your head wondering what's wrong with your design and its implementation. We will return to this subject later in this article, but first we need to focus on the packet filtering grammar.

Related Reading

Essential System Administration
Tools and Techniques for Linux and Unix Administration
By Æleen Frisch

The anatomy of a filtering rule

Packet filtering rules are written using a specialized grammar, similar to that of NAT rules, but capable of describing much finer detail required to implement detailed filtering rules. The large number of keywords and their possible configurations make them a bit overwhelming for a beginner, but there is a method behind this madness. The following guide should make them easier to digest. I divided it into small sections each describing one part of a packet filtering rule, following the order in which they are described in pf.conf(5).

Hint: if you have trouble making sense of the syntax describing those rules in pf.conf(5), remember that the items listed in square brackets ([...]) are optional, while the items separated with a vertical bar (|) are alternative values. Items in double-quotes ("...") are meant to be typed literally, but without the double quotes.

Should a packet be blocked or passed (block, pass)?

The block or pass keywords tell pf(4) what to do with the packet that matches all conditions listed after either block or pass (we are leaving the antispoof keyword aside for a while, but think of it as a special case of block rules). These keywords are required and either of them must be used at the beginning of every filtering rule. To block all incoming and outgoing packets, use:

block in  all
block out all

The opposite would be pass rules that let all traffic in and out of the firewall:

pass in  all
pass out all

With such rules in place, all traffic can move freely in or out of the firewall. Both policies are too general for practical use, but they are handy for explaining the basics. As a general rule, the more conditions you list after pass or block the more specific the rule will be. Conversely the less conditions you use, the more general the rule will be. As you will see later on, I always advise that you start your packet filtering section with a set of block {in, out} all rules. It is safer to block all traffic first and only open those routes that are absolutely necessary later. Apart from being a safer way to write pf(4) rulesets, such approach greatly simplifies the ruleset, which is good, because simple rulesets are easier to debug.

Should you notify the sender (return-icmp, return-rst) after blocking a packet?

A plain block rule drops all matching packets without sending any kind of notification back to the host that tried to initiate the connection. Silently dropping all unwanted packets is good security practice, because the firewall does not have to waste its own resources on sending redundant information, and because "silent" firewalls are harder to scan and fingerprint. (Broadly speaking, scanning is the process of looking for open ports that the attacker could use to break into your firewall or network, while fingerprinting is the process of identifying the operating system or other software running on the scanned host.)

However, as with all general rules, there are exceptions. One of these is sending the ICMP destination-unreachable message to hosts trying to connect to port 113 (auth). It is quite safe to do so and is considered to be good net citizenship. Returning that message helps some services, such as sendmail, complete connections faster, without waiting for connections to port 113 to time out.

This can be achieved with a rule that begins with:

block in all
block return-icmp in on $ext_if from any to $ext_ad port auth quick
pass in on $ext_if from any to $ext_ad port smtp quick

You can add the ICMP message number or name after the return-icmp keyword, although it is optional and only required in special cases. For more information about ICMP, read RFC792 [Postel 1981]; if you want to learn how it works in practice, and how it is implemented, especially on BSD systems, consult [Stevens, Wright 1994].

Another possibility is to answer unwanted packets with the TCP RST message. This is achieved with the return-rst keyword, which can be followed by an integer number defining the TTL (time to live) value for the returned packet as in:

block return-rst in quick on $ext_if from any to $ext_ad


block return-rst 100 quick in on $ext_if from any to $ext_ad port

The return-rst and return-icmp keywords are optional.


Next Pagearrow

Sponsored by: