Changes in pf: Packet Filtering
by Jacek Artymiak06/26/2003
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 packetsantispoof—a special case ofblockrulespass—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 |
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
or
block return-rst 100 quick in on $ext_if from any to $ext_ad port
The return-rst and return-icmp keywords
are optional.