VPS / Cloud

Synchroniser une blacklist IP sur une flotte de VPS : méthodes modernes

12 août 2026 · 10 min de lecture
Illustration immersive du sujet : blacklist IP multi-VPS

Gérer une blacklist IP multi-VPS devient critique dès qu'une infrastructure dépasse trois serveurs : sans synchronisation centralisée, chaque machine maintient sa propre liste noire, obligeant l'administrateur à se connecter en SSH sur chaque nœud pour bloquer manuellement une IP attaquante. Cette approche manuelle génère des fenêtres de vulnérabilité de plusieurs minutes entre la détection d'une menace et son blocage complet sur la flotte, laissant le temps à un acteur malveillant de pivoter vers les serveurs encore non protégés. Cet article détaille les mécanismes kernel-level permettant de centraliser et propager automatiquement les listes de blocage IP sur des dizaines de VPS en moins de 500 ms, en combinant XDP (eXpress Data Path) et nftables dans une architecture pilotée par BPF maps.



Pourquoi la blacklist IP multi-VPS fragmentée expose votre infrastructure

Dans une architecture multi-serveurs classique (serveurs de jeu, APIs, bases de données réparties géographiquement), chaque VPS applique ses propres règles iptables ou nftables localement. Lorsqu'un scan de port massif provenant de 203.0.113.42 frappe le serveur 1, l'administrateur ajoute cette IP dans la table de rejet locale :

nft add element inet filter blacklist { 203.0.113.42 }

Mais les serveurs 2, 3 et 4 continuent d'accepter le trafic de cette même source jusqu'à ce qu'un humain réplique manuellement la règle. Ce délai crée trois problèmes opérationnels majeurs :

  • Fenêtre d'exposition asymétrique : l'attaquant bascule vers les nœuds non protégés dès le premier blocage, exploitant la désynchronisation pour contourner la défense.
  • Charge administrative exponentielle : avec N serveurs, chaque nouvelle IP hostile nécessite N connexions SSH, N commandes firewall, N vérifications de syntaxe — soit N² opérations pour une flotte active.
  • Incohérence des règles : les listes divergent rapidement (IP ajoutée sur serveur A, oubliée sur serveur B, expirée sur serveur C), rendant l'audit de sécurité impossible sans parsing exhaustif de toutes les tables nft.

Les solutions de scrubbing cloud (OVH VAC, Cloudflare Magic Transit) bloquent le trafic en amont du datacenter, mais ne permettent pas de bloquer une IP hostile détectée localement (par exemple, un joueur tricheur identifié par anti-cheat) sans passer par un portail tiers. La propagation de blacklist doit donc rester maîtrisée au niveau kernel pour garantir une latence sub-seconde et un contrôle total de la politique de blocage.



Architecture de synchronisation : BPF maps partagées vs. API de contrôle centralisée

Deux approches techniques coexistent pour synchroniser une blacklist IP multi-VPS au niveau kernel :

Option 1 : BPF maps distribuées avec réplication asynchrone

Un daemon agent (écrit en Go, Rust ou C) tourne sur chaque VPS, connecté à un bus de messages (NATS, Redis Pub/Sub, gRPC bidirectionnel). Lorsqu'un administrateur ajoute 198.51.100.17 à la blacklist via le panel central, le serveur de contrôle envoie un message à tous les agents abonnés :

{
  "action": "blacklist_add",
  "ip": "198.51.100.17",
  "timestamp": 1736035200,
  "ttl": 3600
}

Chaque agent local met alors à jour la BPF map pakkt_blacklist en espace kernel via bpf_map_update_elem(), sans redémarrage du programme XDP. Le code XDP consulte cette map pour chaque paquet entrant :

__u32 key = bpf_ntohl(ip_hdr->saddr);
__u8 *verdict = bpf_map_lookup_elem(&pakkt_blacklist, &key);
if (verdict && *verdict == ACTION_DROP) {
    return XDP_DROP;
}

Cette architecture garantit une propagation en moins de 500 ms sur une flotte de 50 VPS (latence réseau + sérialisation + mise à jour BPF map), avec un overhead CPU inférieur à 1 % par agent.

