Come bloccare il traffico per paese su un firewall Linux con le regole GeoIP
Livello: Professionale
Tempo stimato: ~35 minuti
Obiettivo: Configurare regole firewall GeoIP su un server Linux per limitare l'accesso per paese, usando iptables con il modulo xtables-geoip oppure nftables con gli IP set, e automatizzare gli aggiornamenti in modo che gli elenchi restino attuali.
Introduzione
Se i vostri servizi sono rivolti a una regione specifica, non c'è motivo di accettare traffico dal resto del mondo. Il blocco geografico sul firewall di Linux consente di scartare i pacchetti in base al paese di origine prima che raggiungano l'applicazione, riducendo il rumore degli attacchi brute-force e la superficie di attacco. In questa guida si bloccherà il traffico per paese su un firewall Linux con le regole firewall GeoIP: prima con iptables e xtables-addons, poi con nftables e IP set basati su CIDR, compresi gli aggiornamenti automatici del database, così che il blocco degli IP per paese resti accurato.
Prerequisiti
Prima di iniziare, assicurarsi che siano soddisfatte le seguenti condizioni:
- Sistema operativo: Ubuntu 22.04/24.04 LTS, Debian 12/13, AlmaLinux/RHEL 9/10
- Accesso: accesso sudo o root al server
- Firewall: iptables 1.8+ con xtables-addons, oppure nftables 1.0+
- Pacchetti necessari:
curl,iptables,linux-headers-$(uname -r)(Debian/Ubuntu); repository EPEL abilitato (RHEL/AlmaLinux 9)
Su Debian 12+, Ubuntu 22.04+ e RHEL/AlmaLinux 9+, il comando iptables è un wrapper del backend nftables (iptables-nft). Il match -m geoip funziona correttamente con questo backend.
- Rete: una chiara comprensione dei paesi da cui i servizi devono accettare traffico
- Conoscenze richieste: buona padronanza della riga di comando di Linux, dei concetti di base del firewall e della modifica dei file
- Piano di emergenza: una console out-of-band funzionante (IPMI/KVM) oppure la modalità di rescue del provider, nel caso una regola blocchi l'accesso
Applicare regole firewall a livello di paese tramite una sessione SSH è intrinsecamente rischioso. Se si blocca per errore il proprio paese, si perde l'accesso. Tenere sempre pronto un accesso alternativo out-of-band e testare le regole prima di renderle permanenti.
Passaggio 1: Individuare i paesi consentiti e bloccati
Prima di toccare il firewall, definire la policy. Esistono due approcci:
- Allowlist (consigliato): consentire solo i paesi necessari e scartare tutto il resto. È più rigoroso e sicuro.
- Blocklist: consentire tutto e scartare paesi specifici. Più semplice, ma lascia una superficie di attacco maggiore.
In questa guida useremo l'approccio allowlist, coerente con il principio del privilegio minimo. Supponiamo che i servizi siano destinati solo a utenti di Stati Uniti, Germania e Polonia. I codici paese ISO di due lettere sono: US, DE, PL.
I codici paese seguono lo standard ISO 3166-1 alpha-2. L'elenco completo è disponibile all'indirizzo https://www.iso.org/obp/ui/#search/code/. Essere precisi: un errore di battitura blocca in silenzio il traffico legittimo.
Passaggio 2: Configurare GeoIP con iptables (xtables-addons)
Questo metodo usa il modulo xt_geoip di xtables-addons, che aggiunge a iptables il match -m geoip. È uno dei modi più consolidati per bloccare gli intervalli IP per paese su Linux.
2.1 Installare xtables-addons e le dipendenze
Debian/Ubuntu:
sudo apt install -y linux-headers-$(uname -r) sudo apt update && sudo apt install -y \ xtables-addons-common libtext-csv-xs-perl unzip iptables curl
xtables-addons non è disponibile nei repository di AlmaLinux 10. Usare invece il metodo nftables (Passaggio 3) oppure compilare xtables-addons dal codice sorgente.
Dopo l'installazione, verificare che il modulo sia disponibile:
modinfo xt_geoip
L'output previsto include una riga come:
filename: /lib/modules/.../xt_geoip.ko description: Xtables: country matching via GeoIP
Se modinfo restituisce un errore, il modulo non è stato installato correttamente. Verificare che gli header del kernel corrispondano alla versione del kernel in esecuzione.
2.2 Scaricare e generare il database GeoIP
Il modulo xt_geoip richiede un database locale con la corrispondenza tra paesi e indirizzi IP. Viene distribuito come file CSV che vanno convertiti in formato binario.
Creare la directory di lavoro e quella del database:
sudo mkdir -p /usr/share/xt_geoip
Scaricare i dati GeoIP CSV più recenti. La fonte originale fornisce i dati tramite gli strumenti di xtables-addons:
cd /tmp /usr/libexec/xtables-addons/xt_geoip_dl
Convertire il CSV scaricato in formato binario:
/usr/libexec/xtables-addons/xt_geoip_build -D /usr/share/xt_geoip *.csv
Output previsto: un elenco dei codici paese in elaborazione:
4540 IPv4 ranges for ZA 1071 IPv6 ranges for ZA 147 IPv4 ranges for ZW 94 IPv6 ranges for ZW ...
2.3 Applicare le regole iptables con match GeoIP
Creare ora le vere e proprie regole firewall GeoIP. Lo script seguente consente il traffico dai paesi scelti e scarta tutto il resto nella catena INPUT.
Eseguire prima il backup delle regole attuali:
sudo iptables-save > /tmp/iptables-backup-$(date +%Y%m%d).rules
Applicare la allowlist dei paesi:
# Allow loopback sudo iptables -A INPUT -i lo -j ACCEPT # Allow established and related connections sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Allow traffic from US, DE, PL only sudo iptables -A INPUT -m geoip --src-cc US,DE,PL -j ACCEPT # Log dropped packets sudo iptables -A INPUT -j LOG --log-prefix "GEOIP-DROP: " --log-level 4 # Drop everything else sudo iptables -A INPUT -j DROP
La regola finale DROP blocca tutto il traffico che non corrisponde alla allowlist. Assicurarsi che il paese di provenienza della connessione SSH sia incluso, altrimenti si verrà bloccati immediatamente. In caso di dubbio, aggiungere prima del DROP una regola ACCEPT temporanea per il proprio IP: sudo iptables -I INPUT -s <YOUR_IP_ADDRESS> -j ACCEPT.
Verificare che le regole siano attive:
sudo iptables -L INPUT -v --line-numbers
Output previsto:
Chain INPUT (policy ACCEPT) num target prot opt source destination 1 ACCEPT all -- anywhere anywhere /* loopback */ 2 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED 3 ACCEPT all -- anywhere anywhere -m geoip --source-country US,DE,PL 4 LOG all -- anywhere anywhere LOG level warning prefix "GEOIP-DROP: " 5 DROP all -- anywhere anywhere
2.4 Rendere persistenti le regole iptables dopo il riavvio
Le regole applicate con iptables vanno perse al riavvio. Salvarle:
Debian/Ubuntu:
sudo apt install iptables-persistent -y sudo netfilter-persistent save
AlmaLinux/RHEL:
sudo service iptables save
Passaggio 3: Configurare GeoIP con nftables (IP set)
Se il sistema usa nftables (predefinito su Debian 11+, Ubuntu 22.04+ e RHEL 9+), si può ottenere lo stesso risultato con IP set popolati con blocchi CIDR a livello di paese. Questo approccio non richiede moduli del kernel oltre a quelli già forniti da nftables.
3.1 Ottenere gli intervalli IP dei paesi
Scaricare gli elenchi CIDR dei paesi che si vogliono consentire. Diverse fonti pubbliche li mettono a disposizione, ad esempio ipdeny.com:
sudo mkdir -p /etc/nftables/geoip cd /etc/nftables/geoip # Download CIDR blocks for US, DE, PL for CC in us de pl; do sudo curl -sS -o /etc/nftables/geoip/${CC}.zone \ "https://www.ipdeny.com/ipblocks/data/aggregated/${CC}-aggregated.zone"; done
Verificare che i file contengano intervalli CIDR:
head -5 /etc/nftables/geoip/us.zone
Output previsto (intervalli CIDR, uno per riga):
1.178.0.0/23 1.178.4.0/22 1.178.8.0/21 ...
3.2 Generare e caricare la configurazione nftables
Creare uno script che legge i file CIDR e genera un set nftables. Salvarlo come /etc/nftables/geoip-update.sh:
sudo nano /etc/nftables/geoip-update.sh
Incollare quanto segue:
#!/bin/bash # Generate nftables set from country CIDR files set -euo pipefail GEOIP_DIR="/etc/nftables/geoip" OUTPUT="/etc/nftables/geoip-sets.nft" echo "define ALLOWED_COUNTRIES = {" > "$OUTPUT" for zone_file in "$GEOIP_DIR"/*.zone; do while IFS= read -r cidr; do && continue echo " ${cidr}," >> "$OUTPUT" done < "$zone_file" done echo "}" >> "$OUTPUT" echo "GeoIP set generated: $(wc -l < "$OUTPUT") lines"
Renderlo eseguibile:
sudo chmod +x /etc/nftables/geoip-update.sh
Eseguire lo script:
sudo /etc/nftables/geoip-update.sh
Ora fare riferimento a questo set nella configurazione di nftables:
# Debian/Ubuntu: sudo nano /etc/nftables.conf # AlmaLinux/RHEL: sudo nano /etc/sysconfig/nftables.conf
Aggiungere l'include e usare il set nella catena input:
#!/usr/sbin/nft -f flush ruleset include "/etc/nftables/geoip-sets.nft" table inet filter { chain input { type filter hook input priority 0; policy drop; ct state invalid drop iif lo accept ct state established,related accept # Allow ICMP ip protocol icmp accept ip6 nexthdr icmpv6 accept # GeoIP: allow only listed countries ip saddr $ALLOWED_COUNTRIES accept # Log dropped packets log prefix "geoip-drop: " level info # Everything else is dropped by policy } chain forward { type filter hook forward priority 0; policy drop; } chain output { type filter hook output priority 0; policy accept; } }
Convalidare la sintassi prima di applicare la configurazione:
sudo nft -c -f /etc/nftables.conf
Se non compaiono errori, applicare la configurazione e verificarla:
sudo nft -f /etc/nftables.conf sudo systemctl enable --now nftables sudo nft list ruleset | head -20
Con IP set molto grandi (oltre 100.000 voci), nftables si comporta decisamente meglio di iptables. Il match basato sui set di nftables usa internamente tabelle hash, quindi le prestazioni di ricerca restano costanti indipendentemente dalla dimensione del set.
Passaggio 4: Automatizzare gli aggiornamenti del database GeoIP
Le associazioni tra IP e paese cambiano di continuo. Un database del mese scorso presenterà lacune. Pianificare aggiornamenti automatici con cron.
4.1 Creare lo script di aggiornamento
Creare /usr/local/sbin/geoip-refresh.sh:
sudo nano /usr/local/sbin/geoip-refresh.sh
Per iptables (xtables-addons):
#!/bin/bash set -euo pipefail cd /tmp /usr/libexec/xtables-addons/xt_geoip_dl /usr/libexec/xtables-addons/xt_geoip_build \ -D /usr/share/xt_geoip *.csv rm -f /tmp/*.csv /tmp/*.zip logger "GeoIP database updated successfully"
Per nftables:
#!/bin/bash set -euo pipefail GEOIP_DIR="/etc/nftables/geoip" for CC in us de pl; do curl -sS -o "${GEOIP_DIR}/${CC}.zone" \ "https://www.ipdeny.com/ipblocks/data/aggregated/${CC}-aggregated.zone" done /etc/nftables/geoip-update.sh systemctl reload nftables logger "GeoIP nftables sets updated and reloaded"
Renderlo eseguibile:
sudo chmod +x /usr/local/sbin/geoip-refresh.sh
4.2 Pianificare con cron
Prima di pianificare l'attività cron, assicurarsi che il servizio nftables sia abilitato e in esecuzione (se si usa il metodo nftables):
sudo systemctl enable --now nftables
Eseguire l'aggiornamento ogni settimana:
sudo crontab -e
Aggiungere:
0 3 * * 0 /usr/local/sbin/geoip-refresh.sh >> /var/log/geoip-update.log 2>&1
Viene eseguito ogni domenica alle 3:00. Dopo la prima esecuzione pianificata, verificare che abbia funzionato:
cat /var/log/geoip-update.log
Passaggio 5: Verifica e test
5.1 Test da un paese consentito
Da una macchina situata in uno dei paesi consentiti, connettersi via SSH oppure effettuare una richiesta HTTP:
curl -v http://<YOUR_SERVER_IP>
La connessione dovrebbe riuscire normalmente.
5.2 Simulare un paese bloccato
Usare curl con un IP noto di un paese bloccato tramite proxy, oppure testare in locale rimuovendo temporaneamente il proprio paese dalla allowlist e provando a connettersi da un secondo terminale.
Servizi online come https://ipinfo.io permettono di verificare in quale paese è registrato un determinato IP. Usare curl https://ipinfo.io/<YOUR_IP_ADDRESS> per confermarlo.
5.3 Controllare i contatori del firewall
iptables:
sudo iptables -L INPUT -v -n
Osservare i contatori dei pacchetti sulla regola DROP: dovrebbero aumentare man mano che arriva traffico bloccato.
nftables:
sudo nft list chain inet filter input
5.4 Monitorare i log di sistema
Controllare nel log di sistema la presenza di pacchetti scartati:
sudo journalctl -k --since "1 hour ago" | grep -i "geoip-drop"
Se si osserva un volume elevato di messaggi del log del kernel sui pacchetti scartati e il sistema smette di rispondere, la causa può essere un flood di log. Nel caso peggiore, ciò può causare un kernel panic. Se sono stati configurati il parametro crashkernel e il servizio kdump, un kernel panic produrrà un crash dump (vmcore) in /var/crash che sarà possibile analizzare in seguito. Senza kdump si ottiene soltanto un riavvio e nessuna diagnostica. Una breve panoramica è riportata nella sezione seguente.
Passaggio 6: Proteggersi dal kernel panic durante le modifiche al firewall
Modifiche aggressive al firewall su un server di produzione molto carico, in particolare quelle che causano improvvisi picchi di traffico o flood di log, possono in rari casi portare a un kernel panic. Se succede e non ci si è preparati, non si ottiene nulla: solo un riavvio e nessun dato su che cosa sia andato storto.
Kdump è il meccanismo standard di Linux per acquisire un crash dump quando si verifica un kernel panic. Usa kexec per avviare un secondo kernel di cattura (capture kernel), che scrive su disco l'immagine della memoria (vmcore) prima del riavvio del sistema.
Per avere la diagnostica disponibile:
- Verificare che kdump sia installato e abilitato. Su RHEL/AlmaLinux di solito è preinstallato. Su Debian/Ubuntu:
sudo apt install kdump-tools kexec-tools -y
- Verificare che il parametro
crashkernelsia impostato nella configurazione del bootloader:
Dovrebbe comparire qualcosa comecat /proc/cmdline | grep crashkernel
crashkernel=256M. Se manca, è necessario configurare il parametro crashkernel in GRUB e riavviare. La quantità esatta di memoria da riservare per kdump su Linux dipende dalla RAM totale:256Mè un valore predefinito sicuro per server con 4 GB o più. - Verificare che il servizio kdump sia attivo:
sudo systemctl status kdump
Se si verifica davvero un kernel panic, il sistema userà kexec per avviare il kernel di cattura, scriverà il vmcore in /var/crash e poi si riavvierà normalmente. A quel punto sarà possibile eseguire l'analisi del vmcore su Linux con l'utilità crash per individuare la causa. È una parte standard della risoluzione dei problemi di kernel panic su Linux sui sistemi di produzione. Per una guida completa su come abilitare kdump su Linux e sulla configurazione di kexec e kdump su Linux, consultare un tutorial dedicato a kdump.
Risoluzione dei problemi
- Accesso SSH bloccato: usare la console out-of-band o la modalità di rescue.
modinfo xt_geoip: not found: installarelinux-headers-$(uname -r).iptables: command not found(Debian 13): installare iptables consudo apt install iptables.nftables.serviceis not active: eseguiresudo systemctl enable --now nftables./etc/sysconfig/nftables.confvuoto su AlmaLinux: usare questo percorso al posto di/etc/nftables.confper configurare le regole.
Rollback
Per annullare il blocco per paese e ripristinare l'accesso aperto:
iptables: ripristino dal backup:
sudo iptables-restore < /tmp/iptables-backup-*.rules
Se le regole GeoIP sono state applicate su un server senza una precedente configurazione del firewall, il file di backup sarà vuoto. In tal caso, usare i comandi di svuotamento e impostazione della policy riportati sotto al posto di iptables-restore.
Oppure svuotare tutte le regole:
sudo iptables -F INPUT sudo iptables -P INPUT ACCEPT
Lo svuotamento con policy ACCEPT elimina ogni protezione del firewall. Applicare subito dopo il proprio set standard di regole di sicurezza.
nftables: ripristino della configurazione predefinita:
sudo nft flush ruleset
Quindi ripristinare la configurazione di base di nftables (senza GeoIP):
sudo nft -f /etc/nftables.conf.backup
Per rimuovere completamente i componenti GeoIP:
sudo rm -rf /etc/nftables/geoip sudo rm -f /etc/nftables/geoip-sets.nft /etc/nftables/geoip-update.sh sudo crontab -l | grep -v geoip-refresh | sudo crontab -
Rimuovere xtables-addons (se installato):
Debian/Ubuntu:
sudo apt remove xtables-addons-common -y
AlmaLinux/RHEL:
sudo dnf remove xtables-addons -y
Conclusione
Il server ora usa le regole firewall GeoIP per bloccare il traffico per paese su Linux, tramite iptables con xt_geoip oppure tramite nftables con IP set basati su CIDR. Il database si aggiorna automaticamente, si dispone di un piano di rollback e, con kdump configurato per catturare i crash dump in caso di kernel panic, l'ambiente di produzione è protetto sia dalle minacce ordinarie sia dagli scenari peggiori.
Versione del documento: 1.0
Ultimo aggiornamento: maggio 2026
Responsabile: Team di documentazione tecnica