How to get generic interface names and IPs in OpenBSD PF
Over on Twitter, I had a grump that led to me learning some things:
Since OpenBSD Ethernet interface names are tied to the physical hardware, I wish OpenBSD pf.conf syntax had an abstract name that meant 'the interface with the default route, whatever that is'.
We have straightforward OpenBSD systems with a single active interface that get installed on various hardware, with various interface names, and I would really like to not have to change their pf.conf for every different piece of hardware they get put on (physical or virtual).
Thanks to @oclsc I've now learned about OpenBSD interface group names, and reading the manpage pointed me to the predefined 'egress' group name, which means 'all interfaces with the default route'. You can use 'egress:0' to mean the (first) egress IP address.
We're not savages, so of course we use pf.conf macros in our pf.confs:
server_if = "bnx0" server_ip = 192.168.100.100 [...] pass in on $server_if from any to $server_ip port = 22
But this still means that we have to define the name of the interface
and the server's IP once. When we have two OpenBSD machines that are
clones of each other, for example two OpenVPN servers for redundancy,
this historically means that we've had to have two copies of
that are supposed to differ only in server IP.
(Usually we put the two servers on identical hardware, so the interface names are the same. If we used sufficiently different hardware that the interface names changed, we'd have to vary that too.)
However, it turns out that you can get what I want and, for simple configurations, reduce this to a completely generic version. First and well documented in the manpage for pf.conf is that you can use an interface name in PF rules in place of an IP address (cf). If you do, it means all of the IP addresses associated with that interface. If you want to not accept aliases, you can add :0 on the end to get the first one (cf). Combined with macros, we can write:
server_ip = $server_if:0
Written this way, the address is looked up once when the PF ruleset
is loaded and then substituted in. If you dump the installed rules
pfctl -s rules', you'll see the actual IPs, and the resulting
rules are exactly the same as if you'd specified the IP directly.
To get generic names for interfaces, we need to use the name of
interface groups, which
are documented in the ifconfig
manpage. Conveniently there is a predefined group that does what I
want, the '
- The interfaces the default routes point to are members of the “egress” interface group.
(There can be more than one default route pointing out more than one interface, but in normal use on our servers there is exactly one interface with a default route.)
If you don't want to rely on where the default route is pointing
you can explicitly specify a custom group in
and then use it in a macro in the PF rules. I'm not sure how to
best write this in the file, but sort of following information from
the hostname.if(5) manpage,
I found that it worked to write it on two lines:
inet 192.168.100.100 0xffffff00 group net-sandbox
All of this sounds great but it has a tiny little drawback, which is that it makes your PF configuration a bit more magical. Explicitly writing out the interface name and IP may be annoying some of the time, but it's always extremely obvious what is going on. You don't have to try to remember what 'egress' or 'net-sandbox' means when used as an interface name (or an IP address); it's always right there. Also, you're absolutely guaranteed that your rules are matching only a single IP address or a single interface. With interface group names, you're relying on the rest of your configuration to insure that there is only ever one 'egress' or 'net-sandbox' interface, no matter what you do to the machine.
A related issue is that the meaning of 'egress', 'net-sandbox', and the
like can change between PF ruleset loads (and the associated IPs along
with them), without any direct changes to pf.conf. This means that you
can boot with the system in one setup, change it for some reason, do a
pfctl -f /etc/pf.conf' with an unchanged pf.conf, and wind up with
a different set of rules. In some environments this is a feature; in
others it is a drawback, or at least a potential unpleasant surprise.
(What triggered this otday was testing out a version of our OpenBSD VPN
servers on the current OpenBSD in a virtual machine. Of course I needed
pf.conf, but my virtual machine had both a different IP
address and a different interface than the current real servers.)