Option 2 : nftables avec sets atomiques pilotés par API

Plutôt que de maintenir une map BPF par agent, on centralise la logique dans nftables via des named sets mises à jour par API. L'agent appelle périodiquement le contrôleur (heartbeat toutes les 30 secondes) pour récupérer un delta de changements :

curl -H "Authorization: Bearer ${PAKKT_TOKEN}" \
  https://api.pakkt.io/v1/agents/${AGENT_ID}/blacklist/delta

Le retour JSON liste les IPs ajoutées/supprimées depuis le dernier heartbeat. L'agent applique le delta en une transaction atomique nftables :

nft add element inet pakkt blacklist_v4 { 192.0.2.5, 192.0.2.6 }
nft delete element inet pakkt blacklist_v4 { 203.0.113.99 }

L'avantage de cette approche est la compatibilité immédiate avec les règles stateful (conntrack) et les compteurs nftables natifs, sans développer de logique custom en eBPF. L'inconvénient : la latence de synchronisation dépend de l'intervalle du heartbeat (30 s par défaut), contre une mise à jour push instantanée pour BPF maps distribuées.

Stratégie hybride : XDP pour la blacklist, nftables pour la whitelist et les règles stateful

PAKKT.io implémente cette architecture hybride : la BPF map pakpt_blacklist est mise à jour en temps réel par l'agent, tandis que la table inet pakkt nftables contient la whitelist (set whitelist_v4) et les règles de rate-limit par connexion TCP. Le programme XDP consulte d'abord la blacklist (DROP immédiat si match), puis passe le paquet au stack réseau où nftables applique les règles stateful :

nft add rule inet pakkt input ip saddr @whitelist_v4 accept
nft add rule inet pakkt input tcp dport 25565 ct state new limit rate 50/second accept

