πΏπ¦ cntgate - script to block access to all countries except South Africa for a host π
Script to accept only South African connections on a Linux host using nftables
This is a script to all access to a Linux host except if it is from South Africa:
#!/bin/bash
# Extra cntgate: A country blocking script by r00igev@@r/mybroadband
# Version 0.0.4 February 2024
# usage extra-cntgate.sh <configuration file>
# eg: extra-cntgate.sh /etc/extra/extra-cntgate.conf
# Location: /usr/local/sbin/extra-cntgate.sh
PATH=/usr/sbin:/usr/local/bin:/usr/bin:/bin:
function exists() { command -v "$1" >/dev/null 2>&1 ; }
# Check that configuration file has been specified
if [[ -z "$1" ]]; then
echo "Error: please specify a configuration file, e.g. $0 /etc/extra/extra-cntgate.conf"
exit 1
fi
# Shellcheck source=extra-cntgate.conf
if ! source "$1"; then
echo "Error: can't load configuration file $1"
exit 1
fi
# Check if all commands exist
if ! exists curl && exists egrep && exists grep && exists nft && exists sed && exists sort && exists wc && exists iprange && figlet ; then
echo >&2 "Error: searching PATH fails to find executables among: curl egrep grep nft sed sort wc iprange figlet"
exit 1
fi
figlet -f slant Extra cntgate
echo "Extra enabled edge. Driving SD-WAN adoption in South Africa! https://fusionsdwan.co.za"
# Check if BLOCK_POLICY is defined
if [ -z "$BLOCK_POLICY" ]; then
echo "Error: BLOCK_POLICY is not defined. Please set the BLOCK_POLICY variable."
exit 1
fi
# Do CIDR optimization if set
DO_OPTIMIZE_CIDR=no
if exists iprange && [[ ${OPTIMIZE_CIDR:-yes} != no ]]; then
DO_OPTIMIZE_CIDR=yes
fi
# Remove comments from exceptions files
if [ -f "$EXTERNAL_BLOCKLIST_EXCEPTIONS" ]; then
EXTERNAL_EXCEPTIONS_TMP=$(mktemp)
for exception in $(sed -r -e 's/\s*#.*$//;/^$/d' "$EXTERNAL_BLOCKLIST_EXCEPTIONS")
do
exception_array+=( "$exception" )
echo $exception >> $EXTERNAL_EXCEPTIONS_TMP
done
echo "Created temporary exceptions at $EXTERNAL_EXCEPTIONS_TMP."
fi
# Create the nftables set if needed (or abort if does not exist and FORCE=no)
# Check if the template file exists
if [ ! -f "$NFT_TEMPLATE" ]; then
echo "Error: Template file not found at $NFT_TEMPLATE."
exit 1
fi
# Use the nft command to delete the table
nft delete table ip "$BLOCKLIST_NAME"redirect 2>/dev/null
nft delete table inet "$BLOCKLIST_NAME" 2>/dev/null
echo "nftables table '$BLOCKLIST_NAME' dropped."
# Read the template file, replaces ${BLOCK_POLICY} with the actual value, and create the nftables table
nft -f <(sed -e "s/\${BLOCK_POLICY}/$BLOCK_POLICY/g" -e "s/\${WAN_IF}/$WAN_IF/g" -e "s/\${CNT_CACHE}/$CNT_CACHE/g" "$NFT_TEMPLATE")
echo "nftables table '$BLOCKLIST_NAME' with block policy $BLOCK_POLICY."
BLOCKLIST_TMP=$(mktemp)
echo -n "Downloading blocklists:"
for i in "${BLOCKLISTS[@]}"
do
IP_TMP=$(mktemp)
(( HTTP_RC=$(curl -L -A "extra/script/mybroadband" --connect-timeout 10 --max-time 10 -o "$IP_TMP" -s -w "%{http_code}" "$i") ))
if (( HTTP_RC == 200 || HTTP_RC == 302 || HTTP_RC == 0 )); then # "0" because file:/// returns 000
command grep -Po '^(?:\d{1,3}\.){3}\d{1,3}(?:/\d{1,2})?' "$IP_TMP" | sed -r 's/^0*([0-9]+)\.0*([0-9]+)\.0*([0-9]+)\.0*([0-9]+)$/\1.\2.\3.\4/' >> "$BLOCKLIST_TMP"
[[ ${VERBOSE:-yes} == yes ]] && echo -n "."
elif (( HTTP_RC == 503 )); then
echo -e "\\nUnavailable (${HTTP_RC}): $i"
else
echo >&2 -e "\\nWarning: curl returned HTTP response code $HTTP_RC for URL $i"
fi
rm -f "$IP_TMP"
done
# Optimize CIDR
if [[ ${OPTIMIZE_CIDR} == yes ]]; then
if [[ ${VERBOSE:-no} == yes ]]; then
echo -e "\\nAddresses before CIDR optimization: $(wc -l "$BLOCKLIST_TMP" | cut -d' ' -f1)"
fi
< "$BLOCKLIST_TMP" iprange --optimize - > "$BLOCKLIST" # 2>/dev/null
if [[ ${VERBOSE:-no} == yes ]]; then
echo "Addresses after CIDR optimization: $(wc -l "$BLOCKLIST" | cut -d' ' -f1)"
fi
fi
rm -f "$BLOCKLIST_TMP"
echo -n "Populating nft cntgate:"
# Define the name of the set and the filename containing IPs/subnets
set_name="hexceptions-v4"
blocklist_file="$EXTERNAL_EXCEPTIONS_TMP"
# Read the internal exceptions file line by line
while IFS= read -r line; do
# Add the IP/subnet to the nftables set 2>/dev/null
nft add element inet cntgate "$set_name" { $line }
# Increment the count
done < "$blocklist_file"
rm "$EXTERNAL_EXCEPTIONS_TMP"
# Define the name of the set and the filename containing IPs/subnets
set_name="cntlist-v4"
blocklist_file="$BLOCKLIST"
# Count for printing dots
count=0
# Number of IPs to add in each batch - too big and nft will not load it
batch_size=512
# Initialize a string to store IPs with commas
ip_batch=""
# Read the blocklist file line by line
while IFS= read -r line; do
# Add the IP to the batch with a comma separator
if [ -z "$ip_batch" ]; then
ip_batch="$line"
else
ip_batch="$ip_batch,$line"
fi
# If the batch size is reached, add IPs and print a dot
if (( count % batch_size == batch_size - 1 )); then
nft add element inet cntgate "$set_name" { $ip_batch } 2>/dev/null
((count += 1))
echo -n "."
ip_batch="" # Clear the batch
fi
((count++))
done < "$blocklist_file"
# Add any remaining IPs in the last batch
if [ -n "$ip_batch" ]; then
nft add element inet cntgate "$set_name" { $ip_batch } 2>/dev/null
((count++))
echo -n "."
fi
if [[ ${VERBOSE:-no} == yes ]]; then
echo "cntgate completed. $count IPs/subnets added to $set_name."
fi
The configuration file:
# Location: /etc/extra/extra-cntgate.conf
BLOCKLIST_NAME=cntgate # Table used within nftables
BLOCKLIST_TMP=${BLOCKLIST_NAME}-tmp
BLOCKLIST_DIR=/etc/extra
BLOCKLIST=${BLOCKLIST_DIR}/extra-cntgate.list
EXTERNAL_BLOCKLIST_EXCEPTIONS=${BLOCKLIST_DIR}/external-${BLOCKLIST_NAME}.exceptions
NFT_TEMPLATE=${BLOCKLIST_DIR}/nft-cntgate.template # Template file for nftables rules
# Interfaces
BLOCK_INTERFACE=eth0
WAN_IF=eth0
VERBOSE=yes # Set to no for cron jobs, default to yes
FORCE=yes # Will create the nft table if it does not already exist
OPTIMIZE_CIDR=yes # Optimize block list by aggregating overlapping subnets
# Block policy: 'accept', 'drop' or 'reject', default: 'drop'
BLOCK_POLICY=accept
# cnt Blocklists of IPs being accepted
BLOCKLISTS=(
"file://${BLOCKLIST_DIR}/${BLOCKLIST_NAME}-custom.list" # Optional, for your personal nemeses
"https://raw.githubusercontent.com/ipverse/rir-ip/master/country/za/ipv4-aggregated.txt" # ZA Country list
)
MAXELEM=131072
The exceptions file (IP being excepted - same as whitelist - these addresses will always be accepted):
# Location: /etc/extra/external-cntgate.exceptions
1.1.1.0/24
The nft template file named nft-cntgate.template is also located in /etc/extra
table inet cntgate {
set hexceptions-v4 {
type ipv4_addr
flags interval
auto-merge
}
set hexceptions-v6 {
type ipv6_addr
flags interval
auto-merge
}
set cntlist-v4 {
type ipv4_addr
flags interval
auto-merge
}
set cntlist-v6 {
type ipv6_addr
flags interval
auto-merge
}
chain input {
type filter hook input priority -1; policy accept;
iifname lo accept
ct state established,related accept
iifname "${WAN_IF}" ip saddr @hexceptions-v4 counter accept
iifname "${WAN_IF}" ip6 saddr @hexceptions-v6 counter accept
iifname "${WAN_IF}" ip saddr @cntlist-v4 counter ${BLOCK_POLICY}
iifname "${WAN_IF}" ip6 saddr @cntlist-v6 counter ${BLOCK_POLICY}
iifname "${WAN_IF}" counter drop
}
}
You can do a daily update using this crontab -e:
#
# m h dom mon dow command
@daily /usr/local/sbin/extra-cntgate.sh /etc/extra/extra-cntgate.conf
Ronald works connecting Internet inhabiting things at Fusion Broadband.
Β