Réseaux et cybersécurité 17 min de lecture

Configurer un filtrage GeoIP fiable sur MikroTik RouterOS

Générer une liste CIDR par zone géographique, la valider avant publication, l’importer sans effacer la dernière version fonctionnelle et l’appliquer aux bonnes chaînes du pare-feu.

RouterOS ne contient pas de base GeoIP intégrée permettant de sélectionner un pays dans une règle de pare-feu. Le mécanisme repose sur des listes de préfixes CIDR externes : le routeur les importe dans une address-list, puis une règle input ou forward utilise cette liste comme critère.

La partie délicate n’est pas la règle finale. Elle consiste à obtenir des données vérifiables, à ne jamais remplacer une liste fonctionnelle par un fichier vide et à placer le filtre dans la chaîne qui correspond réellement au service. Ce guide construit une liste IPv4 pour la Belgique, tout en permettant d’ajouter facilement la France, les Pays-Bas ou le Luxembourg.

Comment fonctionne le filtrage GeoIP

Une base géographique associe des blocs d’adresses à un code de territoire. Pour un pare-feu, ces blocs deviennent simplement des réseaux comme 203.0.113.0/24. RouterOS ne réalise aucune recherche géographique pendant le traitement d’un paquet : il vérifie seulement si l’adresse source appartient à l’une des entrées de la liste.

ComposantRôleRisque à contrôler
Données RIRFournir les préfixes enregistrés par code ISOLe pays d’enregistrement peut différer de l’emplacement réel
GénérateurValider et convertir les CIDR en commandes RouterOSUn téléchargement vide ne doit jamais écraser la liste active
Address-listConserver les réseaux dans RouterOSDes milliers d’entrées consomment mémoire et temps d’import
Pare-feuAutoriser ou refuser selon la listeUne mauvaise chaîne ou un mauvais ordre rend le filtre inefficace
SchedulerActualiser les donnéesLes permissions et les erreurs doivent être visibles

Les listes utilisées ci-dessous proviennent des données d’allocation des registres Internet régionaux, regroupées par code ISO et publiées quotidiennement. Elles sont plus petites qu’une base de géolocalisation très détaillée, ce qui convient mieux à un routeur. Elles décrivent toutefois l’enregistrement des ressources Internet, pas une position GPS.

Architecture recommandée

Le routeur ne devrait pas dépendre directement d’une page Web transformée à la volée. Un serveur Linux ou un NAS télécharge les données, valide chaque CIDR, construit un fichier .rsc puis le publie en HTTPS. MikroTik télécharge ce fichier, effectue un import à blanc et l’applique seulement si sa syntaxe est valide.

Pipeline sécurisé de mise à jour GeoIP avec téléchargement HTTPS, validation CIDR, liste temporaire et bascule contrôlée
Le remplacement intervient après le téléchargement, la validation des CIDR, le chargement dans une liste temporaire et un contrôle de volume.

Préparer le serveur de listes

L’exemple suppose un serveur disposant de bash, curl et Python 3. Python n’utilise ici que le module standard ipaddress : aucune bibliothèque supplémentaire n’est requise.

Créez un répertoire publié par votre serveur Web, par exemple /srv/www/geoip, puis protégez-le par HTTPS. Le script suivant télécharge la liste belge, refuse un fichier trop court, valide chaque réseau IPv4, agrège les préfixes qui peuvent l’être et génère un fichier RouterOS de manière atomique.

#!/usr/bin/env bash
set -Eeuo pipefail

COUNTRIES=(be)
BASE_URL="https://raw.githubusercontent.com/ipverse/rir-ip/master/country"
PUBLISH_DIR="/srv/www/geoip"
FINAL_FILE="$PUBLISH_DIR/GeoIP-BE.rsc"
WORK_DIR="$(mktemp -d)"
PUBLISH_TMP="$(mktemp "$PUBLISH_DIR/.GeoIP-BE.XXXXXX")"

cleanup() {
    rm -rf "$WORK_DIR"
    if [[ -n "${PUBLISH_TMP:-}" && -f "$PUBLISH_TMP" ]]; then
        rm -f "$PUBLISH_TMP"
    fi
}
trap cleanup EXIT

