From 1180c1b65f7de258025cd03766418949c46f3eee Mon Sep 17 00:00:00 2001 From: Kopatz <7265381+Kropatz@users.noreply.github.com> Date: Sat, 14 Mar 2026 23:13:10 +0100 Subject: [PATCH] ip blocklist by clanker --- modules/services/blocklist.nix | 192 ++++++++++++++++++++++++ modules/services/gitea.nix | 1 + modules/services/grafana.nix | 1 + systems/amd-server-vm/configuration.nix | 19 +-- 4 files changed, 196 insertions(+), 17 deletions(-) create mode 100644 modules/services/blocklist.nix diff --git a/modules/services/blocklist.nix b/modules/services/blocklist.nix new file mode 100644 index 0000000..ce60c64 --- /dev/null +++ b/modules/services/blocklist.nix @@ -0,0 +1,192 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + # --- Blocklist sources --- + # Add or remove URLs here. Each must be a plain list of IPs or CIDRs (one per line). + # Comments (#) and blank lines are stripped automatically. + blocklistSources = [ + # DShield top attacking subnets (SANS ISC) — tab-separated: IP / mask / CIDR prefix + "https://feeds.dshield.org/block.txt" + # blocklist.de — scanners, bots, brute-force, scrapers + "https://lists.blocklist.de/lists/all.txt" + # GreenSnow — bots and port scanners + "https://blocklist.greensnow.co/greensnow.txt" + # Spamhaus DROP — hijacked netblocks used by cybercrime operations + "https://www.spamhaus.org/drop/drop.txt" + # FireHOL level2 — 48-hour attack tracker, pre-merged CIDRs (good all-rounder) + "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level2.netset" + ]; + + # nftables table and set names — must match your existing nftables config + nftTable = "inet ip_drop"; + nftSet = "blocked-ip4"; + + # How often to refresh (systemd calendar format) + refreshInterval = "*:0/4"; # every 4 hours + + # --- The updater script --- + # Written as a pkgs.writeShellApplication so nix handles the PATH / dependencies. + updaterScript = pkgs.writeShellApplication { + name = "nft-blocklist-update"; + + runtimeInputs = with pkgs; [ + curl + gawk + iproute2 # provides 'ip' for basic sanity; not strictly needed but good to have + nftables # provides 'nft' + coreutils + ]; + + text = '' + set -euo pipefail + + NFT_TABLE="${nftTable}" + NFT_SET="${nftSet}" + + TMPFILE=$(mktemp /tmp/nft-blocklist-XXXXXX.txt) + trap 'rm -f "$TMPFILE"' EXIT + + echo "[blocklist] Fetching sources..." + + # --- Per-source fetch + normalise --- + # Each source may use a different format; we normalise to bare CIDRs. + fetch_and_normalise() { + local url="$1" + curl --silent --fail --max-time 30 --retry 3 "$url" | \ + awk ' + # Skip blank lines and comment lines + /^[[:space:]]*$/ { next } + /^[[:space:]]*#/ { next } + + # DShield format: "IPnetmaskprefix ..." — convert to CIDR + /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[[:space:]]+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[[:space:]]+[0-9]+/ { + print $1 "/" $3 + next + } + + # Plain CIDR (e.g. 1.2.3.0/24) — pass through + /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\/[0-9]+/ { + print $1 + next + } + + # Plain host IP (no prefix) — treat as /32 + /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ { + print $1 "/32" + next + } + ' + } + + # Fetch all sources into the temp file, skip on individual failure + ${lib.concatMapStringsSep "\n" (url: '' + echo "[blocklist] Fetching: ${url}" + fetch_and_normalise "${url}" >> "$TMPFILE" || \ + echo "[blocklist] WARNING: failed to fetch ${url}, skipping" >&2 + '') blocklistSources} + + # Deduplicate lines (nft auto-merge handles overlapping CIDRs at load time) + sort -u "$TMPFILE" -o "$TMPFILE" + + TOTAL=$(wc -l < "$TMPFILE") + echo "[blocklist] Total entries after dedup: $TOTAL" + + if [ "$TOTAL" -eq 0 ]; then + echo "[blocklist] ERROR: no entries fetched — aborting to avoid flushing the set" >&2 + exit 1 + fi + + # Build the nft element string: "1.2.3.0/24, 5.6.7.8/32, ..." + ELEMENTS=$(paste -sd ',' "$TMPFILE") + + echo "[blocklist] Loading into nft set $NFT_TABLE / $NFT_SET ..." + + # Atomic-ish update: flush then reload inside a single nft transaction + nft -f - <