From 2344881229dfa0f00ebf112653e1d48bb99999b1 Mon Sep 17 00:00:00 2001 From: "Jason D. McCormick" Date: Tue, 20 Jan 2026 19:16:02 -0500 Subject: initial add --- LICENSE | 26 ++++++++ Makefile | 75 +++++++++++++++++++++++ blocklist2nft | 130 ++++++++++++++++++++++++++++++++++++++++ blocklist2nft.py | 130 ---------------------------------------- debian/changelog | 5 ++ debian/control | 16 +++++ debian/copyright | 8 +++ debian/postinst | 44 ++++++++++++++ debian/rules | 4 ++ debian/source/format | 1 + debian/source/lintian-overrides | 0 debian/source/local-options | 0 12 files changed, 309 insertions(+), 130 deletions(-) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 blocklist2nft delete mode 100644 blocklist2nft.py create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/postinst create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/source/lintian-overrides create mode 100644 debian/source/local-options diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..12db205 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) Jason McCormick +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1f1e547 --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +# +# Build variables +# +SRCNAME = blocklist2nft +PKGNAME = $(SRCNAME) +RELVER = 1 +DEBVER = 1 +RELPLAT ?= deb$(shell lsb_release -rs 2> /dev/null) + +# +# Other variables +# +prefix ?= /usr +bindir ?= $(prefix)/bin +systemd_enabled_dir ?= /lib/systemd/system + +BIN_FILES = \ + blocklist2nft + +SYSTEMD_ENABLED_FILES = \ + blocklist2nft.service \ + blocklist2nft.timer + +BIN_INSTALLABLES = $(patsubst %, $(DESTDIR)$(bindir)/%, $(BIN_FILES)) +SYSTEMD_ENABLED_INSTALLABLES = $(patsubst %, $(DESTDIR)$(systemd_enabled_dir)/%, $(SYSTEMD_ENABLED_FILES)) + +INSTALLABLES = \ + $(BIN_INSTALLABLES) \ + $(SYSTEMD_ENABLED_INSTALLABLES) + +default: + @echo This does nothing + +install: $(INSTALLABLES) + +$(DESTDIR)$(bindir)/%: % + install -D -m 0755 $< $@ + +$(DESTDIR)$(systemd_enabled_dir)/%: % + install -D -m 0644 $< $@ + +deb: debclean debprep + debchange --distribution stable --package $(PKGNAME) \ + --newversion $(EPOCHVER)$(RELVER)-$(DEBVER).$(RELPLAT) \ + "Autobuild of $(EPOCHVER)$(RELVER)-$(DEBVER) for $(RELPLAT)" + dpkg-buildpackage -b --no-sign + git checkout debian/changelog + +debchange: + debchange -v $(RELVER)-$(DEBVER) + debchange -r + + +debprep: debclean + (cd .. && \ + rm -f $(PKGNAME)-$(RELVER) && \ + rm -f $(PKGNAME)-$(RELVER).tar.gz && \ + rm -f $(PKGNAME)_$(RELVER).orig.tar.gz && \ + ln -s $(SRCNAME) $(PKGNAME)-$(RELVER) && \ + tar --exclude=".git" -h -zvcf $(PKGNAME)-$(RELVER).tar.gz $(PKGNAME)-$(RELVER) && \ + ln -s $(PKGNAME)-$(RELVER).tar.gz $(PKGNAME)_$(RELVER).orig.tar.gz ) + +debclean: + rm -f ../$(PKGNAME)_$(RELVER)* + rm -f ../$(PKGNAME)-$(RELVER)* + rm -rf debian/$(PKGNAME) + rm -f debian/files + rm -rf debian/.debhelper/ + rm -f debian/debhelper-build-stamp + rm -f debian/*.substvars + rm -rf debian/$(SRCNAME)/ debian/.debhelper/ + rm -f debian/debhelper-build-stamp debian/files debian/$(SRCNAME).substvars + rm -f debian/*.debhelper + + diff --git a/blocklist2nft b/blocklist2nft new file mode 100644 index 0000000..fc5fc4a --- /dev/null +++ b/blocklist2nft @@ -0,0 +1,130 @@ +#!/usr/bin/python3 +""" blocklist2nft """ + +import argparse +import ipaddress +import logging +import re +import subprocess +import tempfile + +import requests + +blocklist_urls= [ + "https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt", + "https://rules.emergingthreats.net/blockrules/compromised-ips.txt", + "https://iplists.firehol.org/files/firehol_level1.netset" + ] + +NFT_PRIORITY = -2 + +def nft_commands(v4_list, v6_list, priority): + """ generate nft table commands """ + return f""" +add table inet blocklist2nft +delete table inet blocklist2nft +add table inet blocklist2nft {{ + set addr-set-drop4 {{ + type ipv4_addr + flags interval + elements = {{ {v4_list} + }} + }} + set addr-set-drop6 {{ + type ipv6_addr + flags interval + elements = {{ + FEC0::/10, {v6_list} + }} + }} + chain INPUT {{ + type filter hook input priority filter {priority}; policy accept; + ip saddr @addr-set-drop4 drop + ip6 saddr @addr-set-drop6 drop + }} +}} + """ + +ipv4_list = [] +ipv6_list = [] + +def is_ip_address(a): + """ is this an IP address block """ + n = ipaddress.ip_network(a) + return n + +def get_address_feed(feed): + """ download and pack a feed """ + try: + with requests.get(feed, stream=True, timeout=30) as r: + r.raise_for_status() + for r in r.iter_lines(decode_unicode=True): + if isinstance(r, bytes): + r = r.decode('utf-8', errors="replace") + if not re.match(r'^[0-9a-fA-F]', r): + continue + net = is_ip_address(r) + if net is None: + continue + if net.version == 4: + ipv4_list.append(net) + if net.version == 6: + print(r) + ipv6_list.append(net) + except requests.exceptions.ConnectionError as e: + log.error(e) + +def main(): + """" self-explanatory """ + ipv4_list_str = "" + ipv6_list_str = "" + + try: + for feed in blocklist_urls: + get_address_feed(feed) + for net in ipaddress.collapse_addresses(ipv4_list): + ipv4_list_str += f"{net}, \n" + for net in ipaddress.collapse_addresses(ipv6_list): + ipv6_list_str += f"{net}, \n" + + ipv4_list_str = ipv4_list_str[:-3] + ipv6_list_str = ipv6_list_str[:-3] + + with tempfile.NamedTemporaryFile(delete=True) as nft_file: + nft_contents = nft_commands(ipv4_list_str, ipv6_list_str, args.priority) + nft_contents_bytes = nft_contents.encode("utf-8") + nft_file.write(nft_contents_bytes) + + nft_result = subprocess.run( + ["nft", "-f", nft_file.name], + check=True, + capture_output=True, + text=True + ) + if nft_result.returncode != 0: + print(f"nft call failed with code {nft_result.returncode}") + print(nft_result.stderr) + print(nft_result.stdout) + + except Exception as e: + log.error(e) + + +ap = argparse.ArgumentParser(description="Create an nftables blocklist from a URL data source") +ap.add_argument("--priority", type=int, + default=NFT_PRIORITY, help="nft chain hook priority (default -2)") +ap.add_argument("--debug", action="store_true", help="Enable debug logging") +args = ap.parse_args() + +log = logging.getLogger("blocklist2nft") +lh = logging.StreamHandler() +lf = logging.Formatter(fmt="%(levelname)s: %(message)s") +lh.setFormatter(lf) +log.addHandler(lh) + +log.setLevel(logging.INFO) +if args.debug: + log.setLevel(logging.DEBUG) + +if __name__ == '__main__': + main() diff --git a/blocklist2nft.py b/blocklist2nft.py deleted file mode 100644 index fc5fc4a..0000000 --- a/blocklist2nft.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/python3 -""" blocklist2nft """ - -import argparse -import ipaddress -import logging -import re -import subprocess -import tempfile - -import requests - -blocklist_urls= [ - "https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt", - "https://rules.emergingthreats.net/blockrules/compromised-ips.txt", - "https://iplists.firehol.org/files/firehol_level1.netset" - ] - -NFT_PRIORITY = -2 - -def nft_commands(v4_list, v6_list, priority): - """ generate nft table commands """ - return f""" -add table inet blocklist2nft -delete table inet blocklist2nft -add table inet blocklist2nft {{ - set addr-set-drop4 {{ - type ipv4_addr - flags interval - elements = {{ {v4_list} - }} - }} - set addr-set-drop6 {{ - type ipv6_addr - flags interval - elements = {{ - FEC0::/10, {v6_list} - }} - }} - chain INPUT {{ - type filter hook input priority filter {priority}; policy accept; - ip saddr @addr-set-drop4 drop - ip6 saddr @addr-set-drop6 drop - }} -}} - """ - -ipv4_list = [] -ipv6_list = [] - -def is_ip_address(a): - """ is this an IP address block """ - n = ipaddress.ip_network(a) - return n - -def get_address_feed(feed): - """ download and pack a feed """ - try: - with requests.get(feed, stream=True, timeout=30) as r: - r.raise_for_status() - for r in r.iter_lines(decode_unicode=True): - if isinstance(r, bytes): - r = r.decode('utf-8', errors="replace") - if not re.match(r'^[0-9a-fA-F]', r): - continue - net = is_ip_address(r) - if net is None: - continue - if net.version == 4: - ipv4_list.append(net) - if net.version == 6: - print(r) - ipv6_list.append(net) - except requests.exceptions.ConnectionError as e: - log.error(e) - -def main(): - """" self-explanatory """ - ipv4_list_str = "" - ipv6_list_str = "" - - try: - for feed in blocklist_urls: - get_address_feed(feed) - for net in ipaddress.collapse_addresses(ipv4_list): - ipv4_list_str += f"{net}, \n" - for net in ipaddress.collapse_addresses(ipv6_list): - ipv6_list_str += f"{net}, \n" - - ipv4_list_str = ipv4_list_str[:-3] - ipv6_list_str = ipv6_list_str[:-3] - - with tempfile.NamedTemporaryFile(delete=True) as nft_file: - nft_contents = nft_commands(ipv4_list_str, ipv6_list_str, args.priority) - nft_contents_bytes = nft_contents.encode("utf-8") - nft_file.write(nft_contents_bytes) - - nft_result = subprocess.run( - ["nft", "-f", nft_file.name], - check=True, - capture_output=True, - text=True - ) - if nft_result.returncode != 0: - print(f"nft call failed with code {nft_result.returncode}") - print(nft_result.stderr) - print(nft_result.stdout) - - except Exception as e: - log.error(e) - - -ap = argparse.ArgumentParser(description="Create an nftables blocklist from a URL data source") -ap.add_argument("--priority", type=int, - default=NFT_PRIORITY, help="nft chain hook priority (default -2)") -ap.add_argument("--debug", action="store_true", help="Enable debug logging") -args = ap.parse_args() - -log = logging.getLogger("blocklist2nft") -lh = logging.StreamHandler() -lf = logging.Formatter(fmt="%(levelname)s: %(message)s") -lh.setFormatter(lf) -log.addHandler(lh) - -log.setLevel(logging.INFO) -if args.debug: - log.setLevel(logging.DEBUG) - -if __name__ == '__main__': - main() diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..02aeb61 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +blocklist2nft (1-1) unstable; urgency=medium + + * initial release + + -- Jason McCormick Tue, 20 Jan 2026 19:15:53 -0500 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..d60a0c0 --- /dev/null +++ b/debian/control @@ -0,0 +1,16 @@ +Source: asl3-update-nodelist +Section: Radio +Priority: optional +Maintainer: Jason McCormick +Build-Depends: debhelper-compat (= 13), pandoc +Standards-Version: 4.5.1 +Homepage: https://github.com/AllStarLink/asl3-update-nodelist +Vcs-Browser: https://github.com/AllStarLink/asl3-update-nodelist +Vcs-Git: https://github.com/AllStarLink/asl3-update-nodelist +Rules-Requires-Root: binary-targets + +Package: asl3-update-nodelist +Architecture: all +Depends: ${misc:Depends}, patch +Description: Maintain the ASL node database file + in /var/lib/asterisk/rpt_extnodes diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..f859a28 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,8 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: blocklist2nft +Source: https://gitweb.mfamily.org/blocklist2nft + +Files: * +Copyright: Copyright (C) 2026 Jason McCormick +License: BSD + /usr/share/common-licenses/BSD diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..4284a9b --- /dev/null +++ b/debian/postinst @@ -0,0 +1,44 @@ +#!/bin/sh -e +# postinst script for allmon3 +# +# see: dh_installdeb(1) + +# summary of how this script can be called: +# * `configure' +# * `abort-upgrade' +# * `abort-remove' `in-favour' +# +# * `abort-remove' +# * `abort-deconfigure' `in-favour' +# `removing' +# +# for details, see https://www.debian.org/doc/debian-policy/ or +# the debian-policy package + +do_configure() { + dh_systemd_enable blocklist2nft.timer + dh_systemd_start blocklist2nft.timer + /usr/bin/blocklist2nft +} + +case "$1" in + configure) + do_configure + ;; + + abort-upgrade|abort-remove|abort-deconfigure) + ;; + + *) + echo "postinst called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +# dh_installdeb will replace this with shell code automatically +# generated by other debhelper scripts. + +#DEBHELPER# + +exit 0 + diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..2d33f6a --- /dev/null +++ b/debian/rules @@ -0,0 +1,4 @@ +#!/usr/bin/make -f + +%: + dh $@ diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides new file mode 100644 index 0000000..e69de29 diff --git a/debian/source/local-options b/debian/source/local-options new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3