RAW_FILE="$WORK_DIR/all-prefixes.txt"
VALID_FILE="$WORK_DIR/validated-prefixes.txt"
: > "$RAW_FILE"

for country in "${COUNTRIES[@]}"; do
    curl --fail --location --silent --show-error --retry 3 \
        "$BASE_URL/$country/ipv4-aggregated.txt" \
        --output "$WORK_DIR/$country.txt"
    grep -Ev '^(#|$)' "$WORK_DIR/$country.txt" >> "$RAW_FILE"
done

python3 - "$RAW_FILE" "$VALID_FILE" <<'PY'
import ipaddress
import sys

source, destination = sys.argv[1:3]
networks = []
with open(source, encoding="utf-8") as handle:
    for raw_line in handle:
        line = raw_line.strip()
        if not line:
            continue
        network = ipaddress.ip_network(line, strict=True)
        if network.version != 4:
            raise SystemExit(f"Préfixe non IPv4: {line}")
        networks.append(network)

collapsed = list(ipaddress.collapse_addresses(networks))
if len(collapsed) < 100:
    raise SystemExit("Liste anormalement courte: publication annulée")

with open(destination, "w", encoding="ascii") as handle:
    for network in collapsed:
        handle.write(f"{network}\n")
PY

python3 - "$VALID_FILE" "$PUBLISH_TMP" <<'PY'
import sys

source, destination = sys.argv[1:3]
with open(source, encoding="ascii") as handle:
    prefixes = [line.strip() for line in handle if line.strip()]

with open(destination, "w", encoding="ascii") as output:
    output.write(':do { /ip firewall address-list remove '
                 '[find where list="GeoIP-BE-next"] } on-error={}\n')
    for prefix in prefixes:
        output.write('/ip firewall address-list add '
                     f'list="GeoIP-BE-next" address={prefix} '
                     'comment="GeoIP generated"\n')
    output.write(':local imported [/ip firewall address-list print '
                 'count-only where list="GeoIP-BE-next"]\n')
    output.write(':if ($imported < 100) do={ '
                 ':error "Liste GeoIP trop courte" }\n')
    output.write('/ip firewall address-list remove '
                 '[find where list="GeoIP-BE"]\n')
    output.write(':foreach id in=[/ip firewall address-list find '
                 'where list="GeoIP-BE-next"] do={ '
                 '/ip firewall address-list set numbers=$id '
                 'list="GeoIP-BE" }\n')
    output.write(':log info ("GeoIP: liste active, " . '
                 '$imported . " prefixes")\n')
PY

chmod 0644 "$PUBLISH_TMP"
mv -f "$PUBLISH_TMP" "$FINAL_FILE"
PUBLISH_TMP=""
echo "Publié: $FINAL_FILE"

Pour construire une zone plus large, remplacez COUNTRIES=(be) par COUNTRIES=(be fr nl lu) et adaptez les noms GeoIP-BE et GeoIP-BE-next. Commencez avec une seule zone : une liste étendue augmente le temps d’import et la mémoire utilisée.

Pourquoi ce générateur est plus sûr

  • curl --fail transforme une erreur HTTP en échec au lieu d’enregistrer une page d’erreur comme liste ;
  • ipaddress refuse une syntaxe CIDR invalide et vérifie qu’il s’agit bien d’IPv4 ;
  • un seuil minimal empêche la publication d’un fichier vide ou manifestement incomplet ;
  • le fichier public n’est remplacé qu’après génération complète ;
  • le fichier RouterOS charge d’abord GeoIP-BE-next, vérifie son volume, puis bascule vers GeoIP-BE.

Exécutez le générateur manuellement, vérifiez le fichier publié, puis planifiez-le quotidiennement sur le serveur. Conservez les journaux et ne supprimez pas immédiatement la dernière version connue comme valide.

Tester l’import sur MikroTik

Avant toute automatisation, exportez le pare-feu et activez le Safe Mode. Remplacez le domaine de documentation ci-dessous par votre propre URL HTTPS.

