A number of of my favorite issues in regards to the OpenBSD Packet Filter instruments
The OpenBSD packet filter (PF) was launched a little bit greater than 20 years in the past as a part of OpenBSD 3.0. In a collection of two posts, I invite you to take a brief tour of PF options and instruments that I’ve loved utilizing.
On the time the OpenBSD challenge launched its new packet filter subsystem in 2001, I used to be nowhere close to the primarily full-time OpenBSD person I might quickly develop into. I did, nevertheless, rapidly acknowledge that even what was later dubbed ’the working prototype’ was reported to carry out higher in most contexts than the code it changed.
The explanation PF’s predecessor wanted to get replaced has been coated extensively on my own and others elsewhere, so I’ll restrict myself to noting that the rationale was that a number of somebodies lastly learn and understood the code’s license and determined that it was not, in actual fact, open supply in any acceptable which means of the time period.
Anyway, the preliminary PF launch was very shut in options and syntax to the code it changed. And even at the moment, the config syntax was much more human-readable than the choice I had been dealing with as much as then, which was Linux’s IPtables. The much less stated about IPtables, the higher.
However quickly seen enhancements in user-friendliness, or not less than admin friendliness, began showing. With OpenBSD 3.2, the separate /etc/nat.conf Community Tackle Translation (NAT) configuration file moved to the attic and the NAT and redirection choices moved into the principle PF config file /etc/pf.conf.
The following model, OpenBSD 3.3, noticed the ALTQ queueing configuration transfer into pf.conf as effectively, and the beforehand separate altq.conf file turned out of date. What didn’t change, nevertheless, was the syntax, which was to stay simply bothersome sufficient that many people postpone enjoying with visitors shaping till some years later. Different PF information in that launch included anchors, or named sub-rulesets, in addition to tables, described as “a really environment friendly approach for giant deal with lists in guidelines”, and the preliminary launch of spamd(8), the spam deferral daemon.
Extra on this stuff later; I cannot bore you with an in depth historical past of PF options launched or modified in OpenBSD during the last twenty-some years.
PF rulesets: The fundamentals
So how can we go about writing that excellent firewall config?
I may go on about that at size, and I’ve been recognized to every so often, however allow us to begin with the only doable, but completely safe PF ruleset:
block
With that in place, you might be completely safe. No visitors will cross.
Or as they are saying within the commerce, you’ve gotten just about unplugged your self from the remainder of the world.
That exact ruleset will develop to the next:
block drop all
However we’re getting forward of ourselves.
To give you a number of instruments and a few context, these are the fundamental constructing blocks of a PF rule:
verb standards motion … choices
Listed here are a number of pattern guidelines to place it into context, all lifted from configurations I’ve put into manufacturing:
cross in on egress proto tcp to egress port ssh
This primary pattern says that if a packet arrives on the egress — an interface belonging to the group of interfaces that has a default route — and that packet is a TCP packet with a vacation spot service ssh, let the packet cross to the interfaces belonging to the egress interface group.
Sure, if you write PF rulesets, you don’t essentially want to jot down port numbers for companies and memorize what companies conceal behind port 80, 53 or 443. The widespread or customary companies are recognized to the foundations parsing a part of pfctl(8), typically, and with the service names, you possibly can look these up within the /etc/services file.
The interface teams idea is, so far as I do know, an OpenBSD innovation. You possibly can put interfaces into logical teams and reference the group identify in PF configurations. A number of default interface teams exist with out you doing something; egress is one, and one other widespread one is WLAN the place all configured Wi-Fi interfaces are members by default. Understand that you possibly can create your individual interface teams — set them up utilizing ifconfig(8).
match out on egress nat-to egress
This one matches outbound visitors, once more on egress (which within the easier circumstances consists of 1 interface) and applies the nat-to motion on the packets, reworking them in order that the subsequent hops all the way in which to the vacation spot will see packets the place the supply deal with is the same as the egress interface’s deal with. In case your community runs IPv4 and you’ve got just one routable deal with assigned, you’ll greater than probably have one thing like this configured in your Web-facing gateway.
It’s value noting that early PF variations didn’t have the matching verb. After a number of years of PF follow, builders and practitioners alike noticed the necessity for a technique to apply actions resembling nat-to or different transformations with out making a choice on whether or not to cross or block the visitors. The match key phrase arrived in OpenBSD 4.6, and looking back, looks like a prelude to extra intensive modifications that adopted over the subsequent few releases.
Subsequent up is a variation on the preliminary completely safe ruleset.
block all
I’ll inform you now so you’ll not be stunned later — when you had made a configuration with these three guidelines in that order, your configuration could be functionally the identical because the one-word one we began with. It is because, in PF configurations, the foundations are evaluated from prime to backside, and the final matching rule wins.
The one escape from this development is to insert a fast modifier after the verb, as in:
cross fast from (self)
It will cease analysis when a packet matches the factors within the fast rule. Please use this sparingly, if in any respect.
There’s a particular purpose why PF behaves like this. The system that PF changed in OpenBSD had the highest to backside, last-match wins logic, and the builders didn’t need to break present configurations too badly through the transition away from the outdated system.
So, in follow, you’d put them on this order for a extra practical setup, however probably supplemented by a number of different gadgets.
block all
match out on egress nat-to egress
cross in on egress proto tcp to egress port ssh
For these supplementing gadgets, we will study among the PF options that may make it easier to write readable and maintainable rulesets. And whereas a readable ruleset isn’t routinely a safer one, readability definitely helps spot errors in your logic that might put the methods and customers in your care in attain of potential threats.
To assist that readability, you will need to pay attention to these options:
Choices: Basic configuration choices that set the parameters for the ruleset, resembling
set restrict states 100000
set debug debug
set loginterface dc0
set timeout tcp.first 120
set timeout tcp.established 86400
set timeout { adaptive.begin 6000, adaptive.finish 12000 }
If the which means of a few of these doesn’t appear terribly apparent to you at this level, that’s high-quality. They’re all extensively documented within the pf.conf man web page.
Macros: Content material that can develop in place, resembling lists of companies, interface names or different gadgets you are feeling helpful. Beneath are some examples together with guidelines that use them:
ext_if = "kue0"
all_ifs = "{" $ext_if lo0 "}"
cross out on $ext_if from any to any
cross in on $ext_if proto tcp from any to any port 25
Understand that in case your macros develop to lists of both ports or IP addresses, the macro enlargement will create a number of guidelines to cowl your definitions within the ruleset that’s finally loaded.
Tables: Information constructions which might be particularly designed to retailer IP addresses and networks. There have been initially devised to be a extra environment friendly technique to retailer IP addresses than macros that contained IP addresses and expanded to a number of guidelines that wanted to be evaluated individually. Guidelines can seek advice from tables so the rule will match any member of the desk.
desk <badhosts> persist counters file "/residence/peter/badhosts"
# ...
block from <badhosts>
Right here the desk is loaded from a file. You can too initialize a desk in pf.conf itself, and you may even manipulate desk contents from the command line with out reloading the foundations:
$ doas pfctl -t badhosts -T add 192.0.2.11 2001:db8::lifeless:beef:baad:f00d
As well as, a number of of the daemons within the OpenBSD base system resembling spamd, bgpd and dhcpd might be set as much as work together along with your PF guidelines.
Guidelines: The foundations with the verbs, standards and actions that decide how your system handles community visitors.
A quite simple and affordable baseline is one which blocks all incoming visitors however permits all visitors initiated on the native system:
block
cross from (self)
The cross rule lets our visitors cross to elsewhere, and since PF is a stateful firewall by default, return visitors for the connections the native system sends out might be allowed again.
You in all probability observed the configuration right here references one thing referred to as (self).
The string self is a default macro that expands to all configured native interfaces on the host. Right here, self is ready inside parentheses () indicating that a number of of the interfaces in self might have dynamically allotted addresses and that PF will detect any modifications within the configured interface IP addresses.
This actual ruleset expanded to this on my laptop computer in my residence community at one level:
$ doas pfctl -vnf /and many others/pf.conf
block drop all
cross inet6 from ::1 to any flags S/SA
cross on lo0 inet6 from fe80::1 to any flags S/SA
cross on iwm0 inet6 from fe80::a2a8:cdff:fe63:abb9 to any flags S/SA
cross inet6 from 2001:470:28:658:a2a8:cdff:fe63:abb9 to any flags S/SA
cross inet6 from 2001:470:28:658:8c43:4c81:e110:9d83 to any flags S/SA
cross inet from 127.0.0.1 to any flags S/SA
cross inet from 192.168.103.126 to any flags S/SA
The pfctl command right here says to verbosely parse however do not load guidelines from the file /etc/pf.conf.
This reveals what the loaded ruleset might be, after any macro expansions or optimizations.
For that actual purpose, it’s strongly advisable to evaluate the output of the pfctl -vnf command on configurations you write earlier than loading it as your working configuration.
Should you look carefully at that command output, you will note each the inet and inet6 key phrases. These designate IPv4 and IPv6 addresses respectively. Because the earliest days, PF has supported each, and if you don’t specify which deal with household your rule applies to, it’ll apply to each.
However this has all been on a boring single-host configuration. In my expertise, the extra fascinating setting for PF use is when the configuration is for a bunch that handles visitors for different hosts, resembling a gateway or different intermediate host.
To ahead visitors to and from different hosts, it’s essential to allow forwarding. You are able to do that from the command line:
# sysctl web.inet.ip.forwarding=1
# sysctl web.inet6.ip6.forwarding=1
However it would be best to make the change everlasting by placing the next traces in your /etc/sysctl.conf so the change survives reboots.
web.inet.ip.forwarding=1
web.inet6.ip6.forwarding=1
With these settings in place, a configuration (/etc/pf.conf) like this may make sense in case your system has two community interfaces which might be each of the bge type:
ext_if=bge0
int_if=bge1
client_out = "{ ftp-data ftp ssh area pop3, imaps nntp https }"
udp_services = "{ area ntp }"
icmp_types = "echoreq unreach"
match out on egress inet nat-to ($ext_if)
block
cross inet proto icmp all icmp-type $icmp_types hold state
cross fast proto { tcp, udp } to port $udp_services hold state
cross proto tcp from $int_if:community to port $client_out
cross proto tcp to self port ssh
Your community probably differs in a number of methods from this instance. I’ll put some references on the finish of Half 2 for a extra thorough therapy of all these choices.
And as soon as once more, please use the readability options of the PF syntax to maintain you sane and secure.
NOTE: If you’re extra of a slides individual, the abstract for the SEMIBUG person group assembly is available. A model with out trackers however ‘classical’ formatting can be available.
Peter N. M. Hansteen is a puffyist, daemon charmer, and penguin wrangler.
Tailored from authentic put up which appeared on BSDLY.
The views expressed by the authors of this weblog are their very own
and don’t essentially replicate the views of APNIC. Please observe a Code of Conduct applies to this weblog.