Integrating Anubis with Pangolin and Traefik

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 pangolin stack, 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


:page_facing_up: 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:

  1. A user visits pangolin.development.hhf.technology.

  2. The anubis-catchall-router, with its low priority (1), intercepts the request on the public websecure entrypoint and forwards it to the anubis-service.

  3. Anubis presents the proof-of-work challenge.

  4. Once solved, Anubis forwards the original request to its TARGET, which is http://traefik:3923 (the internal anubis entrypoint).

  5. The request re-enters Traefik on the anubis entrypoint. Now, the high-priority (10) protected routers (next-router, api-router) match the request and route it to the correct final service (next-service or api-service).

Step 4: Deploy and Verify

With the new configuration in place, redeploy your stack.

  1. From your terminal, run:

    docker compose up -d --force-recreate
    
    
    
  2. 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 traefik
    
    
    

    Look for messages in the Traefik log indicating it has detected changes in your rules file.

  3. 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.

Your file-based Traefik setup is now successfully protected by Anubis!

Next will publish on individual resources implementation via middlewaremanager.

2 Likes

I still have the same question like in t/applying-anubis-selectively-with-middleware-manager/3479/3

where’s the crowdsec settings?.

Anubis and Crowdsec are inherently different type of protection (PoW and IDS), it’s unreasonable to drop crowdsec if I want to put Anubis in the pangolin stack.

not to mention there’s also the ordering how traefik (or the administrator) should prioritize the middleware. crowdsec first then anubis before it get into the actual service or some different way.

1 Like

For some users, CrowdSec isn’t a viable option, especially in sanctioned regions or areas behind deep-packet-inspection firewalls. This is where a container like Anubis becomes useful.

If you are using Anubis or any similar protection (e.g., geo-blocking), it should always be placed before CrowdSec in your traefik file. CrowdSec developers are working to integrate Anubis support into the Traefik plugin.

thanks for this!

its working fine for me.

so all protected sites (auth via pangolin) are also protected via anubis.

is there an option to protect also the resources where the pangolin sso is deactivated, only via anubis?

this would be great.

1 Like

yes you can do that. you will have to use forward-auth and middleware manager.

Hello, is it possible to have explanation on how to achieve this ? thank you in advance

1 Like

My bad , i have not seen this topic : Applying Anubis Selectively with Middleware Manager

1 Like