VyOS IP Blocklist generator

We’ve been working on a IP blocklist generator specifically for VyOS routers and thought the community might find it useful.

Didnt know in which categorie to post it, feel free to move it.

Features;

  • Automatically fetches threat intelligence from multiple sources (Emerging Threats, Binary Defense, AbuseIPDB)
  • Applies blocklists directly to VyOS nftables with dual IPv4/IPv6 support
  • Smart deduplication and CIDR optimization to keep the firewall efficient
  • Whitelist protection so you never accidentally block your own networks
  • Professional-grade code with proper error handling, logging, and type hints

There is a simple .deb file available to install, after that it’s as easy as creating the firewall groups and letting it sync.

GitHub:

6 Likes

Always curios when I see buzzwords.

How does “professional-grade code” differ from lets say “regular-grade code”?

Wont both have error handling, logging and type hints?

The code we found on this subject didnt have any proper error handling and was not maintained. Not even whitelisting or verbose logging, and lacking more features we did implement as we decided to start from scratch.

I still fail to see what makes that “professional-grade code” from “regular-grade code”…

Not really what this topic is about, is it. Feel free to use it or not.

1 Like

It looked nice up until the buzzword BS showed up.

Thank you Wieger for sharing this! I will try it out ASAP.

1 Like

@CharlieRoot I tried the package today on 1.4.2 LTS. It seems to work well. After enabling, within 10 minutes I got 115 threat related drops on my dual WAN setup. Nice!

The README is great but I ran into a few quirks.

Firstly, I had to explicitly define

set firewall group network-group threats-blocklist-ipv4
set firewall group ipv6-network-group threats-blocklist-ipv6

as stubs in my config to prevent commit errors when adding the rules referring to the threat network groups. Maybe that sync script should take care of this, but I can’t find that sync script you’re talking about in the README:

» ls /usr/share/doc/vyos-ipblock/examples
whitelist.txt.example

Secondly, on my version, the correct config syntax for using an IPv6 network group in a rule is

set firewall ipv6 forward filter rule 12 source group network-group 'threats-blocklist-ipv6'

and not

set firewall ipv6 forward filter rule 12 source group ipv6-network-group 'threats-blocklist-ipv6' which in in your README file.

Not sure if I did something wrong, or if this is because of Sagitta vs Circinus, or it’s an error in the documentation? Also curious what the sync script will add to the setup.

@marc_s Thanks for the feedback, and it’s good to hear it’s already having an effect.

Regarding the need to create firewall groups, this was missing from the Readme, and I’ve just updated it.

The sync script has been removed from the latest version because we’ve moved it to the systemd-timer function. If this is enabled, a refresh will occur every 6 hours.

sudo systemctl status vyos-ipblock.timer

1 Like

I see no sync going on between nft and VyOS config.

The timer unit is in place and active:

» sudo systemctl status vyos-ipblock.timer
● vyos-ipblock.timer - Run VyOS IP Blocklist Generator every 6 hours
     Loaded: loaded (/lib/systemd/system/vyos-ipblock.timer; enabled; preset: enabled)
     Active: active (waiting) since Sat 2025-08-16 08:30:44 CEST; 1 day 3h ago
    Trigger: Sun 2025-08-17 12:02:51 CEST; 10min left
   Triggers: ● vyos-ipblock.service

Aug 16 08:30:44 gw systemd[1]: Started vyos-ipblock.timer - Run VyOS IP Blocklist Generator every 6 hours.

The service unit is set up correctly:

» sudo systemctl status vyos-ipblock.service
○ vyos-ipblock.service - VyOS IP Blocklist Generator
     Loaded: loaded (/lib/systemd/system/vyos-ipblock.service; enabled; preset: enabled)
     Active: inactive (dead) since Sun 2025-08-17 06:06:34 CEST; 5h 47min ago
TriggeredBy: ● vyos-ipblock.timer
       Docs: file:///usr/share/doc/vyos-ipblock/README.md
    Process: 3639062 ExecStart=/usr/bin/vyos-ipblock (code=exited, status=0/SUCCESS)
   Main PID: 3639062 (code=exited, status=0/SUCCESS)
        CPU: 2min 52.345s

It seems to run at the set times. Log of last run:

