Block or allow countries using iptables, ipset and ipdeny.com
- Supports RH and Debian with iptables, nftables and firewalld
- Also works with ipverse.com and other block list providers
- Both ipv4 and ipv6 are supported
- Blocks incoming traffic only
Setup firewall first, if you have not done so yet. At least an input chain is needed.
Then run this script manually and if all is well, add to cron (e.g. /etc/cron.daily) or systemd service.
To automatically setup a daily systemd timer, run ipset-country -i.
To uninstall, run: ipset-country -u.
Or to run script on boot, add it to rc (e.g. '/etc/rc.local').
Running this script will add rules to your firewall. Make sure you do not lock yourself out in case of issues on a remote system.
Note that all options and settings are explained in the script itself, see top of ipset-country
Optionally, you can use a separate config file located in the same directory as the script. To specify a custom location use ipset-country -c /path/to/conf (like "/etc/ipset-country.conf" or "/usr/local/etc/ipset-country.conf".)
The config file will overwrite any options set in script. To create a new conf file, run:
sed -n '/# CONFIGURATION:/,/# END OF CONFIG/p' ipset-country > ipset-country.confIf needed, change OS using DISTRO setting. Default is "auto", which should usually work OK.
Options are:
- "auto", "debian" or "redhat"
- "manual"
confdir="/etc/iptables"(example)rulesfile="${confdir}/myrules"(example)
Specify countries to block as "ISOCODE,Name" (same as ipdeny.com), multiple entries should be separated by semicolon ;
Example:
COUNTRY="CN,China; US,United States; RU,Russia"
In case of issues check the log file '/var/log/ipset-country.log'.
To change log file location, set: LOG="/path/to/log"
Or, log to screen only: LOG="/dev/stdout"
Disable all logging: LOG="/dev/null 2>&1"
Set option FIREWALL to: "iptables", "ntftables" or "firewalld"
Default is "iptables".
To block specified Countries, set MODE to target "reject" or "drop" (blacklist).
To allow specified Countries and block all others, set MODE to "accept" (whitelist).
Default is "reject".
Note that outbound traffic is not blocked, and this script does not support it. If you really want to do that anyways, you could edit the script and add rules in OUTPUT chain for iptables or for nftables filter output. FirewallD does not block outbound traffic and would require --direct entries or you'd have to create a new policy. None of this was tested.
The script will add iptables chains, rules and sets (needs ipset).
Change DENY_RULENUM to "1" to insert 'deny' rule at beginning of existing rules.
Or, set a specific rule number (see iptables --numeric -L INPUT --line-numbers)
Default is "0" (add at end).
Uses nft with native sets. Needs at least a table and "input" chain already setup.
Minimal ipv4 example:
nft add table ip filter
nft add chain ip filter input \{ type filter hook input priority 0\; \}
table ip filter {
chain input {
type filter hook input priority filter; policy accept;
# ...
}
}
To use optional location specifier of an existing rule set NFT_RULE_LOC.
Example: NFT_RULE_LOC="handle 3" or NFT_RULE_LOC="index 5"
Default is empty/unset (e.i. NFT_RULE_LOC=""), which appends rule.
See nft --handle list ruleset or man nft for more details.
FirewallD does not support LOGIPS=1 or MODE='reject"
Set MODE to "drop" or "accept":
- if mode is "accept", ipset will be added to "drop zone" as allowed (whitelist)
- if mode is "drop", ipset will added to "public zone" as denied (blacklist)
There are issues with firewalld and nft on CentOS/RHEL 8 which can cause your firewall to break resulting in being locked out. Adding large ipsets apparently can take a VERY long time. To abort, you need remote console access and run:
pkill firewal-cmd; nft flush ruleset
Unsupported frontend. Apparently it is possible to run both iptables and ufw and mix rules. Enable with
UFW=1(untested).
Set URLs for ipv4 and/or ipv6 block files, you probably do not have to change these.
By default ipdeny.com is used
IPBLOCK_URL_V4="http://www.ipdeny.com/ipblocks/data/aggregated"
IPBLOCK_URL_V6="http://www.ipdeny.com/ipv6/ipaddresses/blocks"
To change to ipverse, set:
IPBLOCK_URL="https://raw.githubusercontent.com/ipverse/rir-ip/master/country"
Add argument -f to load unchanged zonefiles instead of skipping
For more details see inside script.
Useful commands to check and clear blocked ips
ipset listipset test setname <ip>ipset flushipset destroy
nft --handle list rulesetnft list table ip filternft list setsnft list set ip filter <proto>-<country>(e.g. 'ipv4-china')nft list chain ip filter inputnft flush set ip filter <proto>-<country>nft delete set ip filter <proto>-<country>
- [20250924] changed iptables
-p tcptoall - [20250729] add suport for nftables
- [20250721] add option to inject rejct rule on specific rulenum (pr #22 by miathedev)
- [20220227] fixed iptables-legacy paths (pr #16 by mainboarder)
- [20201212] added config file option, systemd install (pr #14 by srulikuk)
- [20201108] added flush option, fix restore=0 (pr #13 by srulikuk)
- [20200927] fixed restore + logips bug (pr #10 by G4bbix)
- [20200605] added Blacklist/Whitelist mode (#3)
- [20200129] added option to DROP instead of REJECT (#1)
- [20191116] added ipverse support, md5check option
- [20190905] tested on debian 10 and centos 7
- [20190905] blocking multiple countries should work
- [20190905] it will check if INPUT chain exists in iptables
- [20190905] cleaned it up a bit
- [20190905] using firewalld is also supported now
Also available: github.com/tokiclover/dotfiles/blob/master/bin/ips.bash