Bug 238694

Summary: Configuring & using a customized IPFW rule set now causes additional rules to be (involuntarily) added
Product: Base System Reporter: Ronald F. Guilmette <rfg-freebsd>
Component: confAssignee: freebsd-ipfw (Nobody) <ipfw>
Status: New ---    
Severity: Affects Some People CC: jamie, karl, rgrimes, rkoberman
Priority: --- Keywords: regression
Version: 12.0-RELEASE   
Hardware: Any   
OS: Any   

Description Ronald F. Guilmette 2019-06-18 18:58:29 UTC
The HandBook (Section 30.4.1) describes how to enable an IPFW firewall while using a locally customized set of IPFW filtering rules.  This basically boils down to placing two lines, like the following, into the /etc/rc.conf file:

     firewall_enable="YES"
     firewall_type="path-to-my-rules-file"

I have been using this exact motif in /etc/rc.conf, and my own customized set of ipfw rules for years, but I recently upgraded to FreeBSD 12.0-RELEASE.  Once I had done so, I noticed (when I checkd using "ipfw -a list") that now, several different IPFW rules were somehow being added to my explicitly specified IPFW rule set, prior to my own rules.

This appears to be due to the invocation of the new /etc/rc.firewall script which injects into ipfw several of its own IPFW rules ahead of whatever rules the user provides within the file designated by "path-to-my-rules-file".

I verified this by finding one part of the /etc/rc.firewall script where this was ocurring, commenting out some of the relevant lines therein, and then rebooting the system.  Sure enough, the relevant IPFW rules that were formerly being inserted into IPFW by /etc/rc.firewall were no longer showing up when I did a fresh "ipfw -a list".

I believe that it would be appropriate (and would be maximally consistant with past behavior in prior FreeBSD releases) if, when the user specifies his or her own explicitly provided IPFW rule set, those rules are used verbatim, and are not augmented by the /etc/rc.firewall script.
Comment 1 Rodney W. Grimes freebsd_committer freebsd_triage 2019-06-19 16:09:56 UTC
I concur that this is in fact a bug in the /etc/rc.firewall script.  It is NOT a bug in the documentation.  The documentation is correct, the behavior of the script is wrong.

I suggest that the "mandatory" stuff that is always done is simply wrong to be always done, if in fact a file type script is being invoked I may very much want those "mandatory" rules in a different place and shall be allowed to do so.

Further this is a change in past behavior causing a POLA violation.
Had I been using firewall_type rather than firewall_script to point to my custom firewall this would of bit me too, and in not very pleasant ways.

Besides, they are not actually mandatory from anything I can even remotely imagine.  Technically you should even be able to remove lo0, but *sigh* that has also degerated over the years, as has hard coded 127.0.0.1 and ::1, which is a royal PITA for some.
Comment 2 rkoberman 2019-06-19 22:05:11 UTC
It is not a bug as it is mandatory for IPv6 support. Without those rules, the network startup will hang. If IPv6 is disabled, it ould be best if those rules were NOT added and that might be a bug, but, assuming the default setting of deny_by_default, the firewall is always started before the network and deny_by_default will block ICMPv6 resulting in the system startup never completing.

