#!/usr/bin/env bash # # usage update-blacklist.sh # eg: update-blacklist.sh /etc/ipset-blacklist/ipset-blacklist.conf # function exists() { command -v "$1" >/dev/null 2>&1 ; } if [[ -z "$1" ]]; then echo "Error: please specify a configuration file, e.g. $0 /etc/ipset-blacklist/ipset-blacklist.conf" exit 1 fi # shellcheck source=ipset-blacklist.conf if ! source "$1"; then echo "Error: can't load configuration file $1" exit 1 fi if ! exists curl && exists egrep && exists grep && exists ipset && exists iptables && exists sed && exists sort && exists wc ; then echo >&2 "Error: searching PATH fails to find executables among: curl egrep grep ipset iptables sed sort wc" exit 1 fi DO_OPTIMIZE_CIDR=no if exists iprange && [[ ${OPTIMIZE_CIDR:-yes} != no ]]; then DO_OPTIMIZE_CIDR=yes fi if [[ ! -d $(dirname "$IP_BLACKLIST") || ! -d $(dirname "$IP_BLACKLIST_RESTORE") ]]; then echo >&2 "Error: missing directory(s): $(dirname "$IP_BLACKLIST" "$IP_BLACKLIST_RESTORE"|sort -u)" exit 1 fi # create the ipset if needed (or abort if does not exists and FORCE=no) if ! ipset list -n|command grep -q "$IPSET_BLACKLIST_NAME"; then if [[ ${FORCE:-no} != yes ]]; then echo >&2 "Error: ipset does not exist yet, add it using:" echo >&2 "# ipset create $IPSET_BLACKLIST_NAME -exist hash:net family inet hashsize ${HASHSIZE:-16384} maxelem ${MAXELEM:-65536}" exit 1 fi if ! ipset create "$IPSET_BLACKLIST_NAME" -exist hash:net family inet hashsize "${HASHSIZE:-16384}" maxelem "${MAXELEM:-65536}"; then echo >&2 "Error: while creating the initial ipset" exit 1 fi fi # create the iptables binding if needed (or abort if does not exists and FORCE=no) if ! iptables -nvL INPUT|command grep -q "match-set $IPSET_BLACKLIST_NAME"; then # we may also have assumed that INPUT rule n°1 is about packets statistics (traffic monitoring) if [[ ${FORCE:-no} != yes ]]; then echo >&2 "Error: iptables does not have the needed ipset INPUT rule, add it using:" echo >&2 "# iptables -I INPUT ${IPTABLES_IPSET_RULE_NUMBER:-1} -m set --match-set $IPSET_BLACKLIST_NAME src -j DROP" exit 1 fi if ! iptables -I INPUT "${IPTABLES_IPSET_RULE_NUMBER:-1}" -m set --match-set "$IPSET_BLACKLIST_NAME" src -j DROP; then echo >&2 "Error: while adding the --match-set ipset rule to iptables" exit 1 fi fi IP_BLACKLIST_TMP=$(mktemp) for i in "${BLACKLISTS[@]}" do IP_TMP=$(mktemp) (( HTTP_RC=$(curl -L -A "blacklist-update/script/github" --connect-timeout 10 --max-time 10 -o "$IP_TMP" -s -w "%{http_code}" "$i") )) if (( HTTP_RC == 200 || HTTP_RC == 302 || HTTP_RC == 0 )); then # "0" because file:/// returns 000 command grep -Po '^(?:\d{1,3}\.){3}\d{1,3}(?:/\d{1,2})?' "$IP_TMP" | sed -r 's/^0*([0-9]+)\.0*([0-9]+)\.0*([0-9]+)\.0*([0-9]+)$/\1.\2.\3.\4/' >> "$IP_BLACKLIST_TMP" [[ ${VERBOSE:-yes} == yes ]] && echo -n "." elif (( HTTP_RC == 503 )); then echo -e "\\nUnavailable (${HTTP_RC}): $i" else echo >&2 -e "\\nWarning: curl returned HTTP response code $HTTP_RC for URL $i" fi rm -f "$IP_TMP" done # sort -nu does not work as expected sed -r -e '/^(0\.0\.0\.0|10\.|127\.|172\.1[6-9]\.|172\.2[0-9]\.|172\.3[0-1]\.|192\.168\.|22[4-9]\.|23[0-9]\.)/d' "$IP_BLACKLIST_TMP"|sort -n|sort -mu >| "$IP_BLACKLIST" if [[ ${DO_OPTIMIZE_CIDR} == yes ]]; then if [[ ${VERBOSE:-no} == yes ]]; then echo -e "\\nAddresses before CIDR optimization: $(wc -l "$IP_BLACKLIST" | cut -d' ' -f1)" fi < "$IP_BLACKLIST" iprange --optimize - > "$IP_BLACKLIST_TMP" 2>/dev/null if [[ ${VERBOSE:-no} == yes ]]; then echo "Addresses after CIDR optimization: $(wc -l "$IP_BLACKLIST_TMP" | cut -d' ' -f1)" fi cp "$IP_BLACKLIST_TMP" "$IP_BLACKLIST" fi rm -f "$IP_BLACKLIST_TMP" # family = inet for IPv4 only cat >| "$IP_BLACKLIST_RESTORE" <> "$IP_BLACKLIST_RESTORE" cat >> "$IP_BLACKLIST_RESTORE" <