Cette double couche garantit un DROP sub-microseconde pour les IPs blacklistées (avant même l'allocation skb), tout en conservant la richesse de nftables pour les cas complexes (whitelist géolocalisée, burst par port, logging sélectif).



Cas d'usage concrets : serveurs de jeu, APIs SaaS et clusters de bases de données

Serveurs de jeu multi-régions (Minecraft, CS2, Rust)

Un hébergeur de serveurs Minecraft exploite 12 VPS répartis sur trois datacenters (Paris, Francfort, Varsovie). Chaque VPS héberge 4 instances Minecraft sur ports 25565–25568. Un scanner de vulnérabilités Log4Shell cible massivement le port 25565 depuis un botnet de 300 IPs. L'administrateur détecte le pattern via GeoIP (90 % du trafic hostile provient de deux ASN chinois) et ajoute un bloc CIDR entier dans la blacklist centralisée :

curl -X POST https://api.pakkt.io/v1/agents/blacklist \
  -H "X-API-Key: ${PAKKT_API_KEY}" \
  -d '{"cidr": "103.224.0.0/14", "reason": "Log4Shell scan", "ttl": 86400}'

En moins de 2 secondes, les 12 agents reçoivent la mise à jour, mettent à jour leur BPF map locale, et DROP tous les paquets SYN provenant de ce range. Le trafic hostile passe de 45 000 pps à zéro sur l'ensemble de la flotte, sans latence ajoutée pour les joueurs légitimes (temps de lookup BPF map : ~20 ns par paquet).

API SaaS avec rate-limit distribué

Un éditeur SaaS expose une API REST sur 8 VPS derrière round-robin DNS (pas de load balancer matériel). Un client abuse du endpoint POST /api/v1/reports en envoyant 500 requêtes/seconde depuis 198.51.100.42, saturant la base PostgreSQL backend. L'équipe détecte l'abus via les logs applicatifs et ajoute l'IP dans la blacklist PAKKT. Résultat :

  • Les 8 VPS DROP les paquets TCP SYN de cette IP au niveau XDP, avant même la handshake TCP.
  • La base de données voit sa charge CPU redescendre de 85 % à 12 % en moins de 5 secondes.
  • Pas besoin de redémarrer Nginx, de modifier les règles applicatives, ou de patcher le code métier.

L'IP est automatiquement retirée de la blacklist après 3600 secondes (TTL défini dans la requête API initiale), évitant un blocage permanent d'un client ayant corrigé son script bugué.

Cluster de bases de données avec accès restreint

Une infrastructure de 6 nœuds PostgreSQL en réplication streaming autorise uniquement les connexions depuis un set de 4 IPs (serveurs applicatifs frontaux). La whitelist est définie dans nftables sur chaque nœud :

nft add set inet pakkt whitelist_db { type ipv4_addr \; }
nft add element inet pakkt whitelist_db { 10.0.1.10, 10.0.1.11, 10.0.1.12, 10.0.1.13 }
nft add rule inet pakkt input tcp dport 5432 ip saddr != @whitelist_db drop

Lorsqu'un nouveau serveur applicatif (10.0.1.14) est provisionné, l'équipe ajoute son IP via le panel PAKKT. Les 6 agents PostgreSQL reçoivent la mise à jour et exécutent :

nft add element inet pakkt whitelist_db { 10.0.1.14 }

Le nouveau serveur peut immédiatement se connecter au cluster, sans connexion SSH manuelle sur chaque nœud PostgreSQL.



Métriques et audit : tracer les ajouts/suppressions sur toute la flotte

Une blacklist IP multi-VPS synchronisée perd toute valeur opérationnelle si l'équipe ne peut pas auditer qui a ajouté quelle IP, quand, et sur combien de serveurs la règle a réellement été appliquée. PAKKT enregistre chaque mutation de blacklist/whitelist dans une base TimescaleDB avec les colonnes suivantes :

Colonne Type Description
timestamp timestamptz Horodatage UTC de la mutation (précision microseconde)
user_id uuid Utilisateur ayant initié l'action (API key ou session panel)
action enum blacklist_add | blacklist_remove | whitelist_add | whitelist_remove
ip_address inet IP ou CIDR concerné (PostgreSQL type inet natif)
agent_id uuid Agent ayant appliqué la règle (null si commande globale)
apply_latency_ms int Temps entre émission centrale et ACK agent (heartbeat + exec nft/BPF)
reason text Commentaire libre (ex: "DDoS SYN flood port 80")

Cette traçabilité permet de répondre à trois questions critiques en production :

  • Qui a bloqué cette IP ? Requête SQL : SELECT user_id, reason FROM audit_log WHERE ip_address = '203.0.113.42' AND action = 'blacklist_add';
  • Combien de serveurs ont appliqué la règle en moins de 1 seconde ? Agrégation : SELECT COUNT(*) FROM audit_log WHERE timestamp > NOW() - INTERVAL '1 second' AND apply_latency_ms < 1000;
  • Quelles IPs sont actuellement blacklistées sur au moins un agent mais pas sur tous ? Détection de désynchronisation via GROUP BY ip_address HAVING COUNT(DISTINCT agent_id) < (SELECT COUNT(*) FROM agents WHERE status = 'online');

Pour les infrastructures soumises à audit de conformité (PCI-DSS, ISO 27001), exporter ces logs vers un SIEM externe (Splunk, Elastic SIEM) se fait via webhook configurable dans le panel PAKKT, sans parsing de logs texte non structurés.



Gestion des conflits : priorisation entre blacklist locale et blacklist globale

Un cas limite apparaît lorsqu'un administrateur ajoute manuellement une règle nftables locale avant que PAKKT ne synchronise la blacklist globale. Par exemple, le serveur VPS-3 contient déjà :

nft add rule inet filter input ip saddr 192.0.2.50 drop

Puis PAKKT propage une blacklist globale incluant 192.0.2.50. Deux stratégies de résolution existent :

Stratégie 1 : table nftables isolée (recommandé)

PAKKT crée une table dédiée inet pakkt avec priorité 0, tandis que la table inet filter (utilisée par fail2ban, scripts manuels, iptables-persistent) reste à priorité -200. Les règles PAKKT s'appliquent donc avant les règles manuelles, évitant tout conflit :

nft add table inet pakkt { comment "PAKKT managed - do not edit manually" \; }
nft add chain inet pakkt input { type filter hook input priority 0 \; policy accept \; }

Toute IP ajoutée dans inet pakkt sera DROP avant que la règle manuelle dans inet filter ne soit même évaluée. Cette isolation garantit que PAKKT et les outils legacy (fail2ban, Docker, Pterodactyl) cohabitent sans collision.

Stratégie 2 : merge des sets nftables

Si l'infrastructure utilise déjà des sets nftables nommés (ex: @blacklist_manual), PAKKT peut injecter ses IPs dans le même set via nft add element, évitant de dupliquer les règles. Inconvénient : PAKKT ne peut plus garantir qu'une IP retirée via le panel sera effectivement supprimée si un script tiers l'a ré-ajoutée manuellement entre-temps.

La stratégie 1 (table isolée) reste donc recommandée pour toute flotte dépassant 5 VPS, car elle offre une séparation nette entre politique centralisée (PAKKT) et règles d'urgence locales (SSH admin).



Conclusion

Centraliser une blacklist IP multi-VPS via BPF maps synchronisées et nftables pilotées par API transforme une tâche administrative chronophage (N² opérations manuelles) en une propagation sub-seconde sur toute la flotte. L'architecture hybride XDP + nftables offre à la fois la performance d'un DROP kernel stateless (moins de 50 nanosecondes par paquet) et la richesse des règles stateful (conntrack, rate-limit par connexion), tout en maintenant une isolation stricte avec les outils firewall existants. L'audit centralisé des mutations de blacklist garantit enfin la traçabilité indispensable aux environnements de production réglementés, sans sacrifier la latence de réponse face à une menace détectée en temps réel.



FAQ

Quelle latence de propagation peut-on espérer entre l'ajout d'une IP dans le panel central et son blocage effectif sur 50 VPS ?

Avec une architecture push (gRPC bidirectionnel ou WebSocket), la latence totale se décompose ainsi : émission du message central (5–10 ms), transit réseau vers les 50 agents (50–200 ms selon géolocalisation), désérialisation JSON + appel bpf_map_update_elem() (2–5 ms par agent). Total : entre 60 ms (agents dans le même datacenter) et 500 ms (agents intercontinentaux). Le heartbeat polling (mode pull toutes les 30 secondes) introduit une latence médiane de 15 secondes, acceptable pour une blacklist non urgente mais insuffisante pour répondre à un DDoS en cours.

Comment gérer une IP légitime bloquée par erreur dans une blacklist globale sans accès SSH direct aux serveurs ?

Le panel PAKKT permet de supprimer une IP de la blacklist en un clic, déclenchant une propagation inverse (bpf_map_delete_elem() ou nft delete element) sur tous les agents en moins de 500 ms. Pour éviter ce scénario, configurez une whitelist permanente contenant les IPs critiques (passerelles de monitoring, IPs des administrateurs, serveurs applicatifs internes) : cette whitelist est consultée avant la blacklist dans le programme XDP, garantissant qu'une IP whitelistée ne sera jamais DROP même si elle apparaît ensuite dans la blacklist globale.

Peut-on synchroniser une blacklist IPv6 en plus d'IPv4 avec la même architecture BPF map ?

Oui, mais cela nécessite deux BPF maps distinctes (pakkt_blacklist_v4 et pakkt_blacklist_v6) car les clés ont des tailles différentes (32 bits vs. 128 bits). Le programme XDP détecte le protocole via eth_hdr->h_proto (0x0800 pour IPv4, 0x86DD pour IPv6) et consulte la map correspondante. Côté nftables, un set de type ipv6_addr séparé est requis : nft add set inet pakkt blacklist_v6 { type ipv6_addr \; }. L'agent PAKKT gère nativement cette double pile, synchronisant les deux maps en parallèle depuis le même flux d'événements centralisé.

Protégez vos serveurs

Déployez PAKKT en 30 secondes

Protection kernel double couche XDP + nftables, pilotable depuis un panel centralisé. Essai gratuit 7 jours.