roles/common: Add Spamhaus DROP lists to firewalld ipsets
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 commit is contained in:
parent
ee5f4cdf74
commit
d7c34a30a3
6
roles/common/files/spamhaus-ipv4.xml
Normal file
6
roles/common/files/spamhaus-ipv4.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ipset type="hash:net">
|
||||
<option name="family" value="inet" />
|
||||
<short>spamhaus-ipv4</short>
|
||||
<description>Spamhaus DROP and EDROP lists placeholder (IPv4).</description>
|
||||
</ipset>
|
6
roles/common/files/spamhaus-ipv6.xml
Normal file
6
roles/common/files/spamhaus-ipv6.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ipset type="hash:net">
|
||||
<option name="family" value="inet6" />
|
||||
<short>spamhaus-ipv6</short>
|
||||
<description>Spamhaus DROP list placeholder (IPv6).</description>
|
||||
</ipset>
|
27
roles/common/files/update-spamhaus-lists.service
Normal file
27
roles/common/files/update-spamhaus-lists.service
Normal file
@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=Update Spamhaus lists
|
||||
# This service will fail if firewalld is not running so we use Requires to make
|
||||
# sure that firewalld is started.
|
||||
Requires=firewalld.service
|
||||
# Make sure the network is up and firewalld is started
|
||||
After=network-online.target firewalld.service
|
||||
Wants=network-online.target update-spamhaus-lists.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/firewalld/ipsets
|
||||
PrivateTmp=true
|
||||
WorkingDirectory=/var/tmp
|
||||
|
||||
SyslogIdentifier=update-spamhaus-lists
|
||||
ExecStart=/usr/bin/flock -x update-spamhaus-lists.lck \
|
||||
/usr/local/bin/update-spamhaus-lists.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
107
roles/common/files/update-spamhaus-lists.sh
Executable file
107
roles/common/files/update-spamhaus-lists.sh
Executable file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# update-spamhaus-lists.sh v0.0.5
|
||||
#
|
||||
# Download Spamhaus DROP lists and load them into firewalld ipsets. Should work
|
||||
# with both the iptables and nftables backends.
|
||||
#
|
||||
# See: https://www.spamhaus.org/drop/
|
||||
#
|
||||
# Copyright (C) 2021 Alan Orth
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
# Exit on first error
|
||||
set -o errexit
|
||||
|
||||
firewalld_ipsets=$(firewall-cmd --get-ipsets)
|
||||
xml_temp=$(mktemp)
|
||||
spamhaus_ipv4_ipset_path=/etc/firewalld/ipsets/spamhaus-ipv4.xml
|
||||
spamhaus_ipv6_ipset_path=/etc/firewalld/ipsets/spamhaus-ipv6.xml
|
||||
|
||||
function download() {
|
||||
echo "Downloading $1"
|
||||
wget -q -O - "https://www.spamhaus.org/drop/$1" > "$1"
|
||||
}
|
||||
|
||||
download drop.txt
|
||||
download edrop.txt
|
||||
download dropv6.txt
|
||||
|
||||
if [[ -f "drop.txt" && -f "edrop.txt" ]]; then
|
||||
echo "Processing IPv4 DROP lists"
|
||||
|
||||
# Extract all networks from drop.txt and edrop.txt, skipping blank lines and
|
||||
# comments.
|
||||
networks=$(cat drop.txt edrop.txt | sed -e '/^$/d' -e '/^;.*/d' -e 's/[[:space:]];[[:space:]].*//')
|
||||
|
||||
# If firewalld already has this ipset we should delete it first to emulate
|
||||
# `ipset flush` (but I don't want to use that because newer hosts might be
|
||||
# using nftables and firewalld will handle that for us).
|
||||
if [[ "$firewalld_ipsets" =~ spamhaus-ipv4 ]]; then
|
||||
echo "Deleting existing spamhaus-ipv4 ipset"
|
||||
# This deletes the firewalld ipset XML file as well as the ipset itself
|
||||
firewall-cmd --permanent --delete-ipset=spamhaus-ipv4
|
||||
else
|
||||
echo "Creating placeholder spamhaus-ipv4 ipset"
|
||||
# Create a placeholder ipset so firewalld doesn't complain when we try
|
||||
# to reload the ipset later after having added a new XML definition. I
|
||||
# don't know why, but depending on the system state there may not be a
|
||||
# ipset defined and firewalld errors on INVALID_IPSET.
|
||||
firewall-cmd --permanent --new-ipset=spamhaus-ipv4 --type=hash:net --option=family=inet
|
||||
fi
|
||||
|
||||
# I'm not proud of this, but writing the XML directly is WAY faster than
|
||||
# using firewall-cmd to add each entry one by one (and we can't add from
|
||||
# a file because many of our hosts are using old firewalld).
|
||||
cat << XML_HEAD > "$xml_temp"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ipset type="hash:net">
|
||||
<option name="family" value="inet" />
|
||||
<short>spamhaus-ipv4</short>
|
||||
<description>Spamhaus DROP and EDROP lists (IPv4).</description>
|
||||
XML_HEAD
|
||||
|
||||
for network in $networks; do
|
||||
echo " <entry>$network</entry>" >> "$xml_temp"
|
||||
done
|
||||
|
||||
echo "</ipset>" >> "$xml_temp"
|
||||
|
||||
install -m 0600 "$xml_temp" "$spamhaus_ipv4_ipset_path"
|
||||
fi
|
||||
|
||||
if [[ -f "dropv6.txt" ]]; then
|
||||
echo "Processing IPv6 DROP list"
|
||||
|
||||
networks=$(sed -e '/^$/d' -e '/^;.*/d' -e 's/[[:space:]];[[:space:]].*//' dropv6.txt)
|
||||
|
||||
if [[ "$firewalld_ipsets" =~ spamhaus-ipv6 ]]; then
|
||||
echo "Deleting existing spamhaus-ipv6 ipset"
|
||||
firewall-cmd --permanent --delete-ipset=spamhaus-ipv6
|
||||
else
|
||||
echo "Creating placeholder spamhaus-ipv6 ipset"
|
||||
firewall-cmd --permanent --new-ipset=spamhaus-ipv6 --type=hash:net --option=family=inet6
|
||||
fi
|
||||
|
||||
cat << XML_HEAD > "$xml_temp"
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ipset type="hash:net">
|
||||
<option name="family" value="inet6" />
|
||||
<short>spamhaus-ipv6</short>
|
||||
<description>Spamhaus DROP lists (IPv6).</description>
|
||||
XML_HEAD
|
||||
|
||||
for network in $networks; do
|
||||
echo " <entry>$network</entry>" >> "$xml_temp"
|
||||
done
|
||||
|
||||
echo "</ipset>" >> "$xml_temp"
|
||||
|
||||
install -m 0600 "$xml_temp" "$spamhaus_ipv6_ipset_path"
|
||||
fi
|
||||
|
||||
echo "Reloading firewalld"
|
||||
firewall-cmd --reload
|
||||
|
||||
rm -v drop.txt edrop.txt dropv6.txt "$xml_temp"
|
12
roles/common/files/update-spamhaus-lists.timer
Normal file
12
roles/common/files/update-spamhaus-lists.timer
Normal file
@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Update Spamhaus lists
|
||||
|
||||
[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
|
@ -54,6 +54,32 @@
|
||||
loop:
|
||||
- abusers-ipv4.xml
|
||||
- abusers-ipv6.xml
|
||||
- spamhaus-ipv4.xml
|
||||
- spamhaus-ipv6.xml
|
||||
notify:
|
||||
- restart firewalld
|
||||
|
||||
- name: Copy Spamhaus update script
|
||||
when: ansible_distribution_version is version('16.04', '>=')
|
||||
copy: src=update-spamhaus-lists.sh dest=/usr/local/bin/update-spamhaus-lists.sh mode=0755 owner=root group=root
|
||||
|
||||
- name: Copy Spamhaus systemd units
|
||||
when: ansible_distribution_version is version('16.04', '>=')
|
||||
copy: src={{ item }} dest=/etc/systemd/system/{{ item }} mode=0644 owner=root group=root
|
||||
loop:
|
||||
- update-spamhaus-lists.service
|
||||
- update-spamhaus-lists.timer
|
||||
register: spamhaus_systemd_units
|
||||
|
||||
# need to reload to pick up service/timer/environment changes
|
||||
- name: Reload systemd daemon
|
||||
systemd: daemon_reload=yes
|
||||
when: spamhaus_systemd_units is changed
|
||||
|
||||
- name: Start and enable Spamhaus update timer
|
||||
when: ansible_distribution_version is version('16.04', '>=')
|
||||
systemd: name=update-spamhaus-lists.timer state=started enabled=yes
|
||||
|
||||
notify:
|
||||
- restart firewalld
|
||||
|
||||
|
@ -69,4 +69,13 @@
|
||||
<source ipset="abusers-ipv6"/>
|
||||
<drop/>
|
||||
</rule>
|
||||
<rule>
|
||||
<source ipset="spamhaus-ipv4"/>
|
||||
<drop/>
|
||||
</rule>
|
||||
<rule>
|
||||
<source ipset="spamhaus-ipv6"/>
|
||||
<drop/>
|
||||
</rule>
|
||||
|
||||
</zone>
|
||||
|
Loading…
Reference in New Issue
Block a user