Integraciones
Forward auth para nginx, Apache y HAProxy
Un contenedor pequeño al que nginx, Nginx Proxy Manager, Apache, HAProxy, Traefik o Caddy consulta en cada petición.
Instalación
- 1Añade un sitio forward auth en el panel y copia su Site ID.
- 2Arranca el gate en la red Docker de tu proxy con tu clave de API y el Site ID. No publiques su puerto.
- 3Añade el snippet de tu proxy.
ghcr.io/ipscanner/forward-auth es un binario estático para linux/amd64 y linux/arm64 que se ejecuta sin root y sin shell. Tienes binarios para Linux y macOS en la página de releases.
Un sitio nuevo empieza en modo monitor: añade cabeceras y no bloquea nada hasta que lo pases a enforce.
Resumen en la página de nginx, Apache y HAProxy, código en 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 por proxy
- nginx
auth_requestauth_requestconsulta/checken cada petición. Un visitante bloqueado ve la página 403 del gate, y si el gate no responde el tráfico pasa. nginx resuelve el gate al arrancar, así que arranca primero el gate. - Nginx Proxy Manager
auth_requestLo mismo, pegado en la pestaña Advanced del proxy host, con el gate en la red Docker de NPM.
- Traefik
ForwardAuthUn middleware ForwardAuth en los labels de tu app. El plugin de Traefik hace lo mismo sin el contenedor.
- Caddy
forward_authforward_authhacia/check, copiando las cabecerasX-IPScanner-*. El módulo de Caddy hace lo mismo sin el contenedor. - HAProxy
UPSTREAM_URLModo proxy con
UPSTREAM_URL=http://app:80. La app es el servidor de respaldo, así que el tráfico sigue si el gate cae. - Apache
UPSTREAM_URLModo proxy como en HAProxy, con la app como 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 con las cabeceras
X-IPScanner-*, o 403 con la página de bloqueo yX-IPScanner-Request-Id./blockedforward authLa página 403 para el ID de petición de
X-IPScanner-Request-Id, para elerror_pagede nginx./healthzambos modos200 en JSON: versión, modo, sitio, backoff, último error y la política en caché.
/check lee la petición original de las cabeceras del proxy: X-Forwarded-For (o IP_HEADERS), User-Agent, X-Forwarded-Uri o X-Original-URI, y X-Forwarded-Method o X-Original-Method.
Configuración
IPSCANNER_API_KEYTu clave de API. Sin ella, todas las peticiones pasan sin comprobar.
IPSCANNER_API_KEY_FILEArchivo con la clave, para secrets de Docker. Tiene prioridad sobre
IPSCANNER_API_KEY.SITE_IDSite ID del panel. El modo y la política vienen entonces del panel.
MODEpor defecto monitormonitor o enforce. Con un sitio, solo monitor tiene efecto: mantiene el sitio en modo monitor.
BLOCK_CLASSESpor defecto malicious_automationSin sitio: clases que enforce bloquea, separadas por comas, entre malicious_automation, ai_agent, tor, vpn, proxy, hosting, relay o none.
TRUSTED_PROXIESpor defecto privateDirecciones y rangos CIDR cuyas cabeceras de IP y URI se aceptan. private, cloudflare, o none para usar solo la dirección del par.
IP_HEADERSpor defecto X-Forwarded-ForCabeceras que se leen en orden desde un proxy de confianza. Detrás de Cloudflare:
CF-Connecting-IP.TIMEOUT_MSpor defecto 1500Presupuesto de una comprobación, en milisegundos. Si se agota, la petición pasa.
CACHE_TTLpor defecto 600Segundos que se reutiliza un veredicto por sitio, IP y user agent. También admite duraciones como 10m.
CACHE_SIZEpor defecto 10000Veredictos en memoria.
POLICY_TTLpor defecto 30Segundos que se reutiliza la política del sitio. También admite duraciones.
SKIP_PATHSpor defecto archivos estáticosRegex sobre la ruta, sin distinguir mayúsculas. Las peticiones que coinciden no se comprueban; vacío comprueba todas las rutas.
UPSTREAM_URLActiva el modo proxy: las peticiones permitidas van a esta URL.
LISTENpor defecto :8080Dirección de escucha.
HEALTH_PATHpor defecto /healthzEndpoint de salud.
BLOCK_MESSAGEpor defecto Blocked by IPScanner edge guard.Texto de la página 403.
IPSCANNER_API_URLpor defecto https://ipscanner.ioURL base de la API.
DEBUGpor defecto falseRegistra cada decisión en una línea. En modo proxy también añade las cabeceras de estado, clase y acción a las respuestas.
Un valor no válido detiene el gate al arrancar con una línea de log. La clave de API nunca se registra.
IP del cliente
TRUSTED_PROXIESX-Forwarded-Forsolo se lee si el par está en esta lista, recorriendo la cadena hasta la primera dirección que no es un proxy de confianza. Para cualquier otro se usa la dirección del par.privateEl valor por defecto. Encaja con un proxy que llega al gate por una red Docker o privada. Si otros en esa red pueden llegar al gate, pon la dirección de tu proxy.
:8080No publiques el puerto del gate: solo tu proxy debería llegar a él.
Cabeceras que añade
En un 200 de /check, para que tu proxy las pase a la app, y en la petición a tu app en modo proxy. El modo proxy quita las cabeceras X-IPScanner-* entrantes; los snippets hacen lo mismo en el proxy.
X-IPScanner-Statusok, error, timeout, backoff, skippedSi la comprobación se hizo y, si no, por qué.
X-IPScanner-Classhuman, verified_bot, ai_agent, …Clase de tráfico del visitante.
X-IPScanner-Actionallow, flag, block, would_flag, would_blockQué pasó con la petición. En modo monitor:
would_flagywould_block.X-IPScanner-Network-Classresidential_clean, hosting, vpn, tor, …Clase de red de la dirección.
X-IPScanner-Anonymizedtrue, falseLa red oculta dónde está el visitante.
X-IPScanner-Risk0-100Puntuación de riesgo de la dirección.
X-IPScanner-CountryDEPaís de la dirección.
X-IPScanner-Sitesite_...El Site ID, si hay uno.
Si una comprobación falla o se omite, solo se fija X-IPScanner-Status.
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_4fQ8nZ2kLm7xR1vT9cBwCaché y fail open
- Política del sitioGET /v1/sites/{id}/policy
Modo y política por clase, en caché 30 s. Si una actualización falla, la última política válida sigue 24 horas. Los cambios se aplican sin recargar.
- Caché de veredictos
Una comprobación por visitante (sitio, IP, user agent) cada 10 minutos, en memoria, por instancia del gate.
- Fail open
1,5 s para veredicto y política juntos. Ante un timeout o un error de la API la petición pasa y las comprobaciones paran 30 s, o 5 minutos tras un 401, 402, 403 o 429.
- Gate caído
nginx y NPM dejan pasar tras el timeout de conexión de 1 s, HAProxy manda el tráfico a la app al momento, Apache falla una petición y luego usa la app. Traefik responde 500 y Caddy 502, así que arranca el gate con
restart: unless-stopped. - Siempre pasan
Crawlers verificados, peticiones OPTIONS, direcciones privadas y de loopback, y rutas de la lista de exclusión.
- Bloqueado
Una página 403 con el ID de la petición. El modo monitor nunca bloquea.
- Plan Free
Los sitios del plan Free se quedan en modo monitor.
- Coste
2 unidades de petición por visitante fuera de caché. Las peticiones repetidas dentro de la ventana de caché son gratis.
Qué envía
- POST /v1/edge/checkCuenta como 2 solicitudes
Una llamada por visitante fuera de caché.
- GET /v1/sites/{id}/policyNo consume cupo
La política del sitio, si hay un Site ID.
X-IPScanner-Source: forward_authMarca las llamadas como de esta integración en tu tráfico y tu uso.
User-Agent: ipscanner-forward-auth/<version>La versión, que se muestra en la página del sitio en el panel.
Cuerpo de la petición
sitestringEl Site ID, si hay uno.
ipstringLa dirección del visitante.
user_agentstringEl User-Agent del visitante.
headersobjectAccept, Accept-Language, Accept-Encoding, Sec-CH-UA* y Sec-Fetch-*, cada una recortada a 512 bytes.
request_idstringEl
X-Request-Idde la petición o un ID nuevo, que también aparece en la página de bloqueo.
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"
}'