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
- 1Ajoutez un site forward auth dans le tableau de bord et copiez son Site ID.
- 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.
- 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.
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
- nginx
auth_requestauth_requestinterroge/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 Manager
auth_requestLa même chose, collée dans l'onglet Advanced du proxy host, avec le gate sur le réseau Docker de NPM.
- Traefik
ForwardAuthUn middleware ForwardAuth dans les labels de votre app. Le plugin Traefik fait la même chose sans conteneur.
- Caddy
forward_authforward_authvers/check, en copiant les en-têtesX-IPScanner-*. Le module Caddy fait la même chose sans conteneur. - HAProxy
UPSTREAM_URLMode proxy avec
UPSTREAM_URL=http://app:80. L'app est le serveur de secours, le trafic passe donc quand le gate est arrêté. - Apache
UPSTREAM_URLMode 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 auth200 avec les en-têtes
X-IPScanner-*, ou 403 avec la page de blocage etX-IPScanner-Request-Id./blockedforward authLa page 403 pour l'identifiant de requête de
X-IPScanner-Request-Id, pourerror_pagede nginx./healthzles deux modes200 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_KEYVotre clé d'API. Sans clé, chaque requête passe sans vérification.
IPSCANNER_API_KEY_FILEFichier contenant la clé, pour les secrets Docker. Prioritaire sur
IPSCANNER_API_KEY.SITE_IDSite ID du tableau de bord. Le mode et la politique viennent alors du tableau de bord.
MODEpar défaut monitormonitor ou enforce. Avec un site, seul monitor a un effet : il maintient le site en mode monitor.
BLOCK_CLASSESpar défaut malicious_automationSans 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 privateAdresses 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-ForEn-têtes lus dans l'ordre depuis un proxy de confiance. Derrière Cloudflare :
CF-Connecting-IP.TIMEOUT_MSpar défaut 1500Budget d'une vérification, en millisecondes. En cas de dépassement, la requête passe.
CACHE_TTLpar défaut 600Secondes de réutilisation d'un verdict par site, IP et user agent. Les durées comme 10m fonctionnent aussi.
CACHE_SIZEpar défaut 10000Verdicts gardés en mémoire.
POLICY_TTLpar défaut 30Secondes de réutilisation de la politique du site. Les durées fonctionnent aussi.
SKIP_PATHSpar défaut fichiers statiquesRegex sur le chemin, sans distinction de casse. Les requêtes correspondantes ne sont pas vérifiées ; vide vérifie tous les chemins.
UPSTREAM_URLActive le mode proxy : les requêtes autorisées partent vers cette URL.
LISTENpar défaut :8080Adresse d'écoute.
HEALTH_PATHpar défaut /healthzEndpoint de santé.
BLOCK_MESSAGEpar défaut Blocked by IPScanner edge guard.Texte de la page 403.
IPSCANNER_API_URLpar défaut https://ipscanner.ioURL de base de l'API.
DEBUGpar défaut falseJournalise 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_PROXIESX-Forwarded-Forn'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.privateLa 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.
:8080Ne 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, skippedSi 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_blockCe qui est arrivé à la requête. En mode monitor :
would_flagetwould_block.X-IPScanner-Network-Classresidential_clean, hosting, vpn, tor, …Classe de réseau de l'adresse.
X-IPScanner-Anonymizedtrue, falseLe réseau masque où se trouve le visiteur.
X-IPScanner-Risk0-100Score de risque de l'adresse.
X-IPScanner-CountryDEPays 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.
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_4fQ8nZ2kLm7xR1vT9cBwCache 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.
- GET /v1/sites/{id}/policyNon décompté
La politique du site, si un Site ID est défini.
X-IPScanner-Source: forward_authAttribue 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
sitestringLe Site ID, s'il est défini.
ipstringL'adresse du visiteur.
user_agentstringLe User-Agent du visiteur.
headersobjectAccept, Accept-Language, Accept-Encoding, Sec-CH-UA* et Sec-Fetch-*, chacun coupé à 512 octets.
request_idstringLe
X-Request-Idde la requête ou un nouvel identifiant, affiché aussi sur la page de blocage.
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"
}'