PF filtering

So I want to try as an experiment "straight piping" my home modem (just passing all traffic) and letting my computer act as the firewall with PF rules. I would be using no other devices for the duiration of the experiment. I have read several resources on PF, including "The book of PF" (which I guess is a little outdated by now, I got my hands on a 3rd edition), and I have a feeling that I have a decent beginner's grasp of its capabilities. What I am wondering, though, is if anybody can point me in the direction of resources for some modern best practices on PF rules for a home network. What I am afraid of is not the things I know I don't know, but the things I don't know I don't know. I don't want to set up a firewall that seems complete and comprehensive, only to be steamrolled by some common threat I didn't think of or be unable to browse the internet.

Sorry if this is too FreeBSD-abstracted, and thanks for any help.

---

Linking previous thread on the topic here, for reference: https://forums.freebsd.org/threads/firewall-best-practices-standard-ruleset.67587/
 
My opinion only, others may disagree.
The 3rd edition is old, but the principles still stand. It's a way of thinking how the traffic should or should not flow.

All firewalls should start with default deny on all interfaces. This prevents the world from initiating connections to your system and makes it easier overall to manage.

Think of it like this:
You are renting an apartment. They allow some pets, but not all. It's clearer in the lease to say:
All pets are forbidden except for dogs less than 50lbs, cats and ferrets.
Instead of:
All pets are allowed except for weasels, pigs, ..... This gets you someone saying "you did not explicitly exclude a wombat so my emotional support wombat is allowed"


PF is "last match wins" (unless a matching rule has quick on it), so order of rules can be important.
set skip on lo (reduces a bit of processing)
Use state ( I think this is default)
Learn how to use the "quick" keyword
You will probably want the firewall to NAT all the internal systems

Traffic on the internal lan destined to "the world" will create state so the replies will be allowed.
The number of things actually needed to be opened are actually few in the typical case, less than a dozen.
DNS, HTTP/HTTPS, NTP, email (SMTP but check what your ISP allows) gets you a lot of things actually working.
Monitor and if someone in the house complains, you can just add a new one.
TCP and UDP are different rules, but some things that used to be say UDP only have been changed to be on TCP.

Draw pictures. A box that is your firewall, with internal interface and external interface.
Now stand in your firewall, face external and ask "what do I want to initiate connection to my network?" Answer is typically "nothing" UNLESS you are running a service you want to be available to the public.
Now turn around and face internally and ask "What do I want internal network to initiate connection to?" Easy answer is "anything" but the correct answer is "what do they need" (again my opinion). So the rule(s) is(are) (not real syntax):
allow in on internalinterface to any ports {list of allowed services} keep state

I strongly believe that one should be able to take a picture, take the rules and walk everything to hand check the behavior.
Sorry if this got long hope it helps
 
It helps a lot, and I am extremely grateful.

I guess the logic for a home network (with no public services), or a good logic, is that you want only but every packet you start, restricted to known services in order not to tempt the Devil, to pass out, and only but every response packet to pass in?

Here is thing which I don't know if it's overthinking or paranoia, or maybe real: how common is it for, say, response packets to be exploited in order to, say, scrape my system beyond my bandwith capabilities (or desires)? Are there piggyback threats that I should be actively looking out for, or should the simple logic above cover most situations for a common home network?
 
Others may have more information, but when I first migrated to broadband at home, I just "watched". Once you expose a public IP, everyone tries it. Lot's of script-kiddies hammering on ssh and a few others. Even if you are not serving anything, someone will try. It's like someone walking down the street and trying every doorknob: if one in 10 is unlocked good for the theif.

But that is why "default deny" which means those actions simply get dropped and should never get into your system (default deny means ssh originating from outside is blocked)

Lots of "responses" may happen but if they don't relate to legitimate traffice (this is why you keep state) are treated as "we just going to drop you because you are not real"

So I've done the handcrafted FreeBSD firewall in the past to front my home network, but discovered it's simplier/easier fronting my network with something like a pfSense or OpnSense dedicated device.

I guess the logic for a home network (with no public services), or a good logic, is that you want only but every packet you start, restricted to known services in order not to tempt the Devil, to pass out, and only but every response packet to pass in?
That is my preference but others say that "if it originates from inside, it should be allowed". Basically "default deny in" on every external interface with "default allow out" on the internal LAN interface.
Arguments can be made for both "default deny on every interface" and "default deny in on external, default allow out on internal".

Ease of use vs tighter security. Security is always a tradeoff; you need to decide what you can stand vs how tight things are.
 
