Skip to main content

Securing Webservers - FirewallD and fail2ban

TST, Hong Kong

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

Download net blocks:

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