Some hosts can use fail2ban's nginx-botsearch filter to ban anyone
making requests to non-existent files like wp-login.php. There is
no reason to request such files naively and anyone found doing so
can be banned immediately.
In theory I should report them to AbuseIPDB.com, but that will take
a little more wiring up.
For now I am still manually updating this, as we can only hit their
API five times per day, so it is not possible to have each host get
the list themselves every day with our one API key.
This adds Abuse.sh's list of IPs using blacklisted SSL certificates
to nftables. These IPs are high confidence indicators of compromise
and we should not route them. The list is updated daily by a systemd
timer.
See: https://sslbl.abuse.ch/blacklist/
We should only try to start the nftables service after we finish
copying all the config files just in case there is some unclean
state in one of them. On a first run this shouldn't matter, but
after nftables and some abuse list update scripts have run this
can happen (mostly in testing!).
cron-apt updates the system against the security-only databases at
night so many packages are "missing" unless you run apt update. We
need to update the cache on all apt tasks actually because I might
be running them by their tag and they currently only get updated at
the beginning of the playbook.
I will try using nftables directly instead of via firewalld as of
Debian 11 as it is the replacement for the iptables/ipset stack in
recent years and is easier to work with.
This also includes a systemd service, timer, and script to update
the spamhaus DROP lists as nftables sets.
Still need to add fail2ban support.
This configures the recommended DROP, EDROP, and DROPv6 lists from
Spamhaus as ipsets in firewalld. First we copy an empty placeholder
ipset to seed firewalld, then we use a shell script to download the
real lists and activate them. The same shell script is run daily as
a service (update-spamhaus-lists.service) by a systemd timer.
I am strictly avoiding any direct ipset commands here because I want
to make sure that this works on older hosts where ipsets is used as
well as newer hosts that have moved to nftables such as Ubuntu 20.04.
So far I have tested this on Ubuntu 16.04, 18.04, and 20.04, but ev-
entually I need to abstract the tasks and run them on CentOS 7+ as
well.
See: https://www.spamhaus.org/drop/
This comes from the AbuseIPDB with a confidence level of 95%. I use
the following command to download and sort the IPs:
$ curl -G https://api.abuseipdb.com/api/v2/blacklist -d \
confidenceMinimum=95 -H "Key: $ABUSEIPDB_API_KEY" \
-H "Accept: text/plain" | sort | sed -e '/:/w /tmp/ipv6.txt' \
-e '/:/d' > /tmp/ipv4.txt
I manually add the XML formatting to each file and run them through
tidy:
$ tidy -xml -utf8 -m -iq -w 0 roles/common/files/abusers-ipv4.xml
$ tidy -xml -utf8 -m -iq -w 0 roles/common/files/abusers-ipv6.xml
Older Ubuntus originally didn't use a persistent journal, which was
somewhat of a surprise when looking at logs after a few months. Now
this does not seem to be an issue since Ubuntu 18.04. As for CentOS
I do not use that distro here so I don't need to care.
The nftables backend should be more performant and flexible. I had
been planning to use it on Ubuntu 18.04 and Debian 10 as well, but
there were issues with the specific versions used in those distros.
See: https://firewalld.org/2018/07/nftables-backend
We never used this simple firewall utility and in at least one case
a user on the server tried to use it and messed up the rules I had
set via firewalld.
For some reason the nftables set support in firewalld doesn't seem
to be working. I see that sets (aka ipsets in nftables lingo) are
created, but they are empty. For now I will just leave these tasks
as they are to revert the behavior on current hosts (should do no
change on new installed, as the regexp won't match).
This is active banning of IPs that are brute forcing login attempts
to SSH, versus the passive banning of 10,000 abusive IPs from the
abuseipdb.com blacklist. For now I am banning IPs that fail to log
in successfully more than twelve times in a one-hour period, but
these settings might change, and I can override them at the group
and host level if needed.
Currently this works for CentOS 7, Ubuntu 16.04, and Ubuntu 18.04,
with minor differences in the systemd configuration due to older
versions on some distributions.
You can see the status of the jail like this:
# fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- Journal matches: _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 106.13.112.20
You can unban IPs like this:
# fail2ban-client set sshd unbanip 106.13.112.20
Seems to work around an issue when firewalld is using the nftables
backend with iptables 1.8.2 on Debian 10. Alternatively I could go
back to using the iptables backend... hmm.
See: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=914694
This causes problems every time I re-run the Ansible tag because the
nightly apt security uses a different sources.list and the indexes
are then missing buster-backports. I could add a cache update to the
task, but actually I think the original bug I was trying to solve is
finally fixed, and I'm going to switch to nftables anyways.
This uses the ipsets feature of the Linux kernel to create lists of
IPs (though could be MACs, IP:port, etc) that we can block via the
existing firewalld zone we are already using. In my testing it works
on CentOS 7, Ubuntu 16.04, and Ubuntu 18.04.
The list of abusive IPs currently comes from HPC's systemd journal,
where I filtered for hosts that had attempted and failed to log in
over 100 times. The list is formatted with tidy, for example:
$ tidy -xml -iq -m -w 0 roles/common/files/abusers-ipv4.xml
See: https://firewalld.org/2015/12/ipset-support
There is a bug in iptables 1.8.2 in Debian 10 "Buster" that causes
firewalld to fail when restoring rules. The bug has been fixed in
iptables 1.8.3, which is currently in buster-backports.
See: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=914694