IPScanner

Intégrations

Forward auth pour nginx, Apache et HAProxy

Un petit conteneur que nginx, Nginx Proxy Manager, Apache, HAProxy, Traefik ou Caddy interroge à chaque requête.

Installation

  1. 1Ajoutez un site forward auth dans le tableau de bord et copiez son Site ID.
  2. 2Lancez le gate sur le réseau Docker de votre proxy avec votre clé d'API et le Site ID. Ne publiez pas son port.
  3. 3Ajoutez le snippet de votre proxy.

ghcr.io/ipscanner/forward-auth est un binaire statique pour linux/amd64 et linux/arm64, exécuté sans droits root et sans shell. Les binaires Linux et macOS sont sur la page des releases.

Un nouveau site démarre en mode monitor : il ajoute les en-têtes et ne bloque rien tant que vous ne passez pas en enforce.

Présentation sur la page nginx, Apache et HAProxy, code source sur GitHub.

docker-compose.yml
services:
  ipscanner:
    image: ghcr.io/ipscanner/forward-auth:0.1
    restart: unless-stopped
    environment:
      IPSCANNER_API_KEY: ${IPSCANNER_API_KEY}
      SITE_ID: site_...

Snippets par proxy

  • nginxauth_request

    auth_request interroge /check à chaque requête. Un visiteur bloqué reçoit la page 403 du gate, et un gate injoignable laisse passer le trafic. nginx résout le gate au démarrage : lancez le gate en premier.

  • Nginx Proxy Managerauth_request

    La même chose, collée dans l'onglet Advanced du proxy host, avec le gate sur le réseau Docker de NPM.

  • TraefikForwardAuth

    Un middleware ForwardAuth dans les labels de votre app. Le plugin Traefik fait la même chose sans conteneur.

  • Caddyforward_auth

    forward_auth vers /check, en copiant les en-têtes X-IPScanner-*. Le module Caddy fait la même chose sans conteneur.

  • HAProxyUPSTREAM_URL

    Mode proxy avec UPSTREAM_URL=http://app:80. L'app est le serveur de secours, le trafic passe donc quand le gate est arrêté.

  • ApacheUPSTREAM_URL

    Mode proxy comme pour HAProxy, avec l'app en hot standby.

location / {
    auth_request /_ipscanner;
    auth_request_set $ipscanner_status     $upstream_http_x_ipscanner_status;
    auth_request_set $ipscanner_class      $upstream_http_x_ipscanner_class;
    auth_request_set $ipscanner_action     $upstream_http_x_ipscanner_action;
    auth_request_set $ipscanner_network    $upstream_http_x_ipscanner_network_class;
    auth_request_set $ipscanner_anonymized $upstream_http_x_ipscanner_anonymized;
    auth_request_set $ipscanner_risk       $upstream_http_x_ipscanner_risk;
    auth_request_set $ipscanner_country    $upstream_http_x_ipscanner_country;
    auth_request_set $ipscanner_site       $upstream_http_x_ipscanner_site;
    auth_request_set $ipscanner_request_id $upstream_http_x_ipscanner_request_id;
    error_page 403 = @ipscanner_blocked;

    proxy_set_header X-IPScanner-Status        $ipscanner_status;
    proxy_set_header X-IPScanner-Class         $ipscanner_class;
    proxy_set_header X-IPScanner-Action        $ipscanner_action;
    proxy_set_header X-IPScanner-Network-Class $ipscanner_network;
    proxy_set_header X-IPScanner-Anonymized    $ipscanner_anonymized;
    proxy_set_header X-IPScanner-Risk          $ipscanner_risk;
    proxy_set_header X-IPScanner-Country       $ipscanner_country;
    proxy_set_header X-IPScanner-Site          $ipscanner_site;
    proxy_set_header Host                      $host;
    proxy_set_header X-Forwarded-For           $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto         $scheme;
    proxy_pass http://app:80;
}

location = /_ipscanner {
    internal;
    proxy_pass http://ipscanner:8080/check;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Original-Method $request_method;
    proxy_connect_timeout 1s;
    proxy_read_timeout 5s;
    error_page 500 502 503 504 = @ipscanner_down;
}

location @ipscanner_down {
    return 204;
}