I concede that this needs to be clearly documented, but the behavior is mandatory. like the localhost name, loopback configurtion, and the terminal "65535 deny ip from any to any" for deny-by-default, these are simply required or normal network operations.
Comment 3 Rodney W. Grimes freebsd_committer freebsd_triage 2019-06-19 23:04:01 UTC
(In reply to rkoberman from comment #2)
Your ignoring the fact that though these rules MAY be needed the rc.firewall script can not know where and at what rule numbers they need to be to function correctly with my custom written firewall.script invoked by "firewall_type=pathname".

In my custom firewall the loopback rules are NOT at 100,200.. but are infact burried much deeper in other logic as running these rules for every packet is a total waste of time since very little of my traffic is from or to lo0.

Your also assuming that someone is running the stuff you mention, and that is, as the Primary Reported has stated, and I have acknowledge, a bad assumption.
Comment 4 rkoberman 2019-06-20 05:32:18 UTC
Bottom line default requirements:

1. System firewall must start with a deny-by-default rule in place when network starts

2. Both IPv4 and IPv6 must start

3. Mandatory packets must be allowed from network starts This includes loopback for both IPv4 and IPv6 as well as support for several ICMPv6 and group addresses that are mandatory for default IPv6 function.

When I suggested starting the firewall after the network had started, I was immediately (and correctly) shut down because of the security vulnerability this presents. That is why it needs proper documentation so you can insert rules between those that are mandatory. With spacing of every 100, there is a lot of room.

I have no answer for the issue of efficiency via the ordering of rules. While the time required to process these rules is very small, it is not zero. (Darn close for the trivial, stateless rules, though.)

Since I agree the way it is done now is totally non-transparent, the only solution I can see is proper documentation.
Comment 5 Rodney W. Grimes freebsd_committer freebsd_triage 2019-06-20 05:50:32 UTC
(In reply to rkoberman from comment #4)
Your now implementing, or advocating implementing, policies that are simply not within the scope of what FreeBSD should be implementing.

I'll give you that your list of default requirements are valid, and correct, but the moment a user TOUCHES firewall_foo we are no longer in the default world, and we should fully respect what ever policy the user so chooses and should fully and correctly do so in the most painless way possible.

If the user wishes to change things he shall be allowed to, otherwise we are driving him to go edit etc/rc.firewall and that is not the desired results.

Furthermore this IS a regression in behavior, in the past we had no such rules being added in this case, and that more than anything is the reason we have this bug report at all and we should respect that as a true and valid issue.

Bottom line, no one is advocating changing what the end results of the DEFAULT configuration is, we (I) are advocating that things be made properly flexible and backwards compatible, ie this users old and working configuration suddenly broke in unexpected ways and that is just bad.

It is rather trivial to fix:
case ${firewall_type} in
(very long regex that matches all the known types)
    setup_loopback
    setup_ipv6_mandatory
esac

restored prior behavior and your "Requriements" have also been met.
Comment 6 karl 2019-06-20 13:39:16 UTC
Just a quick note on practicality in the real world.

If you don't have an "ipfw -f flush" as the *first* element in your custom script file you're eventually going to get a nasty and unwanted surprise.  If you *do* have such as the first element of your custom script then whatever was there before it executes is gone.

I use the custom script myself and have since forever as my configuration in places where I need ipfw is quite complex, and when I moved to 12.x I noted no change in behavior.... likely for this reason.
Comment 7 Rodney W. Grimes freebsd_committer freebsd_triage 2019-06-20 14:36:00 UTC
(In reply to karl from comment #6)
I agree Karl, one does have to be very careful when hand crafting there own firewall.  Most of mine do in fact use the simple flush, but there are other techniques, such as load the set of rules into a known empty set and do a set flip, complicated state management that knows how to incrementally remove and add the proper sequence of rules, etc.

I think the reason so very few reports exist about this bug is that we have 2 ways to cause an external script to load, setting firewall_type="/path/to/file" and firewall_script="/path/to/file".  The second form always works exactly as we (we being I think all of us) expected it to, however the former now has this wart that we get the, by my claim fake, loopback stuff.  It is this wart that is at issue and we should solve that so the behavior of firewall_script= and firewall_type=path are exactly the same.   Can I get an agreement on that point?
Comment 8 karl 2019-06-20 15:31:44 UTC
(In reply to Rodney W. Grimes from comment #7)

I never took the "firewall_type" parameter as being legitimate to point at a script.....  Maybe I'm a bit too pedantic but it has kept me out of trouble in this regard! :)
Comment 9 Rodney W. Grimes freebsd_committer freebsd_triage 2019-06-20 15:43:18 UTC
(In reply to karl from comment #8)
Nor have I, but it is documented, it does work, and until the changes that cause the loopback stuff to always be done it worked identically.  I consider this a regression and it should be fixed.  Do you agree?  Does anyone agree?
Comment 10 karl 2019-06-20 15:46:01 UTC
(In reply to Rodney W. Grimes from comment #9)

I agree it's a regression and is easily fixed -- and thus should be.

IMHO if you set up a script that doesn't provide IPv6 rules, and DOES wind up blocking them (e.g. via the default at the end), and that stops the network from coming up (or even hangs the system waiting for it during boot) that's on you.

There are myriad non-default things you can do with the configuration that will result in having to ^C the startup from the console or even panic the box...
Comment 11 Ronald F. Guilmette 2019-06-20 19:52:02 UTC
Just FYI for everyone --

As it happens, *I* have "set up a script that doesn't provide IPv6 rules, and DOES wind up blocking them (e.g. via the default at the end)"... or so I believe anyway.

The system in question appears to be humming along just fine.  (I am typing this message on it as we speak.)


P.S.  I would love to find a person or two who is/are more knowledgable about firewalls than I am... which is to say just about anybody...  and who would be willing to take a peek at my current IPFW rule set and critique it for me.  Understandably, I am not eager to just post the thing publicly (in case it has gaping holes that I'm not aware of) but if anyone is willing to take a peek, please email me privately and I'll seed you a pastebin link and we can take it from there.
Comment 12 rkoberman 2019-06-23 01:01:04 UTC
I am now very curious as to what changed in 12.0 that triggered new behavior. I just looked at the repo and the inclusion of these IPv6 rules happened a decade ago... on Dec 2, 2009. It is almost completely untouched since then.So why had it suddenly been noticed? I believe that the issue is that it was NOT supposed to happen unless IPv6 is enabled due to the "[ $ipv6_available -eq 0 ] || return 0" that, I assume was intended to have these rules added only when IPv6 was not on. It looks like something else changed in 12 that caused ipv6_available to be set to 1 even though IPv6 is not available. (I need to look at other scripts to find out about this).

This also applies to the IPv6 loopback rules.The first incarnation I can find for this was in 1997, but the version in which it was changed 
back in 1997, but it was changed prior to that in a commit (15027) that does not seem to be in the repo.

So, I now agree that there is a bug here, both in the code and in the documentation.
Comment 13 Ronald F. Guilmette 2019-06-23 06:04:25 UTC
(In reply to rkoberman from comment #12)

This is a bit embarassing, but in light of your question, I have a confession to make.

It would NOT be appropriate to jump to the conclusion that this issue just arrived with 12.0.

To be frank, I have just upgraded to 12.0 from 9.1, which I had been running for many years.

Yes, I should have been keeping up with new releases all this time, but I didn't.  I am ernestly embarassed about that, but what can I say?  I had a lot of stuff to do.
Comment 14 rkoberman 2019-06-23 06:31:43 UTC
9.1 is years newer than this change. You would have to go back to 7.2 or 6.3 to predate the IPFW rule inclusion and much older, probably to at least 2.2, to predate the loopback insertion.

I suspect that ipv6_available was set to '0' when the network startup actually brought up an IPv6 connection, but I have yet to find any code to that does so.

Back a few years ago the loopback stuff was changed from using rules starting will 1000 and incrementing by 1000 for wash run to starting with 100 and incrementing vy 100. Initially only the first rule was added and later the rules at 2000 and 3000 ere added. Those are now at 200 and 300. Nothing has changed in the firewall rules insertions between 9.1 and 12.0. I am trying to track down where ipv6_available might have been in 11.2. Guess I'll need to look back a lot further... but not tonight.
Comment 15 Ronald F. Guilmette 2019-06-24 00:56:52 UTC
(In reply to rkoberman from comment #14)

All I can say is that I just pulled the /etc/rc.conf file from my old 9.1 system out of mothballs and here are the relevant lines from that:

firewall_enable="YES"
firewall_type="/etc/fw.rules"
firewall_logging="YES"

The /etc/fw.rules file was a file that I constructed that contained only "add" commands.

I never saw anything other than the filter rules from that file when I did "ipfw -a list".