Securing Webservers - FirewallD and fail2ban
FirewallD
FirewallD provides a dynamically managed firewall with support for network/firewall zones that defines the trust level of network connections or interfaces. It has support for IPv4, IPv6 firewall settings, ethernet bridges and IP sets. There is a separation of runtime and permanent configuration options. It also provides an interface for services or applications to add firewall rules directly. It has following features:
- Timed firewall rules
- IPv4 and IPv6 NAT support
- Firewall zones
- IP set support
- Simple log of denied packets
- Direct interface
- Lockdown: Whitelisting of applications that may modify the firewall
- Support for iptables, ip6tables, ebtables and ipset firewall backends
How to install FirewallD
Before installing FirewallD, please make sure you stop iptables and also make sure that iptables are not using or working anymore. To do so:
sudo systemctl stop iptables
sudo systemctl mask iptables
sudo systemctl status iptables
Ubuntu
sudo apt-get remove ufw
sudo apt-get install firewall-applet
CentOS
sudo yum install firewalld firewall-config -y
How to configure FirewallD
sudo systemctl enable --now firewalld
sudo systemctl status firewalld
Zones
Firewall-cmd uses zones as presets, giving you sane defaults to choose from. Doing this saves you from having to build a firewall from scratch. Zones apply to a network interface, so on a server with two ethernet interfaces, you may have one zone governing one ethernet interface, and a different zone governing the other.
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --get-zones
sudo firewall-cmd --get-default-zone
Setting Default Zone:
sudo firewall-cmd --set-default-zone=public
sudo firewall-cmd --reload
sudo firewall-cmd --get-default-zone
Assigning a zone to an interface:
firewall-cmd --change-interface ens3 --zone public --permanent
The interface is under control of NetworkManager, setting zone to 'public'.
success
Services
sudo firewall-cmd --get-services
Adding a Service to the Public Zone:
sudo firewall-cmd --zone=public --add-service=http
sudo firewall-cmd --zone=public --add-service=https
sudo firewall-cmd --zone=public --add-service=ftp
sudo firewall-cmd --zone=public --add-service=smtp
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --remove-service=ftp
sudo firewall-cmd --zone=public --remove-service=smtp
sudo firewall-cmd --reload
Add the --permanent
option to make your rules permanent.
Ports
Adding a Port to the Public Zone:
sudo firewall-cmd --permanent --zone=public --add-port=80/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --list-ports
sudo firewall-cmd --zone=public --list-all
sudo firewall-cmd --zone=public --remove-port=80/tcp
sudo firewall-cmd --reload
Adding multiple ports:
firewall-cmd --permanent --add-port={80/tcp,443/tcp,9200/tcp,5601/tcp,5044/tcp}
firewall-cmd --permanent --add-port=60000-61000/udp
firewall-cmd --reload
Add the --permanent
option to make your rules permanent.
Panic Mode
sudo firewall-cmd --panic-on
sudo firewall-cmd --panic-off
IP Address
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.4" accept'
sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.5" reject'
sudo firewall-cmd --reload
Add the --permanent
option to make your rules permanent.
Prevent Bruteforce SSH attacks
Reject new incoming ipv4 connections when more than 2 attempts per minute are made. It will also log a message about this:
firewall-cmd --add-rich-rule='rule family="ipv4" service name="ssh" log prefix="SSH Bruteforce:" level="warning" limit value="2/m" accept limit value="2/m"'
If you have both ipv4 and ipv6 configured you’ll probably want the more generic version:
firewall-cmd --add-rich-rule='rule service name="ssh" log prefix="SSH Bruteforce:" level="warning" limit value="2/m" accept limit value="2/m"'
Add the --permanent
option to make your rules permanent.
Create a Blacklist
firewall-cmd --permanent --new-ipset=blacklist --type=hash:net --option=family=inet --option=hashsize=4096 --option=maxelem=200000
–permanent
= use to make changes to the permanent configuration–new-ipset
= name of the new IP/net blacklist–type
= storage hash type, "net" is for subnets, while "ip" for individual ip addresses–option=family
= IPv4 or IPv6 network, inet is for IPv4–option=hashsize
= the initial hash size of the list–option=maxelem
= max number of elements
wget https://www.ipdeny.com/ipblocks/data/aggregated/ru-aggregated.zone
tar -vxzf all-zones.tar.gz
Populate the blacklist:
firewall-cmd --permanent --ipset=blacklist --add-entries-from-file=./ru-aggregated.zone
To add individual IP addresses or net blocks by yourself:
firewall-cmd --permanent --ipset=blacklist --add-entry=4.46.116.112
firewall-cmd --ipset=blacklist --add-entry=4.46.116.112
Redirect the blacklist to the drop zone
firewall-cmd --permanent --zone=drop --add-source=ipset:blacklist
firewall-cmd --reload
Block and Enable ICMP
First, check the type of icmp we are using with below command.
firewall-cmd --get-icmptypes
To add icmp block on any zone, you can use the following command. For example, here I am going to add icmp block on external zone, before blocking, just do a icmp ping to confirm the status of icmp block.
firewall-cmd --zone=public --query-icmp-block=echo-reply
If you get "no", that means there isn’t any icmp block applied, let’s enable (block) icmp.
firewall-cmd --zone=public --add-icmp-block=echo-reply
Lockdown Rules
It’s possible to change the firewalld rules by any local applications, which have the root privileges. To avoid making changes to firewalld rules, we have to put a lock-down in ‘firewalld.conf‘ file. This mostly used to protect the firewalld from any unwanted rules changes by any applications.
nano /etc/firewalld/firewalld.conf
Lockdown=yes
firewall-cmd --reload
firewall-cmd --query-lockdown
To On/Off lockdown mode, use the following combination.
firewall-cmd --lockdown-on
firewall-cmd --lockdown-off
fail2ban-firewalld
Configure fail2ban (see below) to block hosts via firewalld.
fail2ban
fail2ban is a daemon to ban hosts that cause multiple authentication errors.
fail2ban will monitor the SystemD journal to look for failed authentication attempts for whichever jails have been enabled. After the number of failed attempts specified it will add a firewall rule to block that specific IP address for an amount of time configured.
Start by installing the package on your system - Debian, Ubuntu or on Centos through EPEL.
Once installed the next step is to configure a jail (a service you want to monitor and ban at whatever thresholds you’ve set). By default IPs are banned for 1 hour. The best practice is to override the system defaults using _.local files instead of directly modifying the _.config files:
# cat /etc/fail2ban/jail.local
[DEFAULT]
# "bantime" is the number of seconds that a host is banned.
bantime = 1d
# A host is banned if it has generated "maxretry" during the last "findtime"
findtime = 1h
# "maxretry" is the number of failures before a host get banned.
maxretry = 5
After 5 attempts within the last hour the IP will be blocked for 1 day.
The next step is to configure a jail. In this tutorial sshd is shown but the steps are more or less the same for other services. Create a configuration file inside /etc/fail2ban/jail.d
:
# cat /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
Next enable and start the fail2ban service.
sudo systemctl enable --now fail2ban
sudo systemctl status fail2ban
to check the status of fail2ban and make sure the jail is enabled enter:
sudo fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: sshd
sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 8
| |- Total failed: 4399
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
|- Currently banned: 101
|- Total banned: 684
`- Banned IP list: ...
sudo tail -f /var/log/fail2ban.log
Check IP address geo location and add country ban lists where necessary whois ip-addrss | grep -i country
.
Blacklisting Script and Configuration
The following script to automate the process as much as possible:
#!/bin/bash
# Based on the below article
# https://www.linode.com/community/questions/11143/top-tip-firewalld-and-ipset-country-blacklist
# Source the blacklisted countries from the configuration file
. /etc/blacklist-by-country
# Create a temporary working directory
ipdeny_tmp_dir=$(mktemp -d -t blacklist-XXXXXXXXXX)
pushd $ipdeny_tmp_dir
# Download the latest network addresses by country file
curl -LO http://www.ipdeny.com/ipblocks/data/countries/all-zones.tar.gz
tar xf all-zones.tar.gz
# For updates, remove the ipset blacklist and recreate
if firewall-cmd -q --zone=drop --query-source=ipset:blacklist; then
firewall-cmd -q --permanent --delete-ipset=blacklist
fi
# Create the ipset blacklist which accepts both IP addresses and networks
firewall-cmd -q --permanent --new-ipset=blacklist --type=hash:net \
--option=family=inet --option=hashsize=4096 --option=maxelem=200000 \
--set-description="An ipset list of networks or ips to be dropped."
# Add the address ranges by country per ipdeny.com to the blacklist
for country in $countries; do
firewall-cmd -q --permanent --ipset=blacklist \
--add-entries-from-file=./$country.zone && \
echo "Added $country to blacklist ipset."
done
# Block individual IPs if the configuration file exists and is not empty
if [ -s "/etc/blacklist-by-ip" ]; then
echo "Adding IPs blacklists."
firewall-cmd -q --permanent --ipset=blacklist \
--add-entries-from-file=/etc/blacklist-by-ip && \
echo "Added IPs to blacklist ipset."
fi
# Add the blacklist ipset to the drop zone if not already setup
if firewall-cmd -q --zone=drop --query-source=ipset:blacklist; then
echo "Blacklist already in firewalld drop zone."
else
echo "Adding ipset blacklist to firewalld drop zone."
firewall-cmd --permanent --zone=drop --add-source=ipset:blacklist
fi
firewall-cmd -q --reload
popd
rm -rf $ipdeny_tmp_dir
This should be installed to /usr/local/sbin
and don’t forget to make it executable!
sudo chmod +x /usr/local/sbin/firewalld-blacklist
Then create a configure file: /etc/blacklist-by-country
:
# Which countries should be blocked?
# Use the two letter designation separated by a space.
countries=""
And another configuration file /etc/blacklist-by-ip
, which is just one IP per line without any additional formatting.
For this example 10 random countries were selected from the ipdeny zones:
# ls | shuf -n 10 | sed "s/\.zone//g" | tr '\n' ' '
nl ee ie pk is sv na om gp bn
Now as long as at least one country has been added to the config file it’s ready to run!
sudo firewalld-blacklist
sudo firewall-cmd --info-zone=drop
sudo firewall-cmd --info-ipset=blacklist | less
See also - auto blacklist updates: https://pagure.io/firewalld-blacklist/tree/master
download the service file and timer to /etc/systemd/system/
and enable the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now firewalld-blacklist.timer