📢 Freshly defrosted from the GitHub Arctic Code Vault and upgraded from iptables to nftables. Yes, the nftables syntax is... an acquired taste. But I finally bow to the netfilter overlords.
Early stage - expect bugs.
A Bash script that downloads public IP blacklists and blocks them via nftables. IPv4, IPv6, CIDR.
Downloading blacklists..............................................
Downloaded 43 of 43 blacklists
Processing IPv4 addresses...
CIDR optimization: 104791 → 66178 entries
Processing IPv6 addresses...
Auto-detecting server IPs for whitelist...
Whitelisted: 2001:db8:305:2100::1
Whitelisted: 203.0.113.10
Applying IPv4 whitelist...
Whitelist applied: 66178 → 66178 entries
Applying IPv6 whitelist...
Note: IPv6 whitelist uses exact matching only
Whitelist applied: 23927 → 23927 entries
Generating nftables script...
Applying nftables rules...
Blacklist update complete
IPv4: 66178 IPv6: 23927 Total: 90105
Looking for the old ipset/iptables version? See the archive/ folder.
- Features
- Requirements
- Quick Start (Debian/Ubuntu)
- Persistence Across Reboots
- Automatic Updates (Systemd Timer)
- Check Dropped Packets
- Configuration Options
- Customizing Blacklists
- Whitelist (Prevent Self-Blocking)
- Dry Run Mode
- Troubleshooting
- Migrating from the old ipset/iptables version
- Uninstall
- Contributing
- Uses nftables instead of the deprecated iptables/ipset combo
- IPv4 and IPv6, including CIDR notation
- Overlapping ranges are merged automatically (e.g., two /24s become one /23)
- Atomic updates — the blacklist is swapped in one transaction
- Handles 100k+ blocked IPs without breaking a sweat
- Auto-whitelist detects your server's IPs to prevent self-blocking
- Debian 10+ / Ubuntu 20.04+ (or any Linux with nftables)
- nftables
- iprange - combines overlapping IP ranges and handles whitelist subtraction
- curl, grep, sed, sort, wc (usually pre-installed)
-
Install helper tools:
sudo apt install curl iprange
-
Download the script:
sudo curl -fsSL -o /usr/local/sbin/update-blacklist.sh \ https://raw.githubusercontent.com/trick77/nftables-blacklist/master/update-blacklist.sh sudo chmod +x /usr/local/sbin/update-blacklist.sh
-
Create configuration directory and download config:
sudo mkdir -p /etc/nftables-blacklist if [ -f /etc/nftables-blacklist/nftables-blacklist.conf ]; then echo "Config already exists, skipping download" else sudo curl -fsSL -o /etc/nftables-blacklist/nftables-blacklist.conf \ https://raw.githubusercontent.com/trick77/nftables-blacklist/master/nftables-blacklist.conf fi
-
Edit configuration (optional):
sudo nano /etc/nftables-blacklist/nftables-blacklist.conf
-
Run initial update:
sudo /usr/local/sbin/update-blacklist.sh /etc/nftables-blacklist/nftables-blacklist.conf
-
Verify it's working:
# List the blacklist table sudo nft list table inet blacklist # Show IPv4 set contents sudo nft list set inet blacklist blacklist4 # Show IPv6 set contents sudo nft list set inet blacklist blacklist6 # Check drop counters sudo nft list chain inet blacklist input
sudo cat <<'EOF' > /etc/systemd/system/nftables-blacklist.service
[Unit]
Description=nftables IP blacklist
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/update-blacklist.sh /etc/nftables-blacklist/nftables-blacklist.conf
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOFEnable it:
sudo systemctl daemon-reload
sudo systemctl enable nftables-blacklist.servicesudo cat <<'EOF' > /etc/systemd/system/nftables-blacklist-update.timer
[Unit]
Description=Update nftables IP blacklist daily
[Timer]
OnCalendar=*-*-* 23:33:00
Persistent=true
RandomizedDelaySec=14400
[Install]
WantedBy=timers.target
EOFsudo cat <<'EOF' > /etc/systemd/system/nftables-blacklist-update.service
[Unit]
Description=Update nftables IP blacklist
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/update-blacklist.sh --cron /etc/nftables-blacklist/nftables-blacklist.conf
EOFEnable it:
sudo systemctl daemon-reload
sudo systemctl enable --now nftables-blacklist-update.timerCheck timer status:
systemctl list-timers nftables-blacklist-updateTip: Once or twice daily is enough. Updating too frequently may get you banned by blacklist providers.
sudo nft list chain inet blacklist inputExample output:
chain input {
type filter hook input priority -200; policy accept;
ip saddr @blacklist4 counter packets 1523 bytes 91380 drop comment "IPv4 blacklist"
ip6 saddr @blacklist6 counter packets 42 bytes 3360 drop comment "IPv6 blacklist"
}
Edit nftables-blacklist.conf. Key settings:
| Setting | Default | Description |
|---|---|---|
ENABLE_IPV4 |
yes | Block IPv4 addresses |
ENABLE_IPV6 |
yes | Block IPv6 addresses |
FORCE |
yes | Automatically create the nftables table/sets if they don't exist |
VERBOSE |
yes | Show progress output (use --cron flag to suppress) |
AUTO_WHITELIST |
no | Auto-detect and whitelist your server's own IPs (setting this to yes is recommended) |
BLOCK_FORWARD |
no | Also block blacklisted IPs in the forward chain — forwarded traffic (e.g. to containers) is NOT blocked unless set to yes |
NFT_CHAIN_PRIORITY |
-200 | When to check the blacklist (-200 = very early, before most other rules) |
CURL_CONNECT_TIMEOUT |
10 | Seconds to wait for blacklist server connection |
CURL_MAX_TIME |
30 | Maximum seconds per blacklist download |
Edit the BLACKLISTS array in the config file:
BLACKLISTS=(
# Your custom local list
"file:///etc/nftables-blacklist/custom.list"
# Public blacklists
"https://www.spamhaus.org/drop/drop.lasso"
"https://lists.blocklist.de/lists/all.txt"
# Ban an entire country (use country code like 'cn', 'ru', etc.)
# "https://raw.githubusercontent.com/ipverse/country-ip-blocks/master/country/ru/ipv4-aggregated.txt"
)Sometimes your server's IP (or a larger prefix containing it) may appear in a public blacklist. To prevent blocking yourself:
Edit the WHITELIST array in the config file:
WHITELIST=(
"203.0.113.10" # Your server IP
"203.0.113.0/24" # Your network range
"2001:db8::1" # IPv6 address
)For IPv4, whitelisting a range like 10.0.0.0/8 will correctly exclude all IPs in that range, even if the blacklist contains individual IPs like 10.1.2.3.
Note: IPv6 whitelist only matches exact addresses (CIDR ranges not supported for IPv6 whitelist).
To automatically whitelist your server's own IPs (recommended):
AUTO_WHITELIST=yesThis detects all local interface IPs and queries external services (o11.net, icanhazip.com) for your public IPs.
Test without actually applying rules:
update-blacklist.sh --dry-run /etc/nftables-blacklist/nftables-blacklist.confDownloads and processes everything but doesn't actually load the rules.
The script creates the nftables table/sets automatically when FORCE=yes (the default). If you see this error, make sure FORCE=yes in your config.
sudo nft get element inet blacklist blacklist4 '{ 1.2.3.4 }'
sudo nft get element inet blacklist blacklist6 '{ 2001:db8::1 }'The script uses its own nftables table (inet blacklist) and won't conflict with your existing firewall. The default priority (-200) means packets hit the blacklist before most other rules. Adjust NFT_CHAIN_PRIORITY if needed.
By default, the blacklist only hooks into the input chain, which protects the host itself. Forwarded traffic (e.g. to Docker containers) bypasses it entirely. The script will warn you if it detects existing forward chains in nftables while BLOCK_FORWARD is not configured. To extend protection to forwarded traffic, set BLOCK_FORWARD=yes in your configuration. This adds a forward chain that reuses the same IP sets — no data is duplicated. Set BLOCK_FORWARD=no to dismiss the warning.
With very large sets you may see "Message too long" or "No buffer space available" errors. Bump the kernel network buffer:
# Add to /etc/sysctl.d/99-nftables.conf
net.core.rmem_max = 8388608
net.core.rmem_default = 8388608
# Apply now
sysctl -p /etc/sysctl.d/99-nftables.conf- Remove the old ipset/iptables rules and clean up legacy files. The exact paths may vary depending on how you originally set it up — the commands below are examples based on the default configuration:
# Remove iptables rule and ipset iptables -D INPUT -m set --match-set blacklist src -j DROP ipset destroy blacklist # Remove old cron job rm -f /etc/cron.d/update-blacklist # Remove old script rm -f /usr/local/sbin/update-blacklist.sh # Before deleting, check for any custom blacklists or URLs you want to preserve: # - Custom blacklist file: /etc/ipset-blacklist/ip-blacklist-custom.list # - Custom blacklist URLs in: /etc/ipset-blacklist/ipset-blacklist.conf (BLACKLISTS array) # Back up anything you need, then remove the old config and data directory rm -rI /etc/ipset-blacklist
- Set up the new nftables version (see Quick Start)
Stale rule? If the
iptables -D ... --match-setcommand fails with "Set blacklist doesn't exist", the ipset is already gone but a stale rule remains. Delete it by rule number instead:iptables -L INPUT --line-numbers iptables -D INPUT <rule-number>
Note for Debian Trixie (and newer) users: On modern Debian,
iptablesis usually just a compatibility layer (iptables-nft) that translates commands to nftables under the hood. If you previously switched to the legacy iptables backend (iptables-legacy/update-alternatives --set iptables /usr/sbin/iptables-legacy), you'll need to switch back to the nftables backend for this script to work:update-alternatives --set iptables /usr/sbin/iptables-nft update-alternatives --set ip6tables /usr/sbin/ip6tables-nft
To completely remove nftables-blacklist from your system:
-
Stop and disable the systemd timer and service:
sudo systemctl disable --now nftables-blacklist-update.timer sudo systemctl disable --now nftables-blacklist.service
-
Remove the nftables blacklist table (drops all blacklist rules and sets at once):
sudo nft delete table inet blacklist
-
Remove systemd unit files:
sudo rm -f /etc/systemd/system/nftables-blacklist.service sudo rm -f /etc/systemd/system/nftables-blacklist-update.service sudo rm -f /etc/systemd/system/nftables-blacklist-update.timer sudo systemctl daemon-reload
-
Remove the script:
sudo rm -f /usr/local/sbin/update-blacklist.sh
-
Remove configuration and data:
sudo rm -rI /etc/nftables-blacklist
-
Remove sysctl tweaks (only if you added the buffer size workaround):
sudo rm -f /etc/sysctl.d/99-nftables.conf sudo sysctl --system
After these steps, no traces of the blacklist remain and all previously blocked traffic will flow normally again.
Found a bug? Pull requests with fixes are always welcome.
This project intentionally keeps a narrow focus, so install scripts, additional OS/distro support, and feature additions won't be merged. If you have something bigger in mind, you're welcome to fork and make it your own.