/ip firewall export file=avant-geoip
/system backup save name=avant-geoip
/tool fetch \
    url="https://updates.example.net/geoip/GeoIP-BE.rsc" \
    output=file dst-path="GeoIP-BE.rsc"

/import file-name="GeoIP-BE.rsc" verbose=yes dry-run=yes
/import file-name="GeoIP-BE.rsc" verbose=yes

Le mode dry-run de l’import est disponible sur les versions récentes de RouterOS 7. Il analyse le fichier sans modifier la configuration. L’environnement de ce guide cible RouterOS 7.16 ou une version ultérieure.

/ip firewall address-list print count-only where list="GeoIP-BE"
/ip firewall address-list print where list="GeoIP-BE"
/log print where message~"GeoIP"

Un import belge produit normalement bien plus de cent préfixes. Le seuil sert uniquement de garde-fou contre une rupture manifeste ; il ne prouve ni l’exhaustivité ni la qualité géographique du jeu de données.

Automatiser le téléchargement dans RouterOS

Créez un script nommé update-geoip avec les politiques ftp, read, write, policy et test. La politique ftp est nécessaire aux opérations sur les fichiers, même si le téléchargement lui-même utilise HTTPS.

:local geoUrl "https://updates.example.net/geoip/GeoIP-BE.rsc"
:local geoFile "GeoIP-BE.rsc"

:do { /file remove [find where name=$geoFile] } on-error={}

:do {
    /tool fetch url=$geoUrl output=file dst-path=$geoFile
    /import file-name=$geoFile verbose=yes dry-run=yes
    /import file-name=$geoFile verbose=yes
    :log info "GeoIP: mise a jour terminee"
} on-error={
    :log error "GeoIP: echec, ancienne liste conservee"
}

Le fichier importé travaille sur la liste temporaire. Une panne de téléchargement, une erreur de syntaxe ou un volume anormal interrompt la procédure avant la suppression de GeoIP-BE.

Après un test manuel réussi, planifiez l’exécution une fois par jour, à une heure où le routeur est peu sollicité.

/system scheduler add \
    name=update-geoip-daily \
    start-time=05:40:00 interval=1d \
    on-event="/system script run update-geoip" \
    policy=ftp,read,write,policy,test \
    comment="Actualisation de la liste GeoIP"

Appliquer la liste au bon endroit

La chaîne input traite le trafic destiné au routeur lui-même. La chaîne forward traite le trafic qui traverse le MikroTik vers un serveur interne. Une règle GeoIP placée uniquement dans input ne limite donc pas un serveur publié par dst-nat.

Cas 1 : limiter l’accès à un VPN hébergé sur le routeur

L’exemple autorise un point d’entrée WireGuard sur UDP 51820 depuis la liste GeoIP. Il suppose qu’une règle de rejet du trafic WAN vers le routeur existe plus bas. Ne publiez pas WinBox ou WebFig directement sur Internet.

/ip firewall address-list
add list=GeoIP-TRUSTED address=198.51.100.10 \
    comment="Adresse de secours - exemple RFC 5737"

/ip firewall filter
add chain=input action=accept in-interface-list=WAN \
    protocol=udp dst-port=51820 \
    src-address-list=GeoIP-TRUSTED \
    comment="VPN: acces de secours"

add chain=input action=accept in-interface-list=WAN \
    protocol=udp dst-port=51820 \
    src-address-list=GeoIP-BE \
    comment="VPN: autoriser la zone GeoIP"

Remplacez l’adresse de documentation par une véritable adresse de secours contrôlée. Placez les règles après l’acceptation de established,related et le rejet des paquets invalid, mais avant le rejet général du WAN.

Cas 2 : limiter un serveur publié par dst-nat

La règle suivante abandonne les nouvelles connexions TCP vers le port public 443 lorsqu’elles n’appartiennent pas à GeoIP-BE. Une règle de secours placée avant elle permet une exception explicite.

