Re: Firewall rules in a directory

From: Ian Smith <smithi_at_nimnet.asn.au>
Date: Tue, 06 Sep 2022 05:52:55 UTC
On 5 September 2022 12:18:02 pm AEST, "Dan Mahoney (Ports)" <freebsd@gushi.org> wrote:
 > 
 > > On Aug 31, 2022, at 10:47 AM, Ian Smith <smithi@nimnet.asn.au>
 > wrote:
 > > 
 > > On 30 August 2022 2:40:34 pm AEST, "Dan Mahoney (Ports)"
 > <freebsd@gushi.org> wrote:

 > >> Note, this wasn’t intended to be “here’s a diff, please put it
 > in”,
 > >> just an illustration of how trivial an addition it is.
 > >> 
 > >>> On Aug 29, 2022, at 9:36 PM, Dan Mahoney (Ports)
 > >> <freebsd@gushi.org> wrote:
 > >>> 
 > >>> All,
 > >>> 
 > >>> At the dayjob, we’ve taken to putting our ipfw rules into a
 > >> directory using rcorder’able files.  This way, each of our puppet
 > >> manifests can drop its own rules into place without having to
 > manage
 > >> a monolithic file.
 > >>> 
 > >>> It’s a simple patch to rc.firewall, where if you set
 > firewall_type
 > >> to a file, it just runs it, but if it’s a directory, it would
 > treat
 > >> it as such:
 > >>> 
 > >>> *)
 > >>> if [ -r "${firewall_type}" ]; then
 > >>>   if [ -f "${firewall_type}" ]; then
 > >>>     ${fwcmd} ${firewall_flags} ${firewall_type}
 > >>>   else
 > >>>     if [ -d "${firewall_type}" ]; then
 > >>>       for fwfile in `rcorder $firewall_type/*`
 > >>>         do
 > >>>           ipfw -q $fwfile;
 > >>>       done
 > >>>     fi
 > >>>   fi
 > >>> 
 > >>> Is there a possibility of getting this into base?
 > >>> 
 > >>> -Dan
 > > 
 > > Getting code into rc.firewall has proven difficult over the years,
 > for me impossible. It even took julian@ a couple of years to get a
 > sensible use of tables into firewall_type 'simple' - but things may
 > have changed.

https://lists.freebsd.org/archives/freebsd-ipfw/

From my perspective, this used to be a great list.  Now it's nearly all just bugzilla reports with very occasional human seeking discussion.  Clearly, developers must prefer it that way.

I'm inclined to trim quotes somewhat Dan, but for starters let me say that
 a) I think this is a really good idea,
 b) getting it out there as a patch with explanation would be good at somewhere like /usr/share/examples/ipfw, and
 c) I wouldn't blow too much time trying to get it into /etc/rc.firewall

Apart from arguably 'workstation', which I've found amenable to adding a few rules in /etc/rc.local, for years I've copied rc.firewall to rc.myfirewall ono and hacked away, mostly on 'simple' for local nets.

 > > If it's really intended to launch multiple instances of ipfw, it
 > may win more favour - as a bug / feature request as Kevin suggests -
 > if you're sure how things like 'service ipfw status' or 'restart'
 > handle them in /etc/rc.d/ipfw?

I see now that you're adding sections to the one instance, which makes the above moot as these are just lists of ipfw commands, not scripts.

[..]

 > So, right now, there are two knobs you can tweak in /etc/rc.conf --
 > firewall_type, and firewall_script.  Firewall_script defaults to
 > /etc/rc.firewall which understands things like "open" or "client" or
 > "unknown", or a file.  The last bit of the stock rc.firewall looks
 > like this:
 > 
 > [Cc][Ll][Oo][Ss][Ee][Dd])
 >         ${fwcmd} add 65000 deny ip from any to any
 >         ;;
 > [Uu][Nn][Kk][Nn][Oo][Ww][Nn])
 >         ;;
 > *)
 >         if [ -r "${firewall_type}" ]; then
 >                 ${fwcmd} ${firewall_flags} ${firewall_type}
 >         fi
 >         ;;
 > esac
 > 
 > Two problems there.  1) -r only checks for readability, not for it
 > being an actual file.

"-r   True if file exists and is readable"

I usually find -s more useful, but I often add 0-byte files as comments:
 touch -r flxbz 'what this flxbz is'

