From 8dd7663b3caca20537c27cfc2dcb5705df8dde00 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Thu, 29 Jul 2021 10:16:00 +0300 Subject: [PATCH] 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/ --- roles/common/files/abusech-ipv4.nft | 5 ++ .../files/update-abusech-nftables.service | 27 ++++++++ roles/common/files/update-abusech-nftables.sh | 63 +++++++++++++++++++ .../files/update-abusech-nftables.timer | 12 ++++ roles/common/tasks/firewall_Debian.yml | 17 +++-- roles/common/tasks/firewall_Ubuntu.yml | 19 ++++-- roles/common/templates/nftables.conf.j2 | 14 +++++ 7 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 roles/common/files/abusech-ipv4.nft create mode 100644 roles/common/files/update-abusech-nftables.service create mode 100755 roles/common/files/update-abusech-nftables.sh create mode 100644 roles/common/files/update-abusech-nftables.timer diff --git a/roles/common/files/abusech-ipv4.nft b/roles/common/files/abusech-ipv4.nft new file mode 100644 index 0000000..04c9f49 --- /dev/null +++ b/roles/common/files/abusech-ipv4.nft @@ -0,0 +1,5 @@ +#!/usr/sbin/nft -f + +define ABUSECH_IPV4 = { +192.168.254.254 +} diff --git a/roles/common/files/update-abusech-nftables.service b/roles/common/files/update-abusech-nftables.service new file mode 100644 index 0000000..14824a3 --- /dev/null +++ b/roles/common/files/update-abusech-nftables.service @@ -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 diff --git a/roles/common/files/update-abusech-nftables.sh b/roles/common/files/update-abusech-nftables.sh new file mode 100755 index 0000000..6827ace --- /dev/null +++ b/roles/common/files/update-abusech-nftables.sh @@ -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 diff --git a/roles/common/files/update-abusech-nftables.timer b/roles/common/files/update-abusech-nftables.timer new file mode 100644 index 0000000..ef91f7b --- /dev/null +++ b/roles/common/files/update-abusech-nftables.timer @@ -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 0–3600 seconds +RandomizedDelaySec=3600 +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/roles/common/tasks/firewall_Debian.yml b/roles/common/tasks/firewall_Debian.yml index 824dead..8bee761 100644 --- a/roles/common/tasks/firewall_Debian.yml +++ b/roles/common/tasks/firewall_Debian.yml @@ -39,6 +39,7 @@ loop: - spamhaus-ipv4.nft - spamhaus-ipv6.nft + - abusech-ipv4.nft notify: - reload nftables @@ -102,20 +103,23 @@ loop: - update-spamhaus-nftables.sh - 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', '>=') copy: src={{ item }} dest=/etc/systemd/system/{{ item }} mode=0644 owner=root group=root loop: - update-spamhaus-nftables.service - 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 - name: Reload systemd daemon systemd: daemon_reload=yes 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 when: ansible_distribution_version is version('10', '<=') @@ -123,9 +127,12 @@ notify: - restart firewalld - - name: Start and enable Spamhaus nftables update timer + - name: Start and enable nftables update timers 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 when: ansible_distribution_major_version is version('11', '>=') diff --git a/roles/common/tasks/firewall_Ubuntu.yml b/roles/common/tasks/firewall_Ubuntu.yml index 11a9625..7710fce 100644 --- a/roles/common/tasks/firewall_Ubuntu.yml +++ b/roles/common/tasks/firewall_Ubuntu.yml @@ -45,6 +45,7 @@ loop: - spamhaus-ipv4.nft - spamhaus-ipv6.nft + - abusech-ipv4.nft notify: - reload nftables @@ -81,26 +82,29 @@ - update-spamhaus-lists.timer register: spamhaus_firewalld_systemd_units - - name: Copy Spamhaus nftables update scripts + - name: Copy nftables update scripts when: ansible_distribution_version is version('20.04', '>=') copy: src={{ item }} dest=/usr/local/bin/{{ item }} mode=0755 owner=root group=root loop: - update-spamhaus-nftables.sh - 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', '>=') copy: src={{ item }} dest=/etc/systemd/system/{{ item }} mode=0644 owner=root group=root loop: - update-spamhaus-nftables.service - 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 - name: Reload systemd daemon systemd: daemon_reload=yes 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 when: ansible_distribution_version is version('18.04', '<=') @@ -108,9 +112,12 @@ notify: - 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', '>=') - 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 when: ansible_distribution_version is version('20.04', '>=') diff --git a/roles/common/templates/nftables.conf.j2 b/roles/common/templates/nftables.conf.j2 index 16754f5..90e263d 100755 --- a/roles/common/templates/nftables.conf.j2 +++ b/roles/common/templates/nftables.conf.j2 @@ -9,6 +9,9 @@ flush ruleset include "/etc/nftables/spamhaus-ipv4.nft" include "/etc/nftables/spamhaus-ipv6.nft" +# Lists updated daily by update-abusech-nftables.sh +include "/etc/nftables/abusech-ipv4.nft" + # Notes: # - tables hold chains, chains hold rules # - inet is for both ipv4 and ipv6 @@ -26,6 +29,11 @@ table inet filter { elements = $SPAMHAUS_IPV6 } + set abusech-ipv4 { + type ipv4_addr + elements = $ABUSECH_IPV4 + } + chain input { type filter hook input priority 0; @@ -39,6 +47,9 @@ table inet filter { ip saddr @spamhaus-ipv4 counter drop ip6 saddr @spamhaus-ipv6 counter drop + # Drop packets matching the abusech set early. + ip saddr @abusech-ipv4 counter drop + # Allow loopback traffic. iifname lo accept @@ -83,5 +94,8 @@ table inet filter { # Drop outgoing packets matching the spamhaus sets too ip daddr @spamhaus-ipv4 counter drop ip6 daddr @spamhaus-ipv6 counter drop + + # Drop outgoing packets matching the abusech sets too + ip daddr @abusech-ipv4 counter drop } }