
Script per implementare Q-Feeds direttamente su NFtables o IPtables
Blocklist automatica di IP malware per server Linux — supporta nftables e iptables+ipset
Ottieni una chiave API gratuita su tip.qfeeds.com.
git clone https://github.com/Q-Feeds/NFtables-IPtables-integration-script.git cd NFtables-IPtables-integration-script chmod +x qfeeds-installer.sh qfeeds-uninstaller.sh
### Passaggio 3: Esegui l'installer come root```bash
sudo ./qfeeds-installer.sh
L'installer:
Il tuo server è ora protetto. Il job cron controlla gli aggiornamenti ogni 20 minuti (configurabile), e le chiamate API effettive avvengono solo quando la tua licenza lo consente.
Questa soluzione scarica periodicamente il feed di threat intelligence più recente da Q-Feeds e lo applica come regole firewall, permettendoti di:
L'installer rileva automaticamente quale backend firewall è disponibile:
| Priorità | Rilevamento | Backend |
|---|---|---|
| 1° | Comando nft trovato | nftables |
| 2° | Comando iptables trovato | iptables+ipset |
| — | Nessuno trovato | Errore (uscita) |
Il backend rilevato viene memorizzato nel file di configurazione. Gli script di aggiornamento e disinstallazione lo utilizzano per eseguire i comandi firewall corretti.
Entrambi i backend utilizzano la stessa strategia di set divisi per le massime prestazioni:
Backend nftables:``` ┌─────────────────────────────────────────────────────────┐ │ table ip qfeeds │ │ │ │ ┌─────────────────────────┐ ┌───────────────────────┐ │ │ │ qfeeds_blacklist_v4 │ │ qfeeds_blacklist_v4 │ │ │ │ (hash set) │ │ _nets (interval set) │ │ │ │ │ │ │ │ │ │ Individual IPs │ │ CIDR ranges │ │ │ │ ~99% of entries │ │ ~1% of entries │ │ │ │ O(1) lookup & insert │ │ O(log n) lookup │ │ │ └─────────────────────────┘ └───────────────────────┘ │ │ │ │ ┌─────────────────────────┐ │ │ │ qfeeds_whitelist_v4 │ │ │ │ (interval set) │ │ │ │ Your allowed IPs/CIDRs │ │ │ └─────────────────────────┘ │ │ │ │ chain input-chain (hook input, priority 0, accept) │ │ → ip saddr @qfeeds_whitelist_v4 accept │ │ → ip saddr @qfeeds_blacklist_v4 drop │ │ → ip saddr @qfeeds_blacklist_v4_nets drop │ │ │ │ chain output-chain (if enabled) │ │ → ip daddr @qfeeds_whitelist_v4 accept │ │ → ip daddr @qfeeds_blacklist_v4 drop │ │ → ip daddr @qfeeds_blacklist_v4_nets drop │ └─────────────────────────────────────────────────────────┘
**iptables+ipset backend:**```
┌──────────────────────────────────────────────────────────┐
│ ipset sets │
│ │
│ ┌─────────────────────────┐ ┌────────────────────────┐ │
│ │ qfeeds_blacklist_v4 │ │ qfeeds_blacklist_v4 │ │
│ │ (hash:ip) │ │ _nets (hash:net) │ │
│ │ maxelem 1000000 │ │ maxelem 65536 │ │
│ │ │ │ │ │
│ │ Individual IPs │ │ CIDR ranges │ │
│ └─────────────────────────┘ └────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ qfeeds_whitelist_v4 │ │
│ │ (hash:net) │ │
│ └─────────────────────────┘ │
│ │
│ iptables: INPUT/OUTPUT jump to a dedicated chain │
│ (jump rule tagged -m comment "qfeeds"): │
│ │
│ chain QFEEDS_INPUT (rebuilt each run, in order): │
│ -m set --match-set whitelist_v4 src -j ACCEPT │
│ -m set --match-set blacklist_v4 src -j DROP │
│ -m set --match-set blacklist_v4_nets src -j DROP │
│ (QFEEDS_OUTPUT mirrors this with dst, if enabled) │
└──────────────────────────────────────────────────────────┘
La stessa struttura esiste per IPv6 (tabella ip6 qfeeds o ip6tables + ipsets family inet6).
Perché due tipi di set?
┌──────────────────────────────────────────────────────┐ │ 1. Check license schedule (licenses.php API) │ │ → Skip run if not yet time for next update │ │ 2. Determine sync mode (full or diff) │ │ 3. Fetch IPv4 feed (ipv6=0) and IPv6 feed │ │ (ipv6=only) separately │ │ 4. Separate IPs from CIDRs in awk │ │ 5. Batch-load into hash set (IPs) and net/interval │ │ set (CIDRs) │ │ 6. Update whitelist sets from config │ │ 7. Persist rules │ └──────────────────────────────────────────────────────┘
### Sincronizzazione Completa vs Differenziale
| Modalità | Quando | Cosa fa |
|------|------|-------------|
| **Sincronizzazione completa** | Prima esecuzione, aggiornamento forzato, dopo un fallimento della sincronizzazione differenziale, quando il set locale ha perso la baseline (vuoto o molto più piccolo del previsto), o quando l'ultima sincronizzazione è più vecchia di `FULL_SYNC_MAX_AGE` (default 24h) | Recupera e convalida prima ogni feed, poi svuota e ricarica i set di blacklist. Il set viene svuotato solo dopo aver ottenuto dati validi, quindi un recupero fallito non ti lascia mai senza protezione |
| **Sincronizzazione differenziale** | Esecuzioni successive (solo feed `malware_ip`) con un set locale integro | Recupera solo le aggiunte (`+`) e le rimozioni (`-`) dall'ultimo pull |
La sincronizzazione differenziale è **per chiave API** — l'API tiene traccia del tuo ultimo pull riuscito e restituisce solo le modifiche successive. Se una sincronizzazione differenziale fallisce, lo script torna automaticamente a una sincronizzazione completa.
> **Auto-riparazione:** Gli aggiornamenti differenziali aggiornano solo il set esistente. Se quel set viene perso o troncato, ad esempio a causa di un riavvio in cui le regole del firewall non sono state mantenute, un flush manuale o una sincronizzazione parziale precedente, l'aggiornatore rileva la baseline mancante (il conteggio degli elementi live è 0 o molto inferiore all'ultimo conteggio registrato) e forza una ricostruzione completa invece di applicare le differenze su un set vuoto. Come ulteriore rete di sicurezza, forza anche una sincronizzazione completa periodica (ogni 24 ore per impostazione predefinita, tramite `FULL_SYNC_MAX_AGE`).
### Pianificazione Basata sulla Licenza
L'aggiornatore controlla l'API delle licenze Q-Feeds (`licenses.php`) prima di ogni esecuzione. Se il timestamp `next_update` della tua licenza non è stato ancora raggiunto, lo script termina anticipatamente senza effettuare chiamate API non necessarie. Il cron job viene eseguito frequentemente (default: ogni 20 minuti), ma gli aggiornamenti effettivi avvengono solo quando la tua licenza lo consente.
---
## ✅ Prerequisiti
Prima dell'installazione, assicurati di avere:
- [x] **Server Linux** con **nftables** o **iptables** (Debian, Ubuntu, CentOS, Fedora, Arch, Alpine)
- [x] **Accesso root** — il programma di installazione e l'aggiornatore devono essere eseguiti come root
- [x] **Token API Q-Feeds** — ottieni il tuo gratuito su [tip.qfeeds.com](https://tip.qfeeds.com/)
- [x] **Accesso a Internet** — il server deve poter raggiungere `api.qfeeds.com`
Il programma di installazione installerà automaticamente le dipendenze necessarie:
- **Backend nftables**: `nftables`, `curl`, `jq`, `util-linux`
- **Backend iptables**: `iptables`, `ipset`, `curl`, `jq`, `util-linux`
---
## 📝 Guida di Installazione Dettagliata
### 1. Ottieni il Tuo Token API
Visita [tip.qfeeds.com](https://tip.qfeeds.com/) per ottenere il tuo token API Q-Feeds gratuito.
### 2. Scarica ed Esegui```bash
git clone https://github.com/Q-Feeds/NFtables-IPtables-integration-script.git
cd NFtables-IPtables-integration-script
chmod +x qfeeds-installer.sh qfeeds-uninstaller.sh
sudo ./qfeeds-installer.sh
L'installatore chiederà le seguenti domande:
Enter your Q-Feeds API Token:
Il tuo token da [tip.qfeeds.com](https://tip.qfeeds.com/). Il programma di installazione rifiuta di proseguire se vuoto.
#### Tipo di Feed```
Enter feed type [default: malware_ip]:
Predefinito è malware_ip. Cambia solo se Q-Feeds ti ha fornito un tipo di feed diverso.
Enter the limit of IPs to fetch (leave empty for no limit):
Premi Invio per nessun limite (consigliato). Inserisci un numero per limitare la dimensione del feed.
#### Blocco direzionale```
Block INCOMING connections from malicious IPs? [Y/n]:
Block OUTGOING connections to malicious IPs? [y/N]:
Configure a whitelist of IPs/CIDRs that must NEVER be blocked? [y/N]: Enter IPv4 whitelist (comma-separated, e.g. 1.2.3.4,5.6.7.8): Enter IPv6 whitelist (comma-separated, e.g. 2001:db8::1):
Add your management IP(s) here to ensure you are never locked out, even if they appear in the feed. Whitelist rules are always checked **before** blacklist rules.
#### Programma Cron```
Enter cron schedule (e.g., '*/20 * * * *') [default: */20 * * * *]:
How often the updater checks for new data. The default is every 20 minutes. The license-based scheduling ensures the API is only called when your license allows an update.
Re-eseguire l'installer con un crontab personalizzato: Se esiste già una voce cron di Q-Feeds, l'installer chiede prima di modificarla:
An existing Q-Feeds cron entry was found in the current crontab. Replace it with a fresh default entry? Choosing 'no' keeps your crontab unchanged [y/N]:Rispondi
no(il predefinito) per mantenere il tuo crontab esistente. Una prima installazione pulita non ha voci esistenti e salta questa richiesta. Per installazioni automatizzate, impostaQFEEDS_SKIP_CRON=1per lasciare il crontab intatto senza richieste.
Tutte le impostazioni sono memorizzate in /etc/qfeeds/qfeeds_config.conf. Puoi modificare questo file direttamente senza rieseguire l'installer. Le modifiche hanno effetto alla successiva esecuzione del cron.
| Variabile | Descrizione | Predefinito |
|---|---|---|
BACKEND | Backend firewall (nftables o iptables) | (rilevato automaticamente) |
API_TOKEN | Il tuo token API di Q-Feeds | (richiesto) |
FEED_TYPE | Tipo di feed da recuperare | malware_ip |
LIMIT | Numero massimo di IP da recuperare (vuoto = nessun limite) | (vuoto) |
BLOCK_INCOMING | Blocca il traffico in entrata dagli IP nella blacklist | yes |
BLOCK_OUTGOING | Blocca il traffico in uscita verso gli IP nella blacklist | no |
WHITELIST_V4 | Whitelist IPv4 separata da virgole | (vuoto) |
WHITELIST_V6 | Whitelist IPv6 separata da virgole | (vuoto) |
LOG_FILE | Percorso del file di log | /var/log/qfeeds_blocklist.log |
FULL_SYNC_MAX_AGE | Età massima in secondi prima che venga forzata una risincronizzazione completa (difesa in profondità) | 86400 (24h) |
| Percorso | Scopo |
|---|---|
/etc/qfeeds/qfeeds_config.conf | File di configurazione |
/etc/qfeeds/.last_sync | File di stato per il tracciamento della sincronizzazione completa/differenziale |
/etc/qfeeds/.last_count | Ultimo conteggio di elementi riuscito, utilizzato per rilevare una baseline persa |
/usr/local/bin/update_qfeeds_blocklist.sh | Script di aggiornamento (eseguito tramite cron) |
/var/log/qfeeds_blocklist.log | File di log |
nft list table ip qfeeds
nft list set ip qfeeds qfeeds_blacklist_v4 | grep -oP '\d+.\d+.\d+.\d+' | wc -l
nft list set ip qfeeds qfeeds_blacklist_v4_nets | head -20
nft list set ip6 qfeeds qfeeds_blacklist_v6 | wc -l
### iptables+ipset backend```bash
# List all Q-Feeds ipsets and their sizes
ipset list -t | grep -A4 qfeeds
# Count loaded IPv4 IPs
ipset list qfeeds_blacklist_v4 | tail -n +9 | wc -l
# Show loaded CIDR ranges
ipset list qfeeds_blacklist_v4_nets | tail -n +9 | head -20
# Show the qfeeds jump rule in INPUT, then the dedicated chain's block rules
iptables -L INPUT -n --line-numbers | grep qfeeds
iptables -L QFEEDS_INPUT -n
ip6tables -L QFEEDS_INPUT -n
tail -20 /var/log/qfeeds_blocklist.log
grep -i "error" /var/log/qfeeds_blocklist.log
sudo /usr/local/bin/update_qfeeds_blocklist.sh
sudo QFEEDS_FORCE_UPDATE=1 /usr/local/bin/update_qfeeds_blocklist.sh
sudo crontab -l | grep qfeeds
---
## 🔍 Risoluzione dei problemi
### Generale
**L'installazione fallisce con "Unable to locate package"**
- Il programma di installazione rileva automaticamente la tua distribuzione (Debian/Ubuntu, CentOS/RHEL, Fedora, Arch, Alpine). Se il rilevamento fallisce, installa le dipendenze manualmente: `curl`, `jq`, `util-linux` (per `flock`), più `nftables` o `iptables`+`ipset`.
**I set sono vuoti dopo l'installazione**
- Controlla il log: `tail -50 /var/log/qfeeds_blocklist.log`
- Verifica che il tuo token API sia corretto
- Prova un aggiornamento forzato: `sudo QFEEDS_FORCE_UPDATE=1 /usr/local/bin/update_qfeeds_blocklist.sh`
**"Non è ancora il momento. Prossimo aggiornamento programmato per..."**
- L'updater rispetta la pianificazione della tua licenza. Questo messaggio significa che il cron è stato eseguito, ma la tua licenza non consente ancora un aggiornamento. È normale — la prossima esecuzione del cron controllerà di nuovo.
- Il programma di installazione Linux mantiene un indice locale `licenses.php` in cache e utilizza il `next_update` memorizzato come gate della pianificazione. Dopo un prelievo riuscito, aggiorna quell'indice locale per il ciclo successivo.
**Le regole non persistono dopo il riavvio**
- Se `netfilter-persistent` è installato, le regole vengono salvate automaticamente
- **nftables**: se `netfilter-persistent` è assente, l'updater ora scrive automaticamente l'intero ruleset in `/etc/nftables.conf` e abilita il servizio `nftables`; puoi comunque salvare manualmente con `nft list ruleset > /etc/nftables.conf`
- **iptables**: l'updater salva con `ipset save > /etc/ipset.conf` e `iptables-save`; puoi anche salvare manualmente
- Anche se la persistenza fallisce completamente, l'updater si auto-ripara: alla prossima esecuzione rileva il set vuoto dopo il riavvio e lo ricostruisce con una sincronizzazione completa
### Specifiche per nftables
**"Batch nft -f fallito. Ripiego all'esecuzione per comando..."**
- Questo è normale, specialmente su contenitori LXC dove il buffer netlink del kernel (`wmem_max`) è limitato. Il fallback per comando funziona correttamente ed è veloce (~10 secondi per oltre 400k IP).
**Errore di sintassi: "stringa inaspettata"**
- Assicurati di eseguire una versione recente di nftables. Lo script utilizza la sintassi `ip saddr`/`ip daddr` che richiede nftables 0.9+.
**"Errore: Impossibile elaborare la regola: Messaggio troppo lungo"**
- Questo è il limite del buffer netlink, tipico nei contenitori LXC. Lo script passa automaticamente all'esecuzione per comando. Se vedi questo nel log insieme a un caricamento riuscito, funziona come previsto.
### Specifiche per iptables+ipset
**"ipset restore fallito"**
- Verifica che `ipset` sia installato: `command -v ipset`
- Controlla il log per errori specifici: `grep -i "error" /var/log/qfeeds_blocklist.log`
- Assicurati che il modulo ipset sia caricato: `lsmod | grep ip_set`
**Le regole iptables non vengono visualizzate**
- Le regole di blocco risiedono nelle catene dedicate `QFEEDS_INPUT` / `QFEEDS_OUTPUT`; `INPUT`/`OUTPUT` contengono solo un salto `-j QFEEDS_INPUT` taggato con il commento `qfeeds`
- Verifica con: `iptables -L INPUT -n | grep qfeeds` (il salto) e `iptables -L QFEEDS_INPUT -n` (le regole di blocco)
- La regola di salto utilizza `-m comment --comment "qfeeds"` per l'identificazione
- Assicurati che il modulo `xt_set` sia caricato: `modprobe xt_set`
**"ipset create ... fallito"**
- Su kernel molto vecchi, i tipi `hash:ip` o `hash:net` potrebbero non essere disponibili. Aggiorna il kernel o installa `ipset` da un repository più recente.
---
## 🗑️ Disinstallazione```bash
sudo ./qfeeds-uninstaller.sh
Il programma di disinstallazione rimuove tutto in base al backend rilevato:
Backend nftables:
ip qfeeds e ip6 qfeeds (incluse tutte le catene, regole e set)Backend iptables:
qfeeds (incluse le regole di salto)QFEEDS_INPUT / QFEEDS_OUTPUTqfeeds_blacklist_v4, qfeeds_blacklist_v4_nets, qfeeds_whitelist_v4 e gli equivalenti IPv6)Entrambi i backend:
/etc/qfeeds/)/usr/local/bin/update_qfeeds_blocklist.sh)Se il file di configurazione è assente, il programma di disinstallazione tenta la pulizia per entrambi i backend.
Nota: Il programma di disinstallazione non rimuove i pacchetti di sistema (curl, jq, ipset, ecc.) installati come dipendenze.
Questo progetto è concesso in licenza secondo i termini della Apache License 2.0 - consultare il file LICENSE per i dettagli.
Utilizzo a proprio rischio.
Si prega di testare questi script nel proprio ambiente prima di implementarli in produzione. L'autore non è responsabile per eventuali problemi o danni che potrebbero derivare dal loro utilizzo.
Assistenza AI: Parti di questo progetto (codice, correzioni e documentazione) sono state scritte con l'aiuto di strumenti AI e successivamente revisionate dai maintainer. Sebbene testiamo e revisioniamo le modifiche, ti invitiamo a rivedere tu stesso gli script prima di eseguirli e a segnalare eventuali anomalie.