/ip firewall filter
add chain=forward action=accept in-interface-list=WAN \
    connection-state=new connection-nat-state=dstnat \
    protocol=tcp dst-port=443 \
    src-address-list=GeoIP-TRUSTED \
    comment="Serveur: acces de secours"

add chain=forward action=drop in-interface-list=WAN \
    connection-state=new connection-nat-state=dstnat \
    protocol=tcp dst-port=443 \
    src-address-list=!GeoIP-BE \
    comment="Serveur: refuser hors zone GeoIP"

Insérez ces règles avant une éventuelle acceptation générale des connexions dstnat. Le port doit être le port public vu par le filtre. Si le port externe 8443 est traduit vers 443, utilisez 8443 dans ce critère.

Tester sans se verrouiller dehors

  1. Travaillez depuis le LAN et activez le Safe Mode.
  2. Ajoutez d’abord votre accès de secours dans GeoIP-TRUSTED.
  3. Importez la liste et contrôlez son nombre d’entrées avant d’ajouter une règle de rejet.
  4. Testez depuis une connexion externe située dans la zone autorisée.
  5. Testez ensuite depuis une autre sortie contrôlée, par exemple un accès mobile ou un VPN de laboratoire.
  6. Vérifiez les compteurs sans activer un journal par paquet.
  7. Quittez le Safe Mode seulement lorsque le service et le chemin de secours fonctionnent.
/ip firewall filter print stats where comment~"GeoIP|zone"
/ip firewall address-list print count-only where list="GeoIP-BE"
/system resource print
/log print where message~"GeoIP"

Limites et erreurs fréquentes

  • Confondre allocation et localisation : un préfixe enregistré en Belgique peut être annoncé ou utilisé ailleurs.
  • Autoriser une zone comme preuve d’identité : un relais situé dans la zone franchit le même filtre qu’un utilisateur légitime.
  • Supprimer la liste active avant le téléchargement : une panne transforme un filtre restrictif en verrouillage complet.
  • Télécharger en HTTP : une liste de sécurité doit être transportée en HTTPS depuis une origine contrôlée.
  • Importer sans dry-run : une erreur de syntaxe peut laisser une mise à jour partielle.
  • Placer la règle dans input pour un serveur interne : le trafic publié traverse forward.
  • Oublier IPv6 : une règle IPv4 ne protège pas un service également accessible en IPv6.
  • Charger trop de zones : de grandes listes ralentissent l’import et consomment les ressources du routeur.
  • Journaliser chaque refus : utilisez les compteurs de règles pour éviter une surcharge de logs.

Pour IPv6, générez une liste distincte depuis ipv6-aggregated.txt, importez-la dans /ipv6 firewall address-list et créez les règles correspondantes dans /ipv6 firewall filter. Ne laissez pas IPv6 devenir une voie de contournement involontaire.

Conclusion

Un filtrage GeoIP fiable sur MikroTik repose moins sur la quantité de pays bloqués que sur la qualité de la chaîne de mise à jour. Les données doivent être récupérées en HTTPS, validées comme réseaux CIDR, publiées atomiquement, importées à blanc et chargées dans une liste temporaire avant toute bascule.

Une fois cette base solide, l’address-list devient un critère simple du pare-feu : input pour un service du routeur, forward pour un serveur publié. La mesure réduit le bruit et l’exposition, mais conserve volontairement une place secondaire derrière le VPN, l’authentification, les mises à jour et une politique de pare-feu minimale.

Références techniques

À propos de l’auteur

Jeremy Kraft administre les infrastructures et les services auto-hébergés de l’IBCSC. Ces guides reprennent des configurations réellement déployées, revérifiées avant publication.

Discussion

Commentaires

0

Aucun commentaire pour le moment. Vous pouvez ouvrir la discussion avec une question ou un retour d’expérience.

Vous avez un compte ? Se connecter

Texte simple, 2 000 caractères maximum. Les nouveaux commentaires sont modérés. Votre nom sera affiché avec le commentaire. Aucune adresse e-mail n’est demandée ; seule une empreinte non réversible de votre adresse IP est conservée pour lutter contre le spam.