PF Reasonable gateway firewall configuration

After much tribulation, I was able to get my FreeBSD machine operating as a gateway router between my lan and ISP router. I used pf because I found a decent howto online - Building an OpenBSD/pf Firewall. The pain came when I tried typing the rules in - what a friggin' nightmare (insert lots of whining and complaining). Anyhow, I wound up with this set of rules and would like some feedback:

Code:
cat /etc/pf.conf
ext_if="em0"
int_if="ue0"
localnet=$int_if:network
icmp_types="{ echoreq, unreach }"
set skip on lo
scrub in
nat on $ext_if inet from !($ext_if) -> ($ext_if:0)
block all
pass from {self, $localnet} to any keep state
pass in quick inet proto icmp all icmp-type $icmp_types keep state

Goals:
Block all inbound traffic on the external interface
Allow all traffic on localhost and on the internal interface
NAT everything going out and coming back on the external interface
Allow ICMP echoreq and unreach so ping and traceroute work

All of this is IPv4. I'm not doing IPv6 yet.

Questions:
Does the ruleset look reasonable?
Does it meet the goals efficiently?
Any tweaks advised?
Is pf a solid choice for this or is ipfw preferred?
 
Does the ruleset look reasonable?
Very.
Does it meet the goals efficiently?
It does, yes.
Any tweaks advised?
Not really, you pretty much nailed it. Although you could add the interfaces to make it more specific:
Code:
pass in on $int_if from {$localnet} to any keep state
pass out on $ext_if  from {self} to any keep state
Note that you will need to create a rule for the traffic coming in on $int_if but also add a rule to allow the traffic out of $ext_if. Traffic passes both interfaces and thus you need a rule on both for traffic passing through the host. Also note that NAT happens before the rules are evaluated. So remember to use the translated addresses.

Is pf a solid choice for this or is ipfw preferred?
It's more a subjective personal choice actually. Try them both, see which one you like.
 
Code:
...
pass from {self, $localnet} to any keep state
pass in quick inet proto icmp all icmp-type $icmp_types keep state
...
Turns out keep state is added by default to pass rules. The only tweak you can do is modulate state for outgoing TCP if you want to be extra paranoid.

STATE MODULATION
Much of the security derived from TCP is attributable to how well the
initial sequence numbers (ISNs) are chosen. Some popular stack implemen-
tations choose very poor ISNs and thus are normally susceptible to ISN
prediction exploits. By applying a modulate state rule to a TCP connec-
tion, pf(4) will create a high quality random sequence number for each
connection endpoint.
 
Code:
pass in on $int_if from {$localnet} to any keep state
pass out on $ext_if  from {self} to any keep state

After adding these, my setup worked under all conditions (I had some issues with my devices connected via AP without it). Thanks.
 
Learn to use tcpdump(1). It's an invaluable tool to diagnose networking issues. Seeing the actual network packets makes it a lot easier to understand what's happening.
 
I used pf because I found a decent howto online - Building an OpenBSD/pf Firewall.

You are aware that this is a) nearly 10 years old and b) was written for OpenBSD? (and c) has been posted in a "linux library"...)

I can only recommend that you first try to get a basic understanding of how PF works, as it has some little quirks that WILL bite you sooner or later (e.g. "last match wins" for filter rules...). The pf.conf(5) manpage is a very good resource to get a first overview on how it works and what your toolset looks like. For a deeper dive I can highly recommend "The Book of PF", which is written for OpenBSD and FreeBSD PF variants and extensively shows the differences between those two variants e.g. by giving all examples in both flavours of the syntax.

FreeBSD is by default a very secure OS, but it can't and won't save you from a wide open firewall if you misconfigured it by using random rules you found somewhere but didn't understand what they do. And as SirDice already stated: learn how to use tcpdump - you will use it _extensively_ when testing and learning how your rulesets work or why they don't.
And as we already deal with the basics:
Use some form of version control for your PF configuration. This way you can easily roll back or look up *why* you've added or changed some rules 5 months ago (you write proper commit messages, don't you?).
I keep my pf.conf along with other PF related files (e.g. tables) in /etc/pf/ which is a git repo and /etc/pf.conf is a symlink to /etc/pf/pf.conf. I have a default minimal-ruleset configuration for servers I import via git clone on new systems.
On critical systems where I absolutely can't afford to lock myself out (e.g. a Server without IPMI in some remote location) I use /etc/pf/pf.new to test out changes and have alias pftest 'pfctl -f /etc/pf/pf.new && sleep 120 && doas service pf restart' defined in my .cshrc so I can test new rules by issuing pftest and IF I locked myself out, the changes will be rolled back after 2 minutes.
 
Use some form of version control for your PF configuration. This way you can easily roll back or look up *why* you've added or changed some rules 5 months ago (you write proper commit messages, don't you?).
I used good old RCS or CVS for such. CVS can't be used by root.
Code:
pkg install rcs
man rcsintro
cd /etc
mkdir RCS
ci -l -m'initial revision' pf.conf
 
Hi sko, yes I read the article carefully and was aware of its lineage. However, it's a great article and it's principles still apply. I also looked at the relevant chapters of the third edition of "The book of PF", the man pages, and such. I've definitely got a way to go understanding how it all works, but given where I was a week ago and where I am now, I'm in a better place. Honestly, I don't think I'm worse off for pasting in rules I don't understand and observing the working effect than trying to fully understand without experimentation. I didn't tell the entire story in my configuration, when I said I was behind the ISP router, I left out the bit about my fully configured and operational wifi router being between the ISP and my gateway box and my simulated test vs the live test and so on as it would have needlessly complicated the discussion.

Oh, and it being 10 years old... the only reason I have any network understanding at all is because I read RFC 791, 792, and 793 - they're 39+ and still relevant.
 
mjollnir, I had completely forgotten about the usefulness and simplicity of RCS. I tend to use git (hosted on a FreeBSD server in jails using Focker) for my source code and important notes. sko 's point about using version control is excellent. Doh, I usually copy off the originals and versions - standard version control is much easier than copying files around and much less error prone.
 
ticket_1.png
 
I seldomly touched CVS or RCS, and git was the first VCS I've ever really "worked" with and so I just kept that although for many things I do with it, it might be a massive overkill... all the "everyday" git-commands are wired into my hands muscle-brain, so checking out changes to a config is completely automated in my brain. I really do love small, efficient tools, but even if RCS might fit that I think it's near to impossible to overcome the hard-wired git habits...
 
Back
Top