roles/common: Use Abuse.ch's SSL Blacklist in nftables

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/
This commit is contained in:
Alan Orth 2021-07-29 10:16:00 +03:00
parent cba2a7a996
commit 8dd7663b3c
Signed by: alanorth
GPG Key ID: 0FB860CC9C45B1B9
7 changed files with 146 additions and 11 deletions

View File

@ -0,0 +1,5 @@
#!/usr/sbin/nft -f
define ABUSECH_IPV4 = {
192.168.254.254
}

View File

@ -0,0 +1,27 @@
[Unit]
Description=Update Abuse.ch SSL Blacklist IPs
# This service will fail if nftables is not running so we use Requires to make
# sure that nftables is started.
Requires=nftables.service
# Make sure the network is up and nftables is started
After=network-online.target nftables.service
Wants=network-online.target update-abusech-nftables.timer
[Service]
# https://www.ctrl.blog/entry/systemd-service-hardening.html
# Doesn't need access to /home or /root
ProtectHome=true
# Possibly only works on Ubuntu 18.04+
ProtectKernelTunables=true
ProtectSystem=full
# Newer systemd can use ReadWritePaths to list files, but this works everywhere
ReadWriteDirectories=/etc/nftables
PrivateTmp=true
WorkingDirectory=/var/tmp
SyslogIdentifier=update-abusech-nftables
ExecStart=/usr/bin/flock -x update-abusech-nftables.lck \
/usr/local/bin/update-abusech-nftables.sh
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,63 @@
#!/usr/bin/env bash
#
# update-abuseipdb-nftables.sh v0.0.1
#
# Download IP addresses seen using a blacklisted SSL certificate and load them
# into nftables sets. As of 2021-07-28 these appear to only be IPv4.
#
# See: https://sslbl.abuse.ch/blacklist
#
# Copyright (C) 2021 Alan Orth
#
# SPDX-License-Identifier: GPL-3.0-only
# Exit on first error
set -o errexit
abusech_ipv4_set_path=/etc/nftables/abusech-ipv4.nft
abusech_list_temp=$(mktemp)
echo "Downloading Abuse.sh SSL Blacklist IPs"
abusech_response=$(curl -s -G -w "%{http_code}\n" https://sslbl.abuse.ch/blacklist/sslipblacklist.txt --output "$abusech_list_temp")
if [[ $abusech_response -ne 200 ]]; then
echo "Abuse.ch responded: HTTP $abusech_response"
exit 1
fi
if [[ -f "$abusech_list_temp" ]]; then
echo "Processing IPv4 list"
abusech_ipv4_list_temp=$(mktemp)
abusech_ipv4_set_temp=$(mktemp)
# Remove comments, DOS carriage returns, and IPv6 addresses (even though
# Abuse.ch seems to only have IPv4 addresses, let's not break our shit on
# that assumption some time down the line).
sed -e '/#/d' -e 's/ //' -e '/:/d' "$abusech_list_temp" > "$abusech_ipv4_list_temp"
echo "Building abusech-ipv4 set"
cat << NFT_HEAD > "$abusech_ipv4_set_temp"
#!/usr/sbin/nft -f
define ABUSECH_IPV4 = {
NFT_HEAD
while read -r network; do
# nftables doesn't mind if the last element in the set has a trailing
# comma so we don't need to do anything special here.
echo "$network," >> "$abusech_ipv4_set_temp"
done < $abusech_ipv4_list_temp
echo "}" >> "$abusech_ipv4_set_temp"
install -m 0600 "$abusech_ipv4_set_temp" "$abusech_ipv4_set_path"
rm -f "$abusech_list_temp" "$abusech_ipv4_list_temp" "$abusech_ipv4_set_temp"
fi
echo "Reloading nftables"
# The abusech nftables sets are included by nftables.conf
/usr/sbin/nft -f /etc/nftables.conf

View File

@ -0,0 +1,12 @@
[Unit]
Description=Update Abuse.ch SSL Blacklist IPs
[Timer]
# Once a day at midnight
OnCalendar=*-*-* 00:00:00
# Add a random delay of 03600 seconds
RandomizedDelaySec=3600
Persistent=true
[Install]
WantedBy=timers.target

View File

@ -39,6 +39,7 @@
loop: loop:
- spamhaus-ipv4.nft - spamhaus-ipv4.nft
- spamhaus-ipv6.nft - spamhaus-ipv6.nft
- abusech-ipv4.nft
notify: notify:
- reload nftables - reload nftables
@ -102,20 +103,23 @@
loop: loop:
- update-spamhaus-nftables.sh - update-spamhaus-nftables.sh
- aggregate-cidr-addresses.pl - aggregate-cidr-addresses.pl
- update-abusech-nftables.sh
- name: Copy Spamhaus nftables systemd units - name: Copy nftables systemd units
when: ansible_distribution_version is version('11', '>=') when: ansible_distribution_version is version('11', '>=')
copy: src={{ item }} dest=/etc/systemd/system/{{ item }} mode=0644 owner=root group=root copy: src={{ item }} dest=/etc/systemd/system/{{ item }} mode=0644 owner=root group=root
loop: loop:
- update-spamhaus-nftables.service - update-spamhaus-nftables.service
- update-spamhaus-nftables.timer - update-spamhaus-nftables.timer
register: spamhaus_nftables_systemd_units - update-abusech-nftables.service
- update-abusech-nftables.timer
register: nftables_systemd_units
# need to reload to pick up service/timer/environment changes # need to reload to pick up service/timer/environment changes
- name: Reload systemd daemon - name: Reload systemd daemon
systemd: daemon_reload=yes systemd: daemon_reload=yes
when: spamhaus_firewalld_systemd_units is changed or when: spamhaus_firewalld_systemd_units is changed or
spamhaus_nftables_systemd_units is changed nftables_systemd_units is changed
- name: Start and enable Spamhaus firewalld update timer - name: Start and enable Spamhaus firewalld update timer
when: ansible_distribution_version is version('10', '<=') when: ansible_distribution_version is version('10', '<=')
@ -123,9 +127,12 @@
notify: notify:
- restart firewalld - restart firewalld
- name: Start and enable Spamhaus nftables update timer - name: Start and enable nftables update timers
when: ansible_distribution_version is version('11', '>=') when: ansible_distribution_version is version('11', '>=')
systemd: name=update-spamhaus-nftables.timer state=started enabled=yes systemd: name={{ item }} state=started enabled=yes
loop:
- update-spamhaus-nftables.timer
- update-abusech-nftables.timer
- name: Start and enable nftables - name: Start and enable nftables
when: ansible_distribution_major_version is version('11', '>=') when: ansible_distribution_major_version is version('11', '>=')

View File

@ -45,6 +45,7 @@
loop: loop:
- spamhaus-ipv4.nft - spamhaus-ipv4.nft
- spamhaus-ipv6.nft - spamhaus-ipv6.nft
- abusech-ipv4.nft
notify: notify:
- reload nftables - reload nftables
@ -81,26 +82,29 @@
- update-spamhaus-lists.timer - update-spamhaus-lists.timer
register: spamhaus_firewalld_systemd_units register: spamhaus_firewalld_systemd_units
- name: Copy Spamhaus nftables update scripts - name: Copy nftables update scripts
when: ansible_distribution_version is version('20.04', '>=') when: ansible_distribution_version is version('20.04', '>=')
copy: src={{ item }} dest=/usr/local/bin/{{ item }} mode=0755 owner=root group=root copy: src={{ item }} dest=/usr/local/bin/{{ item }} mode=0755 owner=root group=root
loop: loop:
- update-spamhaus-nftables.sh - update-spamhaus-nftables.sh
- aggregate-cidr-addresses.pl - aggregate-cidr-addresses.pl
- update-abusech-nftables.sh
- name: Copy Spamhaus nftables systemd units - name: Copy nftables systemd units
when: ansible_distribution_version is version('20.04', '>=') when: ansible_distribution_version is version('20.04', '>=')
copy: src={{ item }} dest=/etc/systemd/system/{{ item }} mode=0644 owner=root group=root copy: src={{ item }} dest=/etc/systemd/system/{{ item }} mode=0644 owner=root group=root
loop: loop:
- update-spamhaus-nftables.service - update-spamhaus-nftables.service
- update-spamhaus-nftables.timer - update-spamhaus-nftables.timer
register: spamhaus_nftables_systemd_units - update-abusech-nftables.service
- update-abusech-nftables.timer
register: nftables_systemd_units
# need to reload to pick up service/timer/environment changes # need to reload to pick up service/timer/environment changes
- name: Reload systemd daemon - name: Reload systemd daemon
systemd: daemon_reload=yes systemd: daemon_reload=yes
when: spamhaus_firewalld_systemd_units is changed or when: spamhaus_firewalld_systemd_units is changed or
spamhaus_nftables_systemd_units is changed nftables_systemd_units is changed
- name: Start and enable Spamhaus firewalld update timer - name: Start and enable Spamhaus firewalld update timer
when: ansible_distribution_version is version('18.04', '<=') when: ansible_distribution_version is version('18.04', '<=')
@ -108,9 +112,12 @@
notify: notify:
- restart firewalld - restart firewalld
- name: Start and enable Spamhaus nftables update timer - name: Start and enable nftables update timers
when: ansible_distribution_version is version('20.04', '>=') when: ansible_distribution_version is version('20.04', '>=')
systemd: name=update-spamhaus-nftables.timer state=started enabled=yes systemd: name={{ item }} state=started enabled=yes
loop:
- update-spamhaus-nftables.timer
- update-abusech-nftables.timer
- name: Start and enable nftables - name: Start and enable nftables
when: ansible_distribution_version is version('20.04', '>=') when: ansible_distribution_version is version('20.04', '>=')

View File

@ -9,6 +9,9 @@ flush ruleset
include "/etc/nftables/spamhaus-ipv4.nft" include "/etc/nftables/spamhaus-ipv4.nft"
include "/etc/nftables/spamhaus-ipv6.nft" include "/etc/nftables/spamhaus-ipv6.nft"
# Lists updated daily by update-abusech-nftables.sh
include "/etc/nftables/abusech-ipv4.nft"
# Notes: # Notes:
# - tables hold chains, chains hold rules # - tables hold chains, chains hold rules
# - inet is for both ipv4 and ipv6 # - inet is for both ipv4 and ipv6
@ -26,6 +29,11 @@ table inet filter {
elements = $SPAMHAUS_IPV6 elements = $SPAMHAUS_IPV6
} }
set abusech-ipv4 {
type ipv4_addr
elements = $ABUSECH_IPV4
}
chain input { chain input {
type filter hook input priority 0; type filter hook input priority 0;
@ -39,6 +47,9 @@ table inet filter {
ip saddr @spamhaus-ipv4 counter drop ip saddr @spamhaus-ipv4 counter drop
ip6 saddr @spamhaus-ipv6 counter drop ip6 saddr @spamhaus-ipv6 counter drop
# Drop packets matching the abusech set early.
ip saddr @abusech-ipv4 counter drop
# Allow loopback traffic. # Allow loopback traffic.
iifname lo accept iifname lo accept
@ -83,5 +94,8 @@ table inet filter {
# Drop outgoing packets matching the spamhaus sets too # Drop outgoing packets matching the spamhaus sets too
ip daddr @spamhaus-ipv4 counter drop ip daddr @spamhaus-ipv4 counter drop
ip6 daddr @spamhaus-ipv6 counter drop ip6 daddr @spamhaus-ipv6 counter drop
# Drop outgoing packets matching the abusech sets too
ip daddr @abusech-ipv4 counter drop
} }
} }