And in any case, only running ipfw on a file will tell you if it contained valid contents - suggesting that the script should check ipfw's return code, and say something useful if it's non-zero?

 > and 2) For us, we *like* it being a directory.
 > 
 > Here's an output of rcorder:
 > 
 > rcorder /etc/ipfw.d/*
 > /etc/ipfw.d/setup
 > /etc/ipfw.d/production_networks
 > /etc/ipfw.d/production_static
 > /etc/ipfw.d/routing
 > /etc/ipfw.d/services
 > /etc/ipfw.d/snmp_agent
 > /etc/ipfw.d/ssh_service
 > /etc/ipfw.d/tftp_service
 > /etc/ipfw.d/mosh_service
 > /etc/ipfw.d/node_exporter_agent
 > /etc/ipfw.d/nrpe_agent
 > /etc/ipfw.d/ssh_vpn
 > /etc/ipfw.d/outbound
 > /etc/ipfw.d/local
 > /etc/ipfw.d/krb5_client
 > /etc/ipfw.d/dns_client
 > /etc/ipfw.d/syslog_client
 > /etc/ipfw.d/ntp_client
 > /etc/ipfw.d/final
 > 
 > And...as an example, here's some of /etc/ipfw.d/setup (note the
 > PROVIDE and BEFORE entries at top, like rcorder wants).
 > 
 > #
 > # PROVIDE: setup blocked bogons
 > # BEFORE: services routing outbound final
 > #
 > 
 > # remove all existing tables
 > table all destroy
 > table blocked create

All good stuff.

 > # standard (non-service specific) tables
 > table bogons create
 > table bogons add 0.0.0.0/8
 > table bogons add 10.0.0.0/8
 > table bogons add 172.12.0.0/12
 > table bogons add 192.168.0.0/16
 > table bogons add 169.254.0.0/16
 > table bogons add 240.0.0.0/4
 > 
 > # permit existing TCP sessions
 > add allow tcp from any to any established
 > 
 > # permit existing stateful traffic
 > add check-state :default // permit stateful traffic
 > 
 > # permit internal loopback traffic
 > add allow ip from any to any via lo0
 > add allow ip from any to any via lo1
 > 
 > # deny directed loopback traffic
 > add deny ip from any to 127.0.0.0/8 in
 > add deny ip from any to ::/64 in
 > 
 > # deny unexpected sources
 > add deny ip from table(bogons) to me in // unexpected sources
 > 
 > # deny explicitly disabled (non-persistent) sources
 > add deny ip from table(blocked) to me in // emergency
 > (non-persistent) blocklist
 > 
 > # allow bsd-standard-port traceroutes
 > add allow udp from me to any 33434-33600 // traceroute in
 > add allow udp from any to me 33434-33600 // traceroute out
 > 
 > # moderately permissive ICMPv4
 > add allow icmp from any to any icmptypes 0,3,8,11,13,14 // safe
 > ICMPv4

I couldn't get even icmptypes 0,3,8,11 into 'client' and especially 'simple' seen as a good idea, but that was before phk@ added 'workstation' which fixed that there.

I'll have to take your word on ipv6.

 > # link-local ICMPv6 (RS, RA, NS, NA) - per FreeBSD standard rules
 > add allow ipv6-icmp from :: to fe80::/10 // ICMPv6 DAD
 > add allow ipv6-icmp from fe80::/10 to fe80::/10 // ICMPv6 NDP
 > add allow ipv6-icmp from fe80::/10 to ff02::/16 // ICMPv6 NDP
 > add allow ipv6-icmp from any to any icmp6types 1,2,3,128,129,135,136
 > // safe ICMPv6
 > 
 > ....
 > 
 > And here's a service entry...
 > 
 > more /etc/ipfw.d/ssh_service
 > # REQUIRE: services
 > # PROVIDE: ssh_service ssh_clients
 > # BEFORE: outbound
 > 
 > table ssh_clients create
 > 
 > table ssh_clients add 1.2.3.4
 > table ssh_clients add 2001:dead:beef:cafe::d00d
 > 
 > add allow tcp from table(ssh_clients) to me 22 in setup         //
 > inbound SSH
 > 
 > ==
 > 
 > Also unique to our setup: the "local" script is created by puppet but
 > not managed by it, so if you need to drop an emergency override in
 > there for something (i.e. block an attacker, or open a port that you
 > haven't added to automation yet, add a counter to debug an issue, you
 > can, quickly).
 > 
 > Some of our scripts are placeholders, just existing as a no-op to
 > anchor things like BEFORE.

To me, this is a great example of leveraging things like rcorder to another if similar purpose.

 > If people wanted things to put in /usr/share/examples (say
 > /usr/share/examples/ipfw/client, or /usr/share/examples/ipfw/closed?)
 > that mimic'd the main setup, I'd be happy to contribute them.

Maybe you could offer the whole thing as a script that includes
 a) a patch to rc.firewall making say /etc/rc.firewall_builder
 b) sysrc commands to set firewall_script to that, and firewall_type to the directory, perhaps a subdir off 'here'.
 c) 2 or 3 example sections?

So at least there'd be somewhere people could point or refer to, while awaiting favour from ipfw deities :)

 > (I'm also not thrilled with the fact that the stock firewall script
 > adds rules before it determines what kind of firewall you want, and
 > then applies your rules...that could perhaps be a different bug
 > though).

You've included setup_loopback and setup_ipv6_mandatory rules anyway, mostly, but you could always just start off with perhaps
 delete 1-1200

 > If there's a diffferent list I should be posting this to, let me
 > know.

Excuse my cynicism re once-great lists; I've been too long out of touch.

cheers, Ian