#!/usr/bin/env bash # Pull the IRDB blocklist and atomic-replace an ipset. # # Usage (cron-friendly): # IRDB_URL=http://localhost:8081 IRDB_TOKEN=irdb_con_... \ # IPSET_NAME=irdb-blocked examples/consumers/iptables-restore.sh # # Prerequisites: # - the ipset is referenced by an iptables rule, e.g. # iptables -I INPUT -m set --match-set irdb-blocked src -j DROP # - ipset and iptables-restore are installed # - the consumer's policy returns IPv4 entries (or a mixed set; # this script filters per family) # # Atomic-replace pattern: build a swap set in memory, swap, destroy # old. Even an in-flight rule keeps working through the swap. set -euo pipefail : "${IRDB_URL:?must be set}" : "${IRDB_TOKEN:?must be set}" IPSET_NAME="${IPSET_NAME:-irdb-blocked}" TYPE="${IPSET_TYPE:-hash:net}" TIMEOUT="${IRDB_TIMEOUT:-30}" LIST=$(curl -fsS --max-time "$TIMEOUT" \ -H "Authorization: Bearer $IRDB_TOKEN" \ -H "Accept: text/plain" \ "$IRDB_URL/api/v1/blocklist") # Split into v4 / v6 — this script handles v4 only by default. Drop # v6 entries if your ipset is `hash:net` family inet; mirror this # script for inet6 if you need both. SWAP="${IPSET_NAME}-swap" # Re-create the swap set; ignore "Set cannot be created: set with the # same name already exists" since we destroy unconditionally below. ipset create "$SWAP" "$TYPE" -exist ipset flush "$SWAP" while IFS= read -r line; do [ -z "$line" ] && continue case "$line" in *:*) continue ;; # skip v6 *) ipset add "$SWAP" "$line" -exist ;; esac done <<<"$LIST" # Make the existing target set if absent so the swap target is valid. ipset create "$IPSET_NAME" "$TYPE" -exist ipset swap "$SWAP" "$IPSET_NAME" ipset destroy "$SWAP" COUNT=$(ipset list -t "$IPSET_NAME" | awk -F': ' '/^Number of entries:/ {print $2}') echo "irdb-blocklist updated: $COUNT entries in $IPSET_NAME"