Floating rules: outbound block rules not silently dropping packets
-
First, I cannot tell which are floating rules and which are not, but I can tell you that by default floating rules are last matching wins unless you specify the "quick" option.
Also, by default, the first rules in the pre-programmed are the floating block rules. This means they all traffic is blocked unless there is a pass rule. -
First, I cannot tell which are floating rules and which are not, but I can tell you that by default floating rules are last matching wins unless you specify the "quick" option.
Also, by default, the first rules in the pre-programmed are the floating block rules. This means they all traffic is blocked unless there is a pass rule.All inbound traffic is blocked by default. But all outbound traffic is passed by default.
In this specific example, the floating rules are the user-defined rules without the "quick" keyword in them. (In my case, I have no "quick" floating rules.)
-
It is passed only because there is a rule created on LAN. You can change this rule. If you delete this rule, all access to the internet will stop. This is because of the unseen default block rules. Rules create in LAN, WAN and OPTx are all quick rules, this means first matching rule wins. So a block before and allow will always block the traffic. Floating is the opposite unless the quick option is checked.
-
It is passed only because there is a rule created on LAN. You can change this rule. If you delete this rule, all access to the internet will stop. This is because of the unseen default block rules. Rules create in LAN, WAN and OPTx are all quick rules, this means first matching rule wins. So a block before and allow will always block the traffic. Floating is the opposite unless the quick option is checked.
Yes, I know this already. The inbound rules are traversed first. Only if the inbound rules permit the packet to pass, will the outbound rules be traversed. I'm also aware about the difference between quick and non-quick.
This doesn't get us any closer to explaining why I'm getting an ICMP Host Unreachable back from the firewall when I'm doing something I'm not supposed to, rather than the firewall just dropping the packet on the floor, which was the actual subject of this thread.
-
I have just checked on 2.0.1-RELEASE, the same exact problem is present there. Could some friendly mod please move this topic to the Firewalling forum?
-
It turns out that there's some "undocumented behaviour" in which where a packet that has been passed on the incoming side is then blocked on the outgoing side causes pf to emit an ICMP host unreachable message. I've confirmed this on a lot of different systems, including the latest pf upstream (OpenBSD 5.2).
But I was able to work around this issue with an ugly hack. I discovered that by using a policy route to null route the packets instead of just "dropping" them, I could get the behaviour I wanted.
So I just patched the pfsense rule generation code to do just that.
This will probably break about 99 different things, but it seems to work for me in my test environment. If anyone else is having these issues and wants to give it a go, here are my local changes to pfsense (run the patch inside /etc/inc)
--- filter.inc.orig 2012-12-11 02:41:15.000000000 +0100 +++ filter.inc 2012-12-11 02:52:53.000000000 +0100 @@ -2342,6 +2342,18 @@ " label \"NEGATE_ROUTE: Negate policy routing for destination\"\n"; } + + /* ugly hack to convert "block" rules to "pass route-to 0.0.0.0" rules to work around a bug which causes floating rules applied on exit + interfaces to improperly send an ICMP unreachable instead of dropping the packet on the floor. this workaround fixes that. */ + if ($type == "block" && isset($rule['ipprotocol']) && ($rule['ipprotocol'] == "inet" || $rule['ipprotocol'] == "inet6")) { + $aline['type'] = "pass "; + if ($rule['ipprotocol'] == "inet") { + $aline['route'] = " route-to 0.0.0.0 "; + } else { + $aline['route'] = " route-to :: "; + } + } + /* piece together the actual user rule */ $line .= $aline['type'] . $aline['direction'] . $aline['log'] . $aline['quick'] . $aline['interface'] . $aline['reply'] . $aline['route'] . $aline['ipprotocol'] . $aline['prot'] . $aline['src'] . $aline['os'] . $aline['dst'] . --- filter_log.inc.orig 2012-12-11 03:16:15.000000000 +0100 +++ filter_log.inc 2012-12-11 03:15:44.000000000 +0100 @@ -159,6 +159,13 @@ $flent['proto'] = "none"; } + /* check for the hack where we're null routing. */ + list($rulenum, $junk) = explode('/', $rule); + $rule2 = find_rule_by_number($rulenum, $flent['act']); + if (!(strpos($rule2, " route-to 0.0.0.0 ") === FALSE) || !(strpos($rule2, " route-to :: ") === FALSE)) { + $flent['act'] = "block"; + } + /* If there is a src, a dst, and a time, then the line should be usable/good */ if (!((trim($flent['src']) == "") || (trim($flent['dst']) == "") || (trim($flent['time']) == ""))) { return $flent;
This is most emphatically not the "right" way to do it. But it's a practical solution. More practical than digging into the internals of pf. At least for me. :-)
-
While I may have a much simpler setup but I do have a similar concept. I have three interfaces on my pfsense box: wan - lan - opt1. I keep traffic between lan and opt 1 separate entirely, but both are allowed to use the internet. I believe the same is for you with the addition of vlans. Right now I do not permit any traffic but DNS/DHCP requests and traffic to the internet.
-
@pv2b - you've discovered one of the many reasons it's best to block the traffic as close to the source as possible.
Sure an outbound floating rule shortcut sounds convenient, but ultimately it's best to block on the individual tabs (or both, just to be extra sure).
-
I don't agree that it's neccessarilly better to block as close to the source as possible. There's a compelling argument to the opposite in fact: it's generally better to block as close to the target as possible, because otherwise you have to trust all the intervening nodes not to send malicious traffic. That's why we have personal firewalls filtering our incoming traffic instead of relying on everyone else to do egress filtering so no evil packets escape their networks.
Either way the argument for blocking like this isn't convenience, it's security. It's far too easy to screw up when you have to duplicate the same rule on a lot of interfaces.
-
It's far too easy to screw up when you have to duplicate the same rule on a lot of interfaces.
That's never a requirement, use interface groups if you need to do that.
-
I don't agree that it's neccessarilly better to block as close to the source as possible. There's a compelling argument to the opposite in fact: it's generally better to block as close to the target as possible, because otherwise you have to trust all the intervening nodes not to send malicious traffic. That's why we have personal firewalls filtering our incoming traffic instead of relying on everyone else to do egress filtering so no evil packets escape their networks.
Either way the argument for blocking like this isn't convenience, it's security. It's far too easy to screw up when you have to duplicate the same rule on a lot of interfaces.
Not quite. It's better to block as close to the source as possible under your control. Why let traffic any farther into your network than it needs to go? Your point is valid but only when the traffic is sourced remotely, on networks you do not control. When you control the source, there's no reason to let it pass in when you know it shouldn't.
Yes, blocking on the way out is also good but because of the way stateful firewalls work, combining the two philosophies will lead to issues like the one you discovered. You allowed the traffic in one way, and blocked it out another. That is never as good as blocking it before it comes in.
By using aliases, interface groups, etc, you can reduce the likelihood of making an error or over-duplicating.