Aug 17 06:03:41 gw systemd[1]: Starting vyos-ipblock.service - VyOS IP Blocklist Generator...
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,434 - __main__ - INFO - Loading whitelist from /config/scripts/whitelist.txt
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,435 - __main__ - INFO - Converting 23 IPv4 entries to CIDR format
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,435 - __main__ - INFO - Successfully converted 23 IPv4 entries to CIDR
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,435 - __main__ - INFO - Converting 15 IPv6 entries to CIDR format
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,436 - __main__ - INFO - Successfully converted 15 IPv6 entries to CIDR
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,436 - __main__ - INFO - Loaded 23 IPv4 and 15 IPv6 whitelisted networks
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,436 - __main__ - INFO - === Starting VyOS IP Blocklist Generation ===
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,436 - __main__ - INFO - Starting blocklist generation
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,436 - __main__ - INFO - Fetching Emerging Threats blocklist
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,436 - __main__ - INFO - Fetching URL: http://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,626 - __main__ - INFO - Successfully fetched http://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt, resp>
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,643 - __main__ - INFO - Emerging Threats: 1642 IPv4, 0 IPv6 valid entries found
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,643 - __main__ - INFO - Fetching Binary Defense blocklist
Aug 17 06:03:41 gw vyos-ipblock[3639062]: 2025-08-17 06:03:41,643 - __main__ - INFO - Fetching URL: https://www.binarydefense.com/banlist.txt
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,391 - __main__ - INFO - Successfully fetched https://www.binarydefense.com/banlist.txt, response size: 43608 bytes>
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,410 - __main__ - INFO - Binary Defense: 3023 IPv4, 0 IPv6 valid entries found
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,410 - __main__ - INFO - Fetching AbuseIPDB blocklist
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,410 - __main__ - INFO - Fetching URL: https://api.abuseipdb.com/api/v2/blacklist
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,925 - __main__ - INFO - Successfully fetched https://api.abuseipdb.com/api/v2/blacklist, response size: 142737 byt>
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,985 - __main__ - INFO - AbuseIPDB: 9987 IPv4, 13 IPv6 valid entries found
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,985 - __main__ - INFO - Total raw entries collected: 14652 IPv4, 13 IPv6
Aug 17 06:03:42 gw vyos-ipblock[3639062]: 2025-08-17 06:03:42,985 - __main__ - INFO - Converting 14652 IPv4 entries to CIDR format
Aug 17 06:03:43 gw vyos-ipblock[3639062]: 2025-08-17 06:03:43,132 - __main__ - INFO - Successfully converted 14652 IPv4 entries to CIDR
Aug 17 06:03:43 gw vyos-ipblock[3639062]: 2025-08-17 06:03:43,133 - __main__ - INFO - Converting 13 IPv6 entries to CIDR format
Aug 17 06:03:43 gw vyos-ipblock[3639062]: 2025-08-17 06:03:43,133 - __main__ - INFO - Successfully converted 13 IPv6 entries to CIDR
Aug 17 06:03:43 gw vyos-ipblock[3639062]: 2025-08-17 06:03:43,133 - __main__ - INFO - Deduplicating and filtering 14652 IPv4 CIDR entries
Aug 17 06:06:32 gw vyos-ipblock[3639062]: 2025-08-17 06:06:32,813 - __main__ - INFO - Removed 2644 redundant IPv4 entries, 12008 unique entries remaining
Aug 17 06:06:32 gw vyos-ipblock[3639062]: 2025-08-17 06:06:32,814 - __main__ - INFO - Deduplicating and filtering 13 IPv6 CIDR entries
Aug 17 06:06:32 gw vyos-ipblock[3639062]: 2025-08-17 06:06:32,814 - __main__ - INFO - Removed 0 redundant IPv6 entries, 13 unique entries remaining
Aug 17 06:06:32 gw vyos-ipblock[3639062]: 2025-08-17 06:06:32,814 - __main__ - INFO - Applying IPv4 whitelist filter to 12008 entries
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,131 - __main__ - INFO - IPv4 whitelist filtering: removed 0 entries, 12008 entries remaining
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,131 - __main__ - INFO - Applying IPv6 whitelist filter to 13 entries
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,132 - __main__ - INFO - IPv6 whitelist filtering: removed 0 entries, 13 entries remaining
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,132 - __main__ - INFO - Final blocklist contains 12008 IPv4 and 13 IPv6 entries
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,135 - __main__ - INFO - Creating nftables rule file with 12008 IPv4 and 13 IPv6 entries
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,168 - __main__ - INFO - Created temporary nftables file: /tmp/tmplw7de829.nft
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,168 - __main__ - INFO - Applying nftables rule file: /tmp/tmplw7de829.nft
Aug 17 06:06:34 gw sudo[3639213]:     root : PWD=/ ; USER=root ; COMMAND=/usr/sbin/nft list set ip vyos_filter N_threats-blocklist-ipv4
Aug 17 06:06:34 gw sudo[3639213]: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=0)
Aug 17 06:06:34 gw sudo[3639213]: pam_unix(sudo:session): session closed for user root
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,306 - __main__ - INFO - Set N_threats-blocklist-ipv4 already exists
Aug 17 06:06:34 gw sudo[3639215]:     root : PWD=/ ; USER=root ; COMMAND=/usr/sbin/nft list set ip6 vyos_filter N6_threats-blocklist-ipv6
Aug 17 06:06:34 gw sudo[3639215]: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=0)
Aug 17 06:06:34 gw sudo[3639215]: pam_unix(sudo:session): session closed for user root
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,328 - __main__ - INFO - Set N6_threats-blocklist-ipv6 already exists
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,328 - __main__ - INFO - Flushing ip nftables set N_threats-blocklist-ipv4
Aug 17 06:06:34 gw sudo[3639217]:     root : PWD=/ ; USER=root ; COMMAND=/usr/sbin/nft flush set ip vyos_filter N_threats-blocklist-ipv4
Aug 17 06:06:34 gw sudo[3639217]: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=0)
Aug 17 06:06:34 gw sudo[3639217]: pam_unix(sudo:session): session closed for user root
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,407 - __main__ - INFO - Successfully flushed ip nftables set N_threats-blocklist-ipv4
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,407 - __main__ - INFO - Flushing ip6 nftables set N6_threats-blocklist-ipv6
Aug 17 06:06:34 gw sudo[3639219]:     root : PWD=/ ; USER=root ; COMMAND=/usr/sbin/nft flush set ip6 vyos_filter N6_threats-blocklist-ipv6
Aug 17 06:06:34 gw sudo[3639219]: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=0)
Aug 17 06:06:34 gw sudo[3639219]: pam_unix(sudo:session): session closed for user root
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,454 - __main__ - INFO - Successfully flushed ip6 nftables set N6_threats-blocklist-ipv6
Aug 17 06:06:34 gw sudo[3639221]:     root : PWD=/ ; USER=root ; COMMAND=/usr/sbin/nft -f /tmp/tmplw7de829.nft
Aug 17 06:06:34 gw sudo[3639221]: pam_unix(sudo:session): session opened for user root(uid=0) by (uid=0)
Aug 17 06:06:34 gw sudo[3639221]: pam_unix(sudo:session): session closed for user root
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,710 - __main__ - INFO - Successfully applied nftables rules
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,710 - __main__ - INFO - Blocklist successfully applied to nftables
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,710 - __main__ - INFO - Applied 12008 IPv4 and 13 IPv6 entries
Aug 17 06:06:34 gw vyos-ipblock[3639062]: 2025-08-17 06:06:34,716 - __main__ - INFO - === Blocklist generation completed successfully ===
Aug 17 06:06:34 gw systemd[1]: vyos-ipblock.service: Deactivated successfully.
Aug 17 06:06:34 gw systemd[1]: Finished vyos-ipblock.service - VyOS IP Blocklist Generator.
Aug 17 06:06:34 gw systemd[1]: vyos-ipblock.service: Consumed 2min 52.345s CPU time.