location @ipscanner_blocked {
    internal;
    proxy_method GET;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-IPScanner-Request-Id $ipscanner_request_id;
    rewrite ^ /blocked break;
    proxy_pass http://ipscanner:8080;
}

Endpoints

  • /checkforward auth

    200 avec les en-têtes X-IPScanner-*, ou 403 avec la page de blocage et X-IPScanner-Request-Id.

  • /blockedforward auth

    La page 403 pour l'identifiant de requête de X-IPScanner-Request-Id, pour error_page de nginx.

  • /healthzles deux modes

    200 en JSON : version, mode, site, backoff, dernière erreur et politique en cache.

/check lit la requête d'origine dans les en-têtes du proxy : X-Forwarded-For (ou IP_HEADERS), User-Agent, X-Forwarded-Uri ou X-Original-URI, et X-Forwarded-Method ou X-Original-Method.

Configuration

  • IPSCANNER_API_KEY

    Votre clé d'API. Sans clé, chaque requête passe sans vérification.

  • IPSCANNER_API_KEY_FILE

    Fichier contenant la clé, pour les secrets Docker. Prioritaire sur IPSCANNER_API_KEY.

  • SITE_ID

    Site ID du tableau de bord. Le mode et la politique viennent alors du tableau de bord.

  • MODEpar défaut monitor

    monitor ou enforce. Avec un site, seul monitor a un effet : il maintient le site en mode monitor.

  • BLOCK_CLASSESpar défaut malicious_automation

    Sans site : classes bloquées en mode enforce, séparées par des virgules, parmi malicious_automation, ai_agent, tor, vpn, proxy, hosting, relay ou none.

  • TRUSTED_PROXIESpar défaut private

    Adresses et plages CIDR dont les en-têtes IP et URI sont acceptés. private, cloudflare, ou none pour la seule adresse du pair.

  • IP_HEADERSpar défaut X-Forwarded-For

    En-têtes lus dans l'ordre depuis un proxy de confiance. Derrière Cloudflare : CF-Connecting-IP.

  • TIMEOUT_MSpar défaut 1500

    Budget d'une vérification, en millisecondes. En cas de dépassement, la requête passe.

  • CACHE_TTLpar défaut 600

    Secondes de réutilisation d'un verdict par site, IP et user agent. Les durées comme 10m fonctionnent aussi.

  • CACHE_SIZEpar défaut 10000

    Verdicts gardés en mémoire.

  • POLICY_TTLpar défaut 30

    Secondes de réutilisation de la politique du site. Les durées fonctionnent aussi.

  • SKIP_PATHSpar défaut fichiers statiques

    Regex sur le chemin, sans distinction de casse. Les requêtes correspondantes ne sont pas vérifiées ; vide vérifie tous les chemins.

  • UPSTREAM_URL

    Active le mode proxy : les requêtes autorisées partent vers cette URL.

  • LISTENpar défaut :8080

    Adresse d'écoute.

  • HEALTH_PATHpar défaut /healthz

    Endpoint de santé.

  • BLOCK_MESSAGEpar défaut Blocked by IPScanner edge guard.

    Texte de la page 403.

  • IPSCANNER_API_URLpar défaut https://ipscanner.io

    URL de base de l'API.

  • DEBUGpar défaut false

    Journalise chaque décision sur une ligne. En mode proxy, ajoute aussi les en-têtes status, class et action aux réponses.

Une valeur invalide arrête le gate au démarrage avec une ligne de log. La clé d'API n'est jamais journalisée.

IP du client

  • TRUSTED_PROXIES

    X-Forwarded-For n'est lu que si le pair figure dans cette liste, en remontant la chaîne jusqu'à la première adresse qui n'est pas un proxy de confiance. Pour tout autre pair, c'est son adresse qui compte.

  • private

    La valeur par défaut. Elle convient à un proxy qui joint le gate par un réseau Docker ou privé. Si d'autres machines de ce réseau peuvent joindre le gate, indiquez plutôt l'adresse de votre proxy.

  • :8080

    Ne publiez pas le port du gate : seul votre proxy doit pouvoir le joindre.

En-têtes ajoutés

