Packet Filtering with Lists and Macros

PF includes many ways to have one rule reference several similar items, or symbolically represent something with a variable. The basic ways are lists and macros.

Using Lists

A list is a way to represent several similar items in one rule. You might want to use a list if, for example, you want a particular group of TCP ports open on a certain group of hosts, and your rule entries would be repetitions of one rule with minor changes. Opening ports 80 and 443 to one host requires two rules: one for each port. If you have 30 web servers, you would need 60 rules. This is a pain to maintain and error-prone, but lists let you express these common elements more easily.

A list is represented in curly braces within a rule. To make the rule more readable, you can put a comma between items.

pass in on egress proto tcp from any to 192.0.2.12 port {80, 443}

This one pf.conf statement creates two rules, opening both TCP ports 80 and 443 to the target host.

pass in on egress from any to 192.0.2.12 port = 80 flags S/SA
pass in on egress from any to 192.0.2.12 port = 443 flags S/SA

You could also use a list to have this rule cover multiple web servers.

pass in on egress proto tcp from any to {192.0.2.12, 192.0.2.13} port {80, 443}

This expands to four rules: one for each combination of server and port.

Remember that each entry in the list creates its own rules. The list entries do not combine to create a single rule.

Using Macros

A macro is a variable that you create and define for use within PF rules. Macros keep pf.conf more readable, maintainable, and manageable.

Macro names must begin with a letter, but can include letters, numbers, and underscores. You cannot give a macro a name that’s used elsewhere in PF, like pass, block, or proto. Frequent uses of macros include interface names, network addresses, and ports.

Earlier, we saw a list that included the popular web ports 80 and 443. You could make these a macro, as follows:

web_ports="{80, 443}"

Our sample rule would then become this:

pass in on egress proto tcp from any to 192.0.2.12 port $web_ports

When combined with braces, macros can simplify your pf.conf file. Consider the following pf.conf snippet:

webservers="{192.0.2.12, 192.0.2.13, 192.0.2.14, 192.0.2.15}"
web_ports="{80, 443}"
pass in on egress proto tcp from any to $webservers port $web_ports

This expands to eight rules, but requires only three easy-to-understand configuration statements. When you add a new web server, add its IP address to the list in the webservers macro. What’s more, you might use the webservers macro in dozens of places throughout your rules. Changing the IP address list once is much easier and more likely to be correct than doing so in each rule.

While you probably use interface groups to represent IP addresses local to your machine, you might have other IP addresses that you need to represent. Macros are great for this, too.

internal_ip="10.10.0.0/16"

Or if you have multiple disparate blocks, you could use a list inside the macro.

internal_ip="{10.0.0.0/24, 10.0.5.0/24, 10.0.10.0/24}"

You don’t see macros or lists when viewing the running PF rules with pfctl; instead, you see the rules that they expand to.

A Common Error: List Exclusions and Negations

Lists can be counterintuitive, and it’s easy to write lists that negate other rules. For example, this seems like it should work:

clients = "{192.0.2.0/24, !192.0.2.128/29}"
pass in on egress from $clients

The idea here is that our clients have the IP addresses 192.0.2.0/24. We want to permit all of those addresses except for the small chunk in the middle, 192.0.2.128/29. That seems reasonable, right? But much like excluding commands from sudo(8), this breaks. Remember that each entry in a list expands into another rule. This creates two rules.

pass in on egress inet from 192.0.2.0/24 flags S/SA
pass in on egress inet from ! 192.0.2.128/29 flags S/SA

The first rule passes in everything from the 192.0.2.0/24 subnet. That’s what we wanted. The second rule, however, passes in everything that’s not in the subnet 192.0.2.128/29, also known as “everyone in the world”—not what we were hoping to achieve.

Similarly, negating an entire list expands to negating each individual item in the list. If you need to do this sort of exclusion, use a table, as described in the next chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.111.179