From 7d5c71e95694b030644a52cb246e4b700ce8d114 Mon Sep 17 00:00:00 2001 From: ghostersk <68815071+ghostersk@users.noreply.github.com> Date: Wed, 4 Jun 2025 20:40:07 +0100 Subject: [PATCH] Create geoip-firewall-simplified.sh --- Linux/geoip-firewall-simplified.sh | 256 +++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 Linux/geoip-firewall-simplified.sh diff --git a/Linux/geoip-firewall-simplified.sh b/Linux/geoip-firewall-simplified.sh new file mode 100644 index 0000000..6eb171e --- /dev/null +++ b/Linux/geoip-firewall-simplified.sh @@ -0,0 +1,256 @@ +#!/bin/bash +# === ๐Ÿ› ๏ธ DEFAULT CONFIGURATION === +DEFAULT_COUNTRIES=("gb") # "gb" "us" +DEFAULT_MANUAL_IPS=() # "203.0.113.10" "198.51.100.0/24" +DEFAULT_TCP_PORTS=() # Empty means all TCP ports by default +DEFAULT_UDP_PORTS=() # Empty means all UDP ports by default +IPSET_SAVE_PATH="/etc/ipset.conf" + +# === ๐ŸŒ Constants === +GEOIP_DIR="/usr/share/xt_geoip" +IPSET_NAME="geo-allowed" +CRON_JOB_PATH="/etc/cron.weekly/update-xt-geoip" +SYSTEMD_IPSET_SERVICE="/etc/systemd/system/ipset-restore.service" +SCRIPT_NAME="$(basename "$0")" + +# === Runtime Config === +COUNTRIES=("${DEFAULT_COUNTRIES[@]}") +MANUAL_IPS=("${DEFAULT_MANUAL_IPS[@]}") +TCP_PORTS=("${DEFAULT_TCP_PORTS[@]}") +UDP_PORTS=("${DEFAULT_UDP_PORTS[@]}") +REMOVE=false + +# === Help === +usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --countries gb,us Comma-separated list of country codes to allow" + echo " --manual-ips ip1,ip2 Comma-separated list of manual IPs/ranges to allow" + echo " --tcp-ports 25,587 Restrict GeoIP filtering to specific TCP ports (default: all)" + echo " --udp-ports 53,123 Restrict GeoIP filtering to specific UDP ports (default: all)" + echo " --remove Remove all rules and ipset" + echo " -h, --help Show this help message" + exit 1 +} + +# === Parse CLI Arguments === +while [[ "$#" -gt 0 ]]; do + case "$1" in + --countries) IFS=',' read -r -a COUNTRIES <<< "$2"; shift ;; + --manual-ips) IFS=',' read -r -a MANUAL_IPS <<< "$2"; shift ;; + --tcp-ports) IFS=',' read -r -a TCP_PORTS <<< "$2"; shift ;; + --udp-ports) IFS=',' read -r -a UDP_PORTS <<< "$2"; shift ;; + --remove) REMOVE=true ;; + -h|--help) usage ;; + *) echo "Unknown option: $1"; usage ;; + esac + shift +done + +# === Error Handling === +check_command() { + command -v "$1" >/dev/null 2>&1 || { echo "Error: $1 is required but not installed."; exit 1; } +} + +# === Cleanup Function === +remove_firewall_rules() { + echo "๐Ÿ”ง Removing firewalld rules..." + + # TCP + if [ ${#TCP_PORTS[@]} -eq 0 ]; then + firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 \ + -p tcp -m set ! --match-set "$IPSET_NAME" src -j DROP 2>/dev/null || true + else + for port in "${TCP_PORTS[@]}"; do + firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 \ + -p tcp --dport "$port" -m set ! --match-set "$IPSET_NAME" src -j DROP 2>/dev/null || true + done + fi + + # UDP + if [ ${#UDP_PORTS[@]} -eq 0 ]; then + firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 \ + -p udp -m set ! --match-set "$IPSET_NAME" src -j DROP 2>/dev/null || true + else + for port in "${UDP_PORTS[@]}"; do + firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 \ + -p udp --dport "$port" -m set ! --match-set "$IPSET_NAME" src -j DROP 2>/dev/null || true + done + fi + + echo "๐Ÿงผ Flushing and destroying ipset..." + ipset flush "$IPSET_NAME" 2>/dev/null || true + ipset destroy "$IPSET_NAME" 2>/dev/null || true + + echo "๐Ÿ—‘๏ธ Removing cron job at $CRON_JOB_PATH" + rm -f "$CRON_JOB_PATH" + + echo "๐Ÿ—‘๏ธ Removing ipset persistence configuration" + rm -f "$IPSET_SAVE_PATH" + systemctl disable ipset-restore.service 2>/dev/null || true + rm -f "$SYSTEMD_IPSET_SERVICE" + + echo "โ™ป๏ธ Reloading firewalld..." + firewall-cmd --reload || { echo "Error: Failed to reload firewalld."; exit 1; } + + echo "โœ… Firewall rules and GeoIP ipset removed." +} + +if [ "$REMOVE" = true ]; then + remove_firewall_rules + exit 0 +fi + +# === Detect Package Manager === +if command -v apt >/dev/null 2>&1; then + PKG_MANAGER="apt" +elif command -v yum >/dev/null 2>&1; then + PKG_MANAGER="yum" +elif command -v dnf >/dev/null 2>&1; then + PKG_MANAGER="dnf" +else + echo "Error: No supported package manager (apt/yum/dnf) found." + exit 1 +fi + +# === Install Dependencies === +echo "๐Ÿ“ฆ Installing dependencies..." +if [ "$PKG_MANAGER" = "apt" ]; then + apt update && apt install -y ipset xtables-addons-common libtext-csv-xs-perl wget || { echo "Error: Failed to install dependencies."; exit 1; } +elif [ "$PKG_MANAGER" = "yum" ] || [ "$PKG_MANAGER" = "dnf" ]; then + $PKG_MANAGER install -y ipset xtables-addons perl-Text-CSV_XS wget || { echo "Error: Failed to install dependencies."; exit 1; } +fi + +# === Check Required Commands === +check_command ipset +check_command firewall-cmd +check_command wget + +# === Verify iptables-legacy Backend === +if firewall-cmd --get-ipset-types | grep -q "hash:net"; then + echo "โœ… ipset hash:net supported by firewalld." +else + echo "Error: ipset hash:net not supported. Ensure xt_geoip module is loaded." + exit 1 +fi + +# === Locate xtables-addons Binaries === +XTABLES_DIR="" +for dir in /usr/libexec/xtables-addons /usr/lib/xtables-addons /usr/sbin /usr/bin; do + if [ -f "$dir/xt_geoip_dl" ] && [ -f "$dir/xt_geoip_build" ]; then + XTABLES_DIR="$dir" + break + fi +done +if [ -z "$XTABLES_DIR" ]; then + echo "Error: Could not find xt_geoip_dl and xt_geoip_build. Ensure xtables-addons is installed correctly." + exit 1 +fi + +# === Download and Build GeoIP DB === +echo "๐Ÿ”ง Building GeoIP database..." +mkdir -p "$GEOIP_DIR" || { echo "Error: Failed to create $GEOIP_DIR."; exit 1; } +cd "$XTABLES_DIR" || { echo "Error: Failed to change to $XTABLES_DIR."; exit 1; } +./xt_geoip_dl || { echo "Error: Failed to download GeoIP data."; exit 1; } +./xt_geoip_build -D "$GEOIP_DIR" GeoIPCountryWhois.csv || { echo "Error: Failed to build GeoIP database."; exit 1; } + +# === Create and Populate ipset === +echo "๐Ÿงฐ Creating ipset: $IPSET_NAME" +ipset destroy "$IPSET_NAME" 2>/dev/null || true +ipset create "$IPSET_NAME" hash:net || { echo "Error: Failed to create ipset $IPSET_NAME."; exit 1; } + +echo "๐ŸŒ Adding country IPs..." +for cc in "${COUNTRIES[@]}"; do + echo " -> $cc" + wget -q -O "/tmp/${cc}.zone" "https://www.ipdeny.com/ipblocks/data/countries/${cc}.zone" || { echo "Error: Failed to download IP list for $cc."; exit 1; } + while IFS= read -r ip; do + [[ -z "$ip" ]] && continue + ipset add "$IPSET_NAME" "$ip" || { echo "Error: Failed to add $ip to ipset."; exit 1; } + done < "/tmp/${cc}.zone" + rm -f "/tmp/${cc}.zone" +done + +echo "โž• Adding manual IPs..." +for ip in "${MANUAL_IPS[@]}"; do + ipset add "$IPSET_NAME" "$ip" || { echo "Error: Failed to add manual IP $ip to ipset."; exit 1; } +done + +# === Save ipset for Persistence === +echo "๐Ÿ’พ Saving ipset to $IPSET_SAVE_PATH for persistence..." +ipset save "$IPSET_NAME" > "$IPSET_SAVE_PATH" || { echo "Error: Failed to save ipset."; exit 1; } + +# === Create systemd Service for ipset Restore === +echo "๐Ÿ› ๏ธ Creating systemd service for ipset persistence..." +cat < "$SYSTEMD_IPSET_SERVICE" +[Unit] +Description=Restore ipset on boot +After=network.target firewalld.service + +[Service] +Type=oneshot +ExecStart=/usr/sbin/ipset restore -f $IPSET_SAVE_PATH +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable ipset-restore.service || { echo "Error: Failed to enable ipset-restore service."; exit 1; } + +# === Add firewalld rules === +echo "๐Ÿ”ฅ Adding firewalld rules..." + +# TCP +if [ ${#TCP_PORTS[@]} -eq 0 ]; then + echo " -> All TCP ports" + firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \ + -p tcp -m set ! --match-set "$IPSET_NAME" src -j DROP || { echo "Error: Failed to add TCP rule."; exit 1; } +else + for port in "${TCP_PORTS[@]}"; do + echo " -> TCP $port" + firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \ + -p tcp --dport "$port" -m set ! --match-set "$IPSET_NAME" src -j DROP || { echo "Error: Failed to add TCP rule for port $port."; exit 1; } + done +fi + +# UDP +if [ ${#UDP_PORTS[@]} -eq 0 ]; then + echo " -> All UDP ports" + firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \ + -p udp -m set ! --match-set "$IPSET_NAME" src -j DROP || { echo "Error: Failed to add UDP rule."; exit 1; } +else + for port in "${UDP_PORTS[@]}"; do + echo " -> UDP $port" + firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 \ + -p udp --dport "$port" -m set ! --match-set "$IPSET_NAME" src -j DROP || { echo "Error: Failed to add UDP rule for port $port."; exit 1; } + done +fi + +# === Cron Job for Weekly GeoIP Update === +echo "๐Ÿ•’ Installing weekly cron job at $CRON_JOB_PATH" +cat < "$CRON_JOB_PATH" +#!/bin/bash +cd "$XTABLES_DIR" || exit 1 +./xt_geoip_dl || exit 1 +./xt_geoip_build -D "$GEOIP_DIR" GeoIPCountryWhois.csv || exit 1 +ipset flush "$IPSET_NAME" +for cc in ${COUNTRIES[*]}; do + wget -q -O "/tmp/\${cc}.zone" "https://www.ipdeny.com/ipblocks/data/countries/\${cc}.zone" || exit 1 + while IFS= read -r ip; do + [[ -z "\$ip" ]] && continue + ipset add "$IPSET_NAME" "\$ip" || exit 1 + done < "/tmp/\${cc}.zone" + rm -f "/tmp/\${cc}.zone" +done +for ip in ${MANUAL_IPS[*]}; do + ipset add "$IPSET_NAME" "\$ip" || exit 1 +done +ipset save "$IPSET_NAME" > "$IPSET_SAVE_PATH" || exit 1 +EOF +chmod +x "$CRON_JOB_PATH" || { echo "Error: Failed to create cron job."; exit 1; } + +# === Reload firewalld === +echo "๐Ÿ”„ Reloading firewalld..." +firewall-cmd --reload || { echo "Error: Failed to reload firewalld."; exit 1; } + +echo "โœ… GeoIP firewall filtering is now active."