Sur un 200 de /check, pour que votre proxy les transmette à l'app, et sur la requête vers votre app en mode proxy. Le mode proxy supprime les en-têtes X-IPScanner-* entrants ; les snippets font de même dans le proxy.

  • X-IPScanner-Statusok, error, timeout, backoff, skipped

    Si la vérification a eu lieu, et sinon pourquoi.

  • X-IPScanner-Classhuman, verified_bot, ai_agent, …

    Classe de trafic du visiteur.

  • X-IPScanner-Actionallow, flag, block, would_flag, would_block

    Ce qui est arrivé à la requête. En mode monitor : would_flag et would_block.

  • X-IPScanner-Network-Classresidential_clean, hosting, vpn, tor, …

    Classe de réseau de l'adresse.

  • X-IPScanner-Anonymizedtrue, false

    Le réseau masque où se trouve le visiteur.

  • X-IPScanner-Risk0-100

    Score de risque de l'adresse.

  • X-IPScanner-CountryDE

    Pays de l'adresse.

  • X-IPScanner-Sitesite_...

    Le Site ID, s'il est défini.

Si une vérification échoue ou est ignorée, seul X-IPScanner-Status est défini.

Requête vers votre app
X-IPScanner-Status: ok
X-IPScanner-Class: ai_agent
X-IPScanner-Action: would_block
X-IPScanner-Network-Class: hosting
X-IPScanner-Anonymized: true
X-IPScanner-Risk: 60
X-IPScanner-Country: US
X-IPScanner-Site: site_4fQ8nZ2kLm7xR1vT9cBw

Cache et fail open

  • Politique du siteGET /v1/sites/{id}/policy

    Mode et politique par classe, en cache 30 s. Si une mise à jour échoue, la dernière politique valide reste en place 24 heures. Les changements s'appliquent sans rechargement.

  • Cache des verdicts

    Une vérification par visiteur (site, IP, user agent) toutes les 10 minutes, en mémoire, par instance du gate.

  • Fail open

    1,5 s pour le verdict et la politique ensemble. En cas de dépassement ou d'erreur d'API, la requête passe et les vérifications s'arrêtent 30 s, ou 5 minutes après un 401, 402, 403 ou 429.

  • Gate arrêté

    nginx et NPM laissent passer après le délai de connexion de 1 s, HAProxy envoie aussitôt le trafic à l'app, Apache fait échouer une requête puis utilise l'app. Traefik répond 500 et Caddy 502 : lancez le gate avec restart: unless-stopped.

  • Toujours autorisés

    Crawlers vérifiés, requêtes OPTIONS, adresses privées et de loopback, et chemins de la liste d'exclusion.

  • Bloqué

    Une page 403 avec l'identifiant de requête. Le mode monitor ne bloque jamais.

  • Offre Free

    Les sites de l'offre Free restent en mode monitor.

  • Coût

    2 unités de requête par visiteur hors cache. Les requêtes répétées dans la fenêtre du cache sont gratuites.

Ce qui est envoyé

  • POST /v1/edge/checkCompte pour 2 requêtes

    Un appel par visiteur hors cache.

  • La politique du site, si un Site ID est défini.

  • X-IPScanner-Source: forward_auth

    Attribue les appels à cette intégration dans votre trafic et votre consommation.

  • User-Agent: ipscanner-forward-auth/<version>

    La version, affichée sur la page du site dans le tableau de bord.

Corps de la requête

  • sitestring

    Le Site ID, s'il est défini.

  • ipstring

    L'adresse du visiteur.

  • user_agentstring

    Le User-Agent du visiteur.

  • headersobject

    Accept, Accept-Language, Accept-Encoding, Sec-CH-UA* et Sec-Fetch-*, chacun coupé à 512 octets.

  • request_idstring

    Le X-Request-Id de la requête ou un nouvel identifiant, affiché aussi sur la page de blocage.

La vérification edge envoyée
curl https://ipscanner.io/v1/edge/check \
  -H "Authorization: Bearer $IPSCANNER_API_KEY" \
  -H "X-IPScanner-Source: forward_auth" \
  -H "User-Agent: ipscanner-forward-auth/0.1.0" \
  -H "Content-Type: application/json" \
  -d '{
    "site": "site_4fQ8nZ2kLm7xR1vT9cBw",
    "ip": "203.0.113.7",
    "user_agent": "Mozilla/5.0",
    "headers": {
      "accept-language": "en-US"
    },
    "request_id": "9f2c41b7d03e77f2"
  }'