2021.03.02 - FirewallD
My notes on how to get FirewallD to do what I want
Intro
I liked iptables, sure the commands were sometimes a bit cryptic, but it generally made sense to me and I liked that there was one big file of all the commands to easily edit. Well the world has moved on and FirewallD is now the prefered Linux firewall program. You can of course still install iptables but I'm trying to change with the times. That said I'm really having a hard time wraping my head around FirewallD and all the commands to use it, so I'm going to type this guide out and hopefully finally remember how to use this thing. I'm using Alma Linux 8 (as a replacement for the sadly departed CentOS 8) so I'd assume these instructions are good for any linux OS using FirewallD but who knows?
To start with FirewallD has only one zone loaded, public which is also set to the default zone. To control FirewallD you use firewall-cmd. When using firewall-cmd you have to specify a zone, if you don't it uses the default zone (in this case public). For my own sanity I'm always going to specify a zone even if I want public. FirewallD also has a running config and permanent config. When FirewallD is reloaded the running config is erased and the permanent config is loaded (just like iptables). There's also a command to only change the permanent config and not the running config which seems like a stupid idea to me, so I'm going to ignore it.
Getting Started
So first (after making sure FirewallD is running) take a look and see what you've got with:
# firewall-cmd --get-active-zones
public
interfaces: eth0
#
# firewall-cmd --list-all --zone=public
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-block:
rich rules:
#
So this says that I've got one zone active, its being applied to all traffic on eth0 and it only accepting ssh traffic. All unmatched traffic is having target: default applied to it. There are 4 possible targets: default, ACCEPT, DROP, REJECT. You can read about them here, but basicly they all do what they sound like and default is the same as REJECT but it permits ICMP and a few other small things.
In my prefered setup I normally have a selection of services for the world to access, a couple of extra services special IPs can access, some IPs that should be able to access everthing on the server and a list of IPs that shouldn't know the server exists. So I'll be using four zones.Configuring
There's a number of pre-defined zones in FirewallD, you can use firewall-cmd --list-all-zones, or you can just make up your own if you want. I like the names so I'll be using the internal, trusted and block zones. A zone needs an interface or source assigned to it to become active. In my example 192.168.200.0/24 will be internal addresses with access to DNS, 192.168.200.7 a trusted host will full server access, 192.168.200.2 a hacker to be blocked and http/https will be accessable to the public.
First remove the ssh service from the public zone (obviously don't lock yourself out of the server!)
# firewall-cmd --zone=public --remove-service=ssh
Then remove the extra services from the internal zone
# firewall-cmd --zone=internal --list-all
internal
target: default
icmp-block-inversion: no
interfaces:
sources:
services: cockpit dhcpv6-client mdns samba-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
# firewall-cmd --zone=internal --remove-service=ssh --remove-service=samba-client --remove-service=mdns --remove-service=dhcpv6-client --remove-service=cockpit
# firewall-cmd --runtime-to-permanent
Probably a good idea to check the other zones you plan to use for random other services as well and remove as needed.
You also might need to add services or want to see the names of services that are available. For service creation related commands you have to use the --permanent option, which means you'll need to reload FirewallD (firewall-cmd --reload) before the service you add will be useable, so be sure to save the running config firewall-cmd --runtime-to-permanent.
# firewall-cmd --get-services
# firewall-cmd --info-service=http
# firewall-cmd --permanent --new-service=platypusd
# firewall-cmd --permanent --service=platypusd --set-short=Platypus
# firewall-cmd --permanent --service=platypusd --add-port=5124/tcp
And finally, the example promised earlier:
# firewall-cmd --zone=public --add-service=http --add-service=https
# firewall-cmd --zone=internal --add-service=dns
# firewall-cmd --zone=internal --add-source=192.168.200.0/24
# firewall-cmd --zone=trusted --add-source=192.168.200.7
# firewall-cmd --zone=block --add-source=192.168.200.2
# firewall-cmd --runtime-to-permanent
Docker...
Sadly it seems like Docker is here to stay and it just won't play nice with firewalls! I have however found how to access the IP Tables stuff it exposes to lock down the services it exposes to only be visable to a few particular IP Addresses. Assuming I want 8.8.8.8 and 1.1.1.1 to access my docker service (which I have bound to a particular IP address on a eth2) here's what that looks like:
# firewall-cmd --direct --add-rule ipv4 filter DOCKER-USER 5 -i eth2 -s 8.8.8.8 -j ACCEPT
# firewall-cmd --direct --add-rule ipv4 filter DOCKER-USER 10 -i eth2 -s 1.1.1.1 -j ACCEPT
# firewall-cmd --direct --add-rule ipv4 filter DOCKER-USER 100 -i eth2 -j REJECT
You can read more about the DOCKER-USER group here: https://docs.docker.com/network/packet-filtering-firewalls/ You can use firewall-cmd --direct --get-all-rules to view the currently applied "direct" rule. Don't forget to save the changes when you get it all working!
Part 2 - Blocking Whole Countries
EDIT: I've found I have a huge issue with DDOS attacks from China. As the websites we host are only for local interest it's pretty unlikley someone in China would legitimately be trying to access them, so if you want to be able to block large chunks of the world (or just a bunch of blocks of IP addresses) come and read Part 2: https://nick.5i5.org/2021/09/13/FirewallD-Part2/.
-Nick