Put Your Firewall Rules In The Correct Order
There’s a chance you’re here from Google, but there’s a much bigger chance you’re here from my beginners guide to Junos firewall filters.
Regardless of whether you call them firewall filters, security policies, access lists, or something else, there is something that is true across almost all vendors: it is crucial that your rules are in the correct order. Because if they’re not, you could accidentally drop traffic by mistake – or at worst, cause an outage!
Let’s talk about how a networking device checks a packet against a set of rules. This is pure theory, multi-vendor, with no config. Nice and easy on the brain!
A THEORETICAL SET OF RULES
Imagine you have a networking device that is configured with these three rules (which we’ll call “terms”, to get us in the Junos language mindset):
- ALLOW all traffic sourced from 192.168.10.0/24, 192.168.20.0/24, and 192.168.30.0/24, destined to anywhere, for any kind of traffic.
- BLOCK traffic sourced from anywhere, destined to 10.20.0.0/16, if the traffic is on TCP port 443 (encrypted web traffic, HTTPS).
- ALLOW everything else.
All networking devices will process packets against these terms, from top to bottom.
Here’s the crucial thing to know: the device will stop processing as soon as a match is found.
For example, imagine that your networking device receives a packet like this:
- Source IP: 192.168.10.5
- Destination IP: 10.20.5.3
- Type of traffic: TCP, port 443 (HTTPS)
Look at this packet, and compare it to the three terms in this list. Which term will this packet match against?
You can see that term 2 blocks traffic destined to anything in the entire 10.20.0.0/16 range, when the traffic is HTTPS. Aah, looks like we’ve got a match!
But wait… NOT SO FAST, SUNSHINE. The first rule allows all traffic sourced from any IP in the 192.168.10.0/24 range. It doesn’t matter what kind of traffic it is, or where it’s destined. As long as the traffic is sourced from this IP range, or one of the other two ranges mentioned, then the traffic is allowed.
Hmm… this seems like a clash! One of these rules allows the traffic, but the other blocks it. Which rule wins?
The answer is always “the first term in the list that gives you a match”.
In this case, the packet matches the first term – and therefore, the packet is allowed through. Terms 2 and 3 are not even checked. The packet matched term 1, and therefore the packet was allowed.
AVOID RULES THAT SHADOW OTHER RULES
In this particular example, things are working nicely. You only have three terms, and the intent is clear. You want to block all encrypted web traffic destined to 10.20.0.0/16, but there are a few IP ranges that should be excluded from this block. Apart from that, everything is allowed to everywhere.
However, as these security rules get bigger and bigger, it can become more and more difficult to identify if a rule is “shadowing” another rule.
For example, imagine a policy with a hundred terms. Imagine term 30 said this:
- 30: “Block all traffic destined to 10.20.0.0/16”
Then, imagine term 60 said this:
- 60: “Allow all traffic destined to 10.20.30.0/24”
This /24 range is part of the /16 range. This means that any packets destined to a machine in the /24 range will be blocked by line 30 in your list. Your line 60 will never be checked, because all packets destined to the /24 will, in fact match line 30.
You should always try to put more precise rules above more broad rules. For example, in this case, you should move the /24 term above the /16 term.
BEWARE THE DEFAULT “BLOCK ALL” POLICY
Finally, you may already know that almost all vendors have a default, hidden “block all” policy.
For example, imagine that you created a firewall filter / access list that contained just one single term. This term says:
“Allow all traffic destined to 192.168.50.0/24”
In this case, any traffic that isn’t destined to that IP range will be blocked. This is the hidden default “block all” term, in action.
In this circumstance, this is your intended outcome. Nice job, friend!
However, imagine this: instead of a rule with just one “allow” action, what if you had a rule that contained just one “block” action?
For example, imagine you have a single line that says “Block all traffic destined to 10.90.80.0/24”. A newcomer might create this with the intent of blocking traffic to that subnet, but allowing everything else.
However, you can probably guess why this won’t work: the default “block all” policy will block everything else, too. Congratulations: you’ve just caused an outage!
This is even more fun if you’re configuring this remotely, and you’re applying the rule to the interface you’re using to connect to the device in the first place. As soon as you save your work, you’ll lose access. Better grab your car keys!
The broad lesson here is to really put thought into what traffic you want to allow, and to make terms that explicitly allow it.
- The lazy solution is to create an explicit “allow all” policy.
- The more thoughtful solution is to look into exactly what traffic should be allowed, and create rules to allow it.
As you can imagine, this is a highly important concept. Now that you understand it, you will more easily be able to read and identify individual terms in some kind of firewall filter, access list, security policy, and so on. You also now understand that you might need to change the order of terms in a firewall filter, if one term is shadowing another.
With that in mind, let’s go back to the main post, to see how to put it into practice in Junos.