.. _Linux_firewall_configuration:
===============================================
Linux firewall and SSH protection configuration
===============================================
.. Contents:: Contents of this page:
:depth: 2
The Linux kernel's built-in firewall function (netfilter_ packet filtering framework) is administered by the Linux firewalld_.
.. _iptables: http://en.wikipedia.org/wiki/Iptables
.. _netfilter: https://en.wikipedia.org/wiki/Netfilter
General information about the concept of a firewall_.
.. _firewall: http://en.wikipedia.org/wiki/Firewall_%28computing%29
The documentation on this page includes:
* IP_sets_ for handling large numbers of subnets.
This is useful for special treatment of entire countries or large ISPs, for example, by firewalld_.
* SSH brute force attacks handled by firewalld_.
* SSH failed logins causing blacklisting using sshblack_ and firewalld_.
RHEL7/CentOS7 and Fedora firewalld
==================================
A nice introduction is `RHEL7: How to get started with Firewalld `_.
The default firewall service is now firewalld_.
The dynamic firewall daemon firewalld_ provides a dynamically managed firewall with support for network “zones” to assign a level of trust to a network and its associated connections and interfaces.
See `Introduction to firewalld `_.
.. _firewalld: https://fedoraproject.org/wiki/FirewallD
A graphical configuration tool is used to configure firewalld_::
firewall-config
A command line (CLI) configuration tool::
firewall-cmd
may also used to configure firewalld_.
To make any new rule permanent, run the command with the ``--permanent`` flag.
See the manual firewall-cmd_ for further information.
The firewalld_ configuration files are in the directory ``/etc/firewalld/zones/`` where XML files contain the firewall rules.
See firewalld_ log messages in::
/var/log/firewalld
Whitelisting IP addresses with firewalld
----------------------------------------
It is important to be able to whitelist internal IP addresses.
Insert a firewalld_ direct_rule_ so that any incoming source IP packet (src) from a specific IP subnet gets accepted, for example::
firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -s 192.168.1.0/24 -j ACCEPT
To make the rule **permanent**, run the command with the ``--permanent`` flag.
To open a specific port NNNN::
firewall-cmd --zone=public --add-port=NNNN/tcp --permanent
Then remember to reload the firewall for changes to take effect::
firewall-cmd --reload
Here the value 0 is the priority of the rule (smaller numbers have higher priority, so 0 is the highest priority).
Make sure that other direct rules (which might interfere with the whitelist) have a lower priority.
List the rules by::
firewall-cmd --permanent --direct --get-all-rules
Iptables IP sets and country blocking
=====================================
IP_sets_ are a framework inside the Linux 2.4.x and 2.6.x kernel which can be used efficiently to create firewall rules for large numbers of IP subnets.
Depending on the type, currently an IP set may store IP addresses, (TCP/UDP) port numbers or IP addresses with MAC addresses in a way, which ensures lightning speed when matching an entry against a set.
The IP_sets_ can be administered by the ipset_ utility.
.. _IP_sets: http://ipset.netfilter.org/
.. _ipset: http://ipset.netfilter.org/ipset.man.html
Install IP_sets_ by::
yum install ipset # RHEL/CentOS 6 and 7
Read the ``man ipset`` manual page.
Country IP blocks
-----------------
There are providers of country IP blocks available for download and eventual inclusion into IP_sets_ block lists:
* `Country IP Blocks `_ (up-to-date service commercially available).
* `Block Visitors by Country `_.
* IPdeny_ (contains all country zone files)
* `freegeoip.net `_ is a public REST API for searching geolocation of IP addresses and host names.
.. _IPdeny: http://www.ipdeny.com/ipblocks
For example,
the IP blocks for China may start with::
1.0.1.0/24
1.0.2.0/23
1.0.8.0/21
...
Country **IPv6** blocks may be found at IPdeny_ or https://www.countryipblocks.net/ipv6_cidr.php.
For discussions see:
* `How to block countries with ipdeny IP country blocks, ipset and iptables on EL6 `_.
* `Allow only a country with iptables `_.
Create a country geoblock ipset
...............................
We use the IPdeny_ *all countries* zone files and create an IP set which we name *geoblock*.
We offer a simple script which automatically creates an ipset_ *geoblock* data file.
Download this :download:`Makefile ` to a new directory.
Edit the *COUNTRYLIST* list variable according to your needs, then run::
make
to create the *IPSET_DATA* file ``/etc/sysconfig/ipset``.
Tor exit node blocking
----------------------
If you wish to block port-scanning from Tor_ **exit nodes** then you may download the dynamically updated torbulkexitlist_ file.
The torbulkexitlist_ file contains a list of IP-addresses which may be considered as a "country named Tor" and simply added as another zone file like in the above country blocks, for example ``tor.zone``.
.. _Tor: https://en.wikipedia.org/wiki/Tor_(network)
.. _torbulkexitlist: https://check.torproject.org/torbulkexitlist
Using ipsets in firewalld on RHEL/CentOS 7.3 and above
======================================================
According to the `RHEL 7.3 Release Notes `_,
firewalld_ has been upgraded from version 0.3.9 to 0.4.3:
* ipset_ support: firewalld_ now supports ipsets used as zone sources, within rich and direct rules. (`BZ#1302802 `_)
The ipset_ must be configured directly in firewalld_.
Do **not** try to run the Systemd_ service ``ipset`` service together with the firewalld_ 0.4 ``firewalld`` service (as we did previously).
See manual pages for firewalld.ipset_, firewalld.zone_ and firewall-cmd_.
.. _firewalld.ipset: http://www.firewalld.org/documentation/man-pages/firewalld.ipset.html
.. _firewalld.zone: http://www.firewalld.org/documentation/man-pages/firewalld.zone.html
.. _firewall-cmd: http://www.firewalld.org/documentation/man-pages/firewall-cmd.html
.. _Systemd: https://en.wikipedia.org/wiki/Systemd
The procedure for RHEL/CentOS 7.3 is:
* As a prerequisite create the data file ``/etc/sysconfig/ipset`` as shown above.
* Create a new ipset called ``geoblock``, optionally specifying a larger maximum number of elements in the list::
firewall-cmd --permanent --new-ipset=geoblock --type=hash:net [ --option=hashsize=65536 --option=maxelem=524288 ]
See firewall-cmd_ for the ``--option`` flags.
* Add ipset ``geoblock`` to the zone named drop_ ::
firewall-cmd --permanent --zone=drop --add-source=ipset:geoblock
* Now load ipset_ data::
firewall-cmd --permanent --ipset=geoblock --add-entries-from-file=/etc/sysconfig/ipset
* Reload the firewalld_ service::
firewall-cmd --reload
See the manual firewall-cmd_ section *IPSet Options* for a list of ipset_ subcommands.
To list all ipset_ entries::
firewall-cmd --permanent --ipset=geoblock --get-entries
.. _drop: https://fedoraproject.org/wiki/Firewalld#drop
Using ipsets in firewalld on RHEL/CentOS 8
==========================================
In RHEL/CentOS 8 the ipset_ setup works similarly to the above description for 7.3 (and above).
There is an important problem, however, in CentOS 8.3:
* **Overlapping network ranges in the IPsets causes firewalld failures**.
This is described in Bugzilla_1836571_ *firewalld startup failure: COMMAND_FAILED: 'python-nftables' failed: internal:0:0-0: Error: Could not process rule: File exists* (reported on Fedora FC32).
See error messages in `Comment 12 `_.
The current status (Dec. 2020) is:
1. firewalld needs to use the "auto-merge" feature of sets to a allow element coalescing.
2. The nftables_ "auto-merge" feature was introduced in `release 0.8.2 `_.
**Conclusion: Do not (accidentally) use overlapping IPsets in CentOS 8.3.**
.. _Bugzilla_1836571: https://bugzilla.redhat.com/show_bug.cgi?id=1836571
.. _nftables: https://wiki.nftables.org/wiki-nftables/index.php/Main_Page
Aggregate CIDR addresses
------------------------
To circumvent the CentOS 8 Firewalld_ failures in case of overlapping IP ranges, there exist several tools:
1. Python tool aggregate6_.
First install prerequisites::
dnf install gcc platform-python-devel
Then install by::
pip3 install aggregate6 --user
Usage::
~/.local/bin/aggregate6 --help
2. Perl script aggregate-cidr-addresses_ which takes a list of CIDR address blocks and combine them without overlaps.
We have a copy of the :download:`aggregate-cidr-addresses.pl ` file, download it to ``/usr/local/bin/``.
Install prerequisite Perl modules::
dnf install perl-Net-IP
Usage::
cat file1 file2 ... fileN | aggregate-cidr-addresses.pl
.. _aggregate6: https://github.com/job/aggregate6
.. _aggregate-cidr-addresses: https://gist.github.com/denji/17e30bddb9ce9e50294a
Testing the ipset service
-------------------------
Testing the ipset_:
* List all the sets in ipset_::
ipset save
* Test whether an IP address (here: 111.222.33.44) is in a given IPset *geoblock*::
ipset test geoblock 111.222.33.44
Flush the ipset_ kernel table for this IPset::
ipset flush geoblock
This may be required if you delete ipset_ entries - subsequently you should restart the IPset service.
Enabling the ipset service on RHEL/CentOS 7.0, 7.1 and 7.2
==========================================================
RHEL/CentOS 7.0, 7.1 and 7.2 (which use Systemd_) do **not** have a method for starting an ipset_ service.
This has been fixed in later releases.
A workaround exists for enabling the *ipset service* on RHEL7/CentOS7 using scripts from Fedora:
* Download the Fedora 22 src rpm from http://rpm.pbone.net/index.php3/stat/4/idpl/28726591/dir/fedora_other/com/ipset-6.22-1.fc22.i686.rpm.html
* Install the src rpm and copy the ipset service files to the system::
mkdir /usr/libexec/ipset /etc/ipset
rpm -ivh ipset-6.22-1.fc22.src.rpm
cd ~/rpmbuild/SOURCES/
cp -p ipset.start-stop /usr/libexec/ipset/ipset.start-stop
cp -p ipset.service /usr/lib/systemd/system/ipset.service
chmod 755 /usr/lib/systemd/system/ipset.service /usr/libexec/ipset/ipset.start-stop
See below for how to start the *ipset service*.
Using ipset with firewalld (RHEL7/CentOS7 and Fedora)
-----------------------------------------------------
Insert an firewalld_ direct_rule_ so that any incoming source IP packet (src) gets matched against the set of IP addresses in geoblock::
firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 1 -m set --match-set geoblock src -j DROP
.. _direct_rule: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Using_Firewalls.html#sec-Understanding_the_Direct_Interface
Here the value *1* is the *priority* of the rule (smaller numbers have higher priority, so 0 is the highest priority).
Verify the new rule::
firewall-cmd --direct --get-all-rules | grep geoblock
If testing is OK, you can make this rule permanent::
firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT_direct 1 -m set --match-set geoblock src -j DROP
The file ``/etc/firewalld/direct.xml`` will contain this direct_rule_.
Blocking SSH brute force attacks with firewalld (RHEL7/CentOS7 and Fedora)
==========================================================================
See these pages:
* http://itnotesandscribblings.blogspot.com/2014/08/firewalld-adding-services-and-direct.html
* http://serverfault.com/questions/683671/is-there-a-way-to-rate-limit-connection-attempts-with-firewalld
These commands may be tried::
firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 2 -p tcp --dport 22 -m state --state NEW -m recent --set
firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 3 -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 30 --hitcount 4 -j REJECT --reject-with tcp-reset
Note: Logging to syslog is missing from this setup.
Apparently firewalld_ is **incapable of logging rejected or accepted packets**, see:
* https://bluehatrecord.wordpress.com/2014/04/17/logging-packet-drops-in-firewalld/
* https://ask.fedoraproject.org/en/question/45014/how-to-log-drops-and-rejects-by-firewalld/
When tested OK, add the ``--permanent`` flag to the above commands.
sshblack -- Automatically BLACKLIST SSH attackers
=================================================
The sshblack_ script is a real-time security tool for secure shell (ssh).
It monitors -nix log files for suspicious activity and reacts appropriately to aggressive attackers by adding them to a "blacklist"
created using various firewalling tools -- such as iptables_ -- available in most modern versions of Unix and Linux.
The blacklist is simply a list of source IP addresses that are prohibited from making ssh connections to the protected host.
Once a predetermined amount of time has passed, the offending IP address is removed from the blacklist.
**NOTICE:** Since modern RHEL (and clones) as well as Fedora
use firewalld_ in stead of iptables_, the sshblack_ version 2.8.1 from the web-site **does not work**.
All ``iptables`` commands in the sshblack_ scripts need to be replaced by similar ``firewall-cmd`` commands.
See also this page `Further Securing OpenSuSE 11.1 Against SSH Script Attacks `_.
.. _sshblack: http://www.pettingers.org/code/sshblack.html
Installing and configuring sshblack
-----------------------------------
Unpack the sshblack_ tar-ball,
a local copy is here: :download:`sshblackv281.tar.gz `.
Consult the sshblack_install_ instructions,
a local copy is here: :download:`sshblack-install.txt `.
.. _sshblack_install: http://www.pettingers.org/media/sshblack-install.txt
Copy the executable files to a standard executable directory::
cp sshblack.pl bl unbl list unlist /usr/local/sbin/
Configure the ``sshblack.pl`` script, at a minimum define the sshblack_Whitelist_ for your local network by tailoring this line::
my($LOCALNET) = '^(?:127\.0\.0\.1|192\.168\.0)';
.. _sshblack_Whitelist: http://www.pettingers.org/code/sshblack-whitelist.html
For security reasons the sshblack_ *CACHE* should be in a private directory rather than the world-writable volatile directory ``/var/tmp``, for example::
my($CACHE) = '/var/lib/sshblack/ssh-blacklist-pending';
**Note:** The same *CACHE* variable must also be defined in the helper scripts ``list`` and ``unlist``.
Create the private directory (RHEL/CentOS conventional location)::
mkdir -v -p /var/lib/sshblack
Other configurable parameters include::
my($REASONS) = '(Failed password|Failed none|Invalid user)';
my($AGEOUT) = 600;
my($RELEASEDAYS) = 4;
my($CHECK) = 300;
my($MAXHITS) = 4;
my($DOSBAIL) = 200;
my($CHATTY) = 1;
my($EMAILME) = 1;
my($NOTIFY) = 'root';
Some malformed SSH attacks generate log entries like::
sshd[30179]: Received disconnect from 206.191.151.226: 11: Bye Bye [preauth]
To blacklist such IPs add an additional rule::
my($REASONS) = '(Failed password|Failed none|Invalid user|Bye Bye \[preauth\])';
See also the sshblack_config_ page for additional advice.
.. _sshblack_config: http://www.pettingers.org/code/sshblack-config.html
sshblack logfiles
-----------------
The sshblack_ logs to this file, so make sure it exists::
touch /var/log/sshblacklisting
It is a good idea to rotate this logfile on a weekly basis, so create the file ``/etc/logrotate.d/sshblacklisting`` with the contents::
# Log rotation configuration for SSH blacklisting
/var/log/sshblacklisting {
missingok
notifempty
weekly
}
Configuring the sshblack service
---------------------------------
On RHEL (and clones) as well as Fedora Linux you should set up a Systemd_ startup script (and not run the ``sshblack.pl`` command manually).
EL8/EL9/Fedora and sshblack
..................................
A Systemd_ service file :download:`sshblack.service ` must be installed::
cp sshblack.service /etc/systemd/system/
chmod 755 /etc/systemd/system/sshblack.service
systemctl enable sshblack.service
CentOS7/RHEL7 and sshblack
................................
An EL7-specific startup script :download:`init-sshblack-el7 `
must be used for RHEL7/CentOS7/Fedora with Systemd_ and firewalld_.
Install a Systemd_ service file :download:`sshblack.service-el7 `.
Now add the service and create the private sshblack_ directory::
mkdir /usr/libexec/sshblack
cp init-sshblack-el7 /usr/libexec/sshblack/init-sshblack
cp sshblack.service-el7 /etc/systemd/system/sshblack.service
chmod 755 /usr/libexec/sshblack/init-sshblack /etc/systemd/system/sshblack.service
systemctl enable sshblack.service
Configure a firewalld chain
................................
Create a ``BLACKLIST`` chain rule::
firewall-cmd --permanent --direct --add-chain ipv4 filter BLACKLIST
Then make all new connections to port 22 (SSH) jump to the ``BLACKLIST`` chain::
firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 7 -p tcp --dport 22 -m state --state NEW -j BLACKLIST
It is possible to drop packets from specific IP-addresses and subnets using *rich rules* like::
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='192.168.0.11' drop"
but we don't use this yet.
Starting the sshblack service
----------------------------------
The sshblack_ daemon must be started::
systemctl start sshblack.service
There are some useful sshblack_notes_ explaining some additional useful commands
(a local copy is here :download:`sshblack-notes.html `):
* list -- manually adds an IP address to the blacklist and modifies the $CACHE file accordingly
* unlist -- manually remove an IP address from the blacklist and the $CACHE file
* bl -- a manual blacklisting tool (one liner that modifies the iptables_ configuration only)
* unbl -- a manual UNblacklisting tool (one liner that modifies the iptables_ configuration only)
* iptables-setup -- a few shell commands to set up the iptables_ chains if you don't want to do it manually.
.. _sshblack_notes: http://www.pettingers.org/code/sshblack-notes.html
If you want a list of blacklisted IP-addresses, display the ``BLACKLIST`` chain::
/usr/bin/firewall-cmd --direct --get-all-rules | grep BLACKLIST
Checkpoint and restart of sshblack
----------------------------------
The ``sshblack.pl`` script doesn't have any checkpoint/restart feature, so preservation of ``BLACKLIST`` state across restarts must be done manually.
See the `Checkpoint and Restart discussion `_.
The script :download:`sshblack-save-state ` should be downloaded to ``/usr/local/sbin/``
and a new crontab rule should be added to run it every 5 minutes::
# Save the firewalld chain BLACKLIST DROP lines for restarting sshblack
*/5 * * * * /usr/local/sbin/sshblack-save-state
This script will create a restart script ``/var/lib/sshblack/restart.sh`` which should also be executed at system boot time using a crontab rule::
@reboot /var/lib/sshblack/restart.sh
This command prints commands to recreate the ``BLACKLIST`` from the ``BLACKLIST`` CACHE in case it is lost::
awk -F, '{if ($3 > 4) printf("firewall-cmd --direct --add-rule ipv4 filter BLACKLIST 0 -s %s -j DROP\n", $1)}' < /var/lib/sshblack/ssh-blacklist-pending
Firewall and NFS
================
Open up the NFS client's firewall to *all* traffic from the specific NFS-server(s).
In general this is accomplished by this command::
firewall-cmd --direct --add-rule ipv4 filter INPUT_direct 0 -s -j ACCEPT
Summary of RHEL7/CentOS7 and Fedora firewalld settings
======================================================
Summarizing the direct_rule_ commands in the above will result in permanent firewalld_ settings in the file ``/etc/firewalld/direct.xml``::
-s 192.168.1.0/24 -j ACCEPT
-m set --match-set geoblock src -j DROP
-p tcp --dport 22 -m state --state NEW -m recent --set
-p tcp --dport 22 -m state --state NEW -m recent --update --seconds 30 --hitcount 4 -j REJECT --reject-with tcp-reset
Here we have adjusted the *priority* values so that the most important rules have the highest priority (smaller numbers have higher priority, so 0 is the highest priority).