No sync:

11:53 vyos@gw 1.4.2 /
✎ edit » show firewall group network-group threats-blocklist-ipv4
Configuration under specified path is empty

11:56 vyos@gw 1.4.2 /
✎ edit » show firewall group ipv6-network-group threats-blocklist-ipv6
Configuration under specified path is empty

The sets are active:

» sudo nft list sets | grep 'threat' -A 5
        set N_threats-blocklist-ipv4 {
                type ipv4_addr
                flags interval
                auto-merge
                elements = { 1.2.133.249, 1.2.164.34,
                             1.10.16.0/20, 1.10.219.85,
--
        set N6_threats-blocklist-ipv6 {
                type ipv6_addr
                flags interval
                auto-merge
                elements = { 2001:41d0:33a:a00::400/127,
                             2001:41d0:33a:a00::403,

Works fine:

» sudo journalctl --since "2 days ago" --no-hostname --boot -k | egrep "ip.*-5-D" | wc -l
6791

The service unit only runs /usr/bin/vyos-ipblock which (cursory reading!) doesn’t seem to do a sync to VyOS config? Your README still mentions a separate sync script?

@CharlieRoot is there or is there not a separate sync going on between the elements in nftables and the VyOS configuration? If not, please:

  • remove references to a (apparently unneeded) sync script from the README.
  • remove or reprhase the “# 4. If using VyOS groups, verify they’re populated”-part, because without some sort of config sync, you will only see the names of the groups and not their elements.

Thank you for your time putting this together.

You are correct, we removed the sync script because we ran in to issues with commits hitting a timeout. I’ve updated the readme.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.