I guess I'll go with a simple set up along the lines of what you suggest, and simply invest more time and effort in monitoring traffic than I had been planning to.

It's not script kiddies that I'm worried about so much. My theory is that software development is getting to a point of sophistication that, more and more, the value will be in real estate instead of content. Computing capacity, as counterintuitive as it may sound given the monstruous growth of it in the last decade, will, I suspect, become the choke point. Given this, there will be much more incentive than in the past for systems to hijack my machine to deploy their own software.

I think automated attacks will be much more relevant now than what possibly was the major threat a decade ago: hackers, from script kiddies to malware producers. The second category would be much easier to defeat intuitively with human filtering ideas (the whole graylisting and graytrapping section of the Book of PF is a great example), while who knows what a statistical analysis of the systems will reveal as novel and promising entry points.

But if all my googling and all my asking hasn't turned up anything glaringly obvious yet, maybe this is all theoretical paranoia. I think I'll try and see what happens.
 
Seriously, "default deny" as the start means anyone trying to initiate in gets dropped. Think of it as locked door. Locked door means casual testing doorknob fails, but does not prevent someone trying to pick the lock. But picking the lock takes time and if a neighbor sees a stranger trying to pick a lock, call 911.

Default Deny and adding things as needed means you never allow more than needed. A bit more pain in the beginning, but you quickly get to the state of "works for 98% of the time". Then just monitor and work through the last couple percent.
 
My word almost every derned port up to 2000 and beyond has a service name.

This is what irks me about messing with any of this. How many of these services are things I don't think I need but are actually essencial?

Wait f I know. I just run sockstat -cl4 and see what is actually getting used now. And generally maybe just start opening doors if things don't work until they start working.

---

None of what I wrote above makes any sense. Thankfully it was all falsifiable.

I'm dixelsic so sometimes these in -out things can send me througha loop much like right and left could. But the point is: the services I seek to restrict are those on the "to" portion of connections I initiate. Basically "let every packet out that has one of these ports as a destination." And then "allow all responses." The second part which I thinbk I don't need to write, it's the implied statefulness.

We're gonna run with that, after I review in The Book of PF the different monitoring techniques he offers so I can at least have the illusion of a handle on what will transpire.

Shoot how bad can it get.
 
Question: does anybody know if there is a way to programmatically alter a table that's in memory? Say with C? What I don't want is to maintain a list on disk and then have to constantly flush it to disk and then load from disk.
 
I believe that pfctl allows you to alter a table; typically file from disk is used, but stdin is a file so maybe that is a path programmatically.
 
You mean like just system()?

Maybe. I would be happier to find a way that doesn't have to go first through a shell and then through the pfctl interface just to change a line on the table.

But maybe if I want that kind of control, I better start looking at C libraries for packet handling.

Like Ruben Blades said, "decisiones."
 
There are already tools/ports like Suratica and other IDS things that know how to manipulate pf tables (block lists). I'm not sure how they do it, but they could be a clue as to "how" you can.
lower level packet handling I think bpf and netgraph are hints
 
My problem with rate limiting is that I couldn't find a way to write something like:

"each ip can only perform a tls handshake X times per Y seconds." Best I could find was "if ip X performs a tls handshake Y times in Z seconds, add to A table."

For now, I think I will probably do a blanket PF policy, and snipe certain things with blacklistd.
 
Thanks. Yeah, that's the rock I eventually came up against. The best you can do in terms of controlling what happens to a given ip is adding it to a table, and giving that table static properties. It's not very malleable. And if you want to change that table for any reason, you have to modify it with an intrface, probably with cron too. It's too cumbersome. Imagine trying to do this with 1000 connections a second. It's just not viable.

What I want is a program that can directly do much more to the table than PF does, or barring that, that can take system calls or C funcitons that alter it.
 
Blacklistd is the new tables.


So reading here the mans for blacklistd and blacklistd.conf, there is something I don't fully understand:

When mention is made of "attempts," does this mean an attempt to begin a connection state in the PF sense, or simply an attempt for a packet to pass through? Does it maybe refer specifically to a handshake?

I can't believe it would be just "packet incoming," because that would make throttling on the basis on it kind of useless. But I'm missing something, the documentation considers it obvious but myself am not sure.
 
Attempt to initiate a connection. Usually not "just a packet". One can write rules that trigger on unexpected flags on a packet (TCP usually) which can be used to "do stuff"
 
Just served my very first html page from my homecooked server built entirely in C.

Not possible at all without threads like these and gentlemen like you.

Thank you from the bottom of my heart.
 
Back
Top