Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 4 Sep 2022 19:18:02 -0700
From:      "Dan Mahoney (Ports)" <freebsd@gushi.org>
To:        Ian Smith <smithi@nimnet.asn.au>
Cc:        questions@freebsd.org, kpn@neutralgood.org
Subject:   Re: Firewall rules in a directory
Message-ID:  <9BFB27DC-BA45-49B5-8EAD-B5BE7BC14E20@gushi.org>
In-Reply-To: <DED6C218-0517-4A0D-8C7A-1FDBFFC84A3D@nimnet.asn.au>
References:  <3FAB82EC-2C82-4201-AA47-B1AA92B89677@gushi.org> <D666503D-E5E2-4B6D-A960-A362EEFE6F95@gushi.org> <DED6C218-0517-4A0D-8C7A-1FDBFFC84A3D@nimnet.asn.au>

next in thread | previous in thread | raw e-mail | index | archive | help


> On Aug 31, 2022, at 10:47 AM, Ian Smith <smithi@nimnet.asn.au> wrote:
>=20
> On 30 August 2022 2:40:34 pm AEST, "Dan Mahoney (Ports)" =
<freebsd@gushi.org> wrote:
>> Note, this wasn=E2=80=99t intended to be =E2=80=9Chere=E2=80=99s a =
diff, please put it in=E2=80=9D,
>> just an illustration of how trivial an addition it is.
>>=20
>>> On Aug 29, 2022, at 9:36 PM, Dan Mahoney (Ports)
>> <freebsd@gushi.org> wrote:
>>>=20
>>> All,
>>>=20
>>> At the dayjob, we=E2=80=99ve taken to putting our ipfw rules into a
>> directory using rcorder=E2=80=99able files.  This way, each of our =
puppet
>> manifests can drop its own rules into place without having to manage
>> a monolithic file.
>>>=20
>>> It=E2=80=99s a simple patch to rc.firewall, where if you set =
firewall_type
>> to a file, it just runs it, but if it=E2=80=99s a directory, it would =
treat
>> it as such:
>>>=20
>>> *)
>>> 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
>>>=20
>>> Is there a possibility of getting this into base?
>>>=20
>>> -Dan
>=20
> 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.
>=20
> I've tried rendering your code into the usual format below, saving a =
level of indenting with 'elif', and noting that '-q' and path is =
included in ${fwcmd} earlier in rc.firewall.
>=20
> 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?
>=20
> Good Luck, Ian
>=20
> <code>
> *)
> 	if [ -r "${firewall_type}" ]; then
> 		if [ -f "${firewall_type}" ]; then
> 			${fwcmd} ${firewall_flags} ${firewall_type}
> 		elif [ -d "${firewall_type}" ]; then
> 			for fwfile in `rcorder ${firewall_type}/*`
> 				do
> 					${fwcmd} ${firewall_flags} =
${fwfile}
> 				done
> 		fi
> 	fi
> 	;;
> </code>

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.  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

# 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

# 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

=3D=3D

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.

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.

(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).

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

-Dan=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?9BFB27DC-BA45-49B5-8EAD-B5BE7BC14E20>