Integrating Anubis with Pangolin and Traefik (File-Based Routing)
This guide provides a comprehensive walkthrough for integrating Anubis, a powerful proof-of-work bot mitigation tool, into your existing stack that uses Traefik’s file-based dynamic configuration. This method is ideal for complex routing scenarios like yours, where services are defined in YAML files rather than Docker labels.
The core principle remains the same: we will route all incoming public traffic to Anubis first. Anubis challenges the client, and if the challenge is passed, it forwards the legitimate request back to a private Traefik entrypoint, which then routes it to the correct backend service (pangolin).
Prerequisites
-
A working server with Docker and Docker Compose.
-
Your existing
pangolinstack, configured with Traefik’s file provider. -
A domain name pointed to your server’s IP address.
-
A valid TLS certificate resolver configured in Traefik (e.g.,
letsencrypt).
Step 1: Create a Dedicated Anubis EntryPoint in Traefik
First, we need to create a new, internal-only entrypoint in Traefik. Anubis will use this to send verified traffic back into Traefik. This step is done in your static configuration.
Modify your main traefik_config.yml file to add the anubis entrypoint.
# ./config/traefik/traefik_config.yml
entryPoints:
web:
address: :80
# ...
websecure:
address: :443
# ...
# Add this new entrypoint for Anubis
anubis:
address: ":3923" # This port should NOT be exposed to the public internet
# ... rest of your static traefik configuration
Key Point: The port :3923 is for internal communication within the Docker network. Do not expose this port in your docker-compose.yml ports section, as this would create a security vulnerability.
Step 2: Add the Anubis Service to Docker Compose
Next, add the anubis service to your docker-compose.yml. Note that this service definition does not contain any Traefik labels, as all routing will be handled by your dynamic configuration file.
# docker-compose.yml
name: pangolin
services:
pangolin:
image: fosrl/pangolin:1.8.0
container_name: pangolin
restart: unless-stopped
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "10s"
timeout: "10s"
retries: 15
gerbil:
image: fosrl/gerbil:1.0.0
container_name: gerbil
restart: unless-stopped
depends_on:
pangolin:
condition: service_healthy
command:
- --reachableAt=http://gerbil:3003
- --generateAndSaveKeyTo=/var/config/key
- --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config
- --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth
volumes:
- ./config/:/var/config
cap_add:
- NET_ADMIN
- SYS_MODULE
ports:
- 51820:51820/udp
- 3923:3923
- 8080:8080
- 443:443
- 80:80
traefik:
image: traefik:v3.4.0
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./config/traefik:/etc/traefik:ro
- ./config/letsencrypt:/letsencrypt
- ./traefik/plugins-storage:/plugins-storage:rw
- ./traefik/plugins-storage:/plugins-local:rw
- ./config/traefik/rules:/rules
- ./public_html:/var/www/html:ro
anubis:
image: ghcr.io/techarohq/anubis:main
container_name: anubis
restart: unless-stopped
networks:
- default
environment:
# Tells Anubis where to listen for incoming requests.
- BIND=:8923
# Tells Anubis to forward verified requests back to Traefik on the internal 'anubis' entrypoint.
- TARGET=http://gerbil:3923
# IMPORTANT: Change this to your top-level domain.
- COOKIE_DOMAIN=hhf.technology
# Automatically set the cookie domain based on the request's host header.
- COOKIE_DYNAMIC_DOMAIN=false
# Set the difficulty of the proof-of-work challenge.
- DIFFICULTY=4
# NO LABELS NEEDED HERE
middleware-manager:
image: hhftechnology/middleware-manager:traefik-int
container_name: middleware-manager
restart: unless-stopped
volumes:
- ./data:/data
- ./config/traefik/rules:/conf
- ./config/middleware-manager/templates.yaml:/app/config/templates.yaml
- ./config/middleware-manager/templates_services.yaml:/app/config/templates_services.yaml
- ./config/middleware-manager/config.json:/app/config/config.json
- ./config/traefik:/etc/traefik
environment:
- TRAEFIK_API_URL=http://gerbil:8080
- TRAEFIK_CONF_DIR=/conf
- DB_PATH=/data/middleware.db
- PORT=3456
- ACTIVE_DATA_SOURCE=pangolin
- TRAEFIK_STATIC_CONFIG_PATH=/etc/traefik/traefik_config.yml
- PLUGINS_JSON_URL=https://raw.githubusercontent.com/hhftechnology/middleware-manager/traefik-int/plugin/plugins.json
ports:
- "3456:3456"
networks:
default:
driver: bridge
name: pangolin
Step 3: Update Your Dynamic Routing Configuration
This is the most important step. Instead of creating a new file, we will modify your existing dynamic configuration file to integrate the Anubis logic.
Edit your dynamic routing file (e.g., located at ./config/traefik/rules/your-file.yml) to match the following structure.
# ./config/traefik/rules/your-file.yml
http:
middlewares:
# No changes needed here
redirect-to-https:
redirectScheme:
scheme: https
routers:
# --- UNPROTECTED ROUTERS ---
# This router remains on the 'web' entrypoint for HTTP->HTTPS redirection.
main-app-router-redirect:
rule: "Host(`pangolin.development.hhf.technology`)"
service: next-service # Service is a placeholder for redirect, this is fine
entryPoints:
- web
middlewares:
- redirect-to-https
# --- ANUBIS CATCH-ALL ROUTER (NEW) ---
# This NEW router catches all initial traffic on the public 'websecure' entrypoint
# and sends it to Anubis for verification.
anubis-catchall-router:
rule: "Host(`pangolin.development.hhf.technology`)"
service: anubis-service # Forwards to the new Anubis service
entryPoints:
- websecure
priority: 1 # Lowest priority to act as a catch-all
tls:
certResolver: letsencrypt
# --- PROTECTED ROUTERS (Original routers, now on 'anubis' entrypoint) ---
# These routers now only accept traffic that has been verified by Anubis.
next-router:
rule: "Host(`pangolin.development.hhf.technology`) && !PathPrefix(`/api/v1`)"
service: next-service
entryPoints:
- anubis # <-- MOVED from websecure
priority: 10 # Higher priority to be matched first on the internal entrypoint
api-router:
# This router now handles both standard API traffic and WebSocket connections under /api/v1
rule: "Host(`pangolin.development.hhf.technology`) && PathPrefix(`/api/v1`)"
service: api-service
entryPoints:
- anubis # <-- MOVED from websecure
priority: 10
# The 'ws-router' has been removed as it was conflicting and redundant. The 'api-router'
# or 'next-router' will now handle WebSocket traffic based on its path.
services:
# --- ANUBIS SERVICE (NEW) ---
# Defines how Traefik connects to the Anubis container.
anubis-service:
loadBalancer:
servers:
- url: "http://anubis:8923"
# --- ORIGINAL SERVICES (No changes needed) ---
next-service:
loadBalancer:
servers:
- url: "http://pangolin:3002" # Next.js server
api-service:
loadBalancer:
servers:
- url: "http://pangolin:3000" # API/WebSocket server
How it works:
-
A user visits
pangolin.development.hhf.technology. -
The
anubis-catchall-router, with its low priority (1), intercepts the request on the publicwebsecureentrypoint and forwards it to theanubis-service. -
Anubis presents the proof-of-work challenge.
-
Once solved, Anubis forwards the original request to its
TARGET, which ishttp://traefik:3923(the internalanubisentrypoint). -
The request re-enters Traefik on the
anubisentrypoint. Now, the high-priority (10) protected routers (next-router,api-router) match the request and route it to the correct final service (next-serviceorapi-service).
Step 4: Deploy and Verify
With the new configuration in place, redeploy your stack.
-
From your terminal, run:
docker compose up -d --force-recreate -
Check the logs of Traefik and Anubis to ensure they start correctly and that Traefik loads the updated routing rules.
docker compose logs -f anubis traefikLook for messages in the Traefik log indicating it has detected changes in your rules file.
-
Verify the integration:
-
Open a new private browsing window and navigate to
https://pangolin.development.hhf.technology. -
You should see the Anubis “Please wait, we are checking your browser…” page.
-
After the challenge completes, you should be seamlessly redirected to your Pangolin interface.
-
Subsequent requests should pass through immediately.
-
