See the forums for further discussion here
This Traefik plugin provides a dynamic IP whitelisting mechanism with an admin approval flow. When a user tries to access a protected service and is not in the whitelist, they can request temporary access through a special endpoint. An administrator receives a notification with an approval link that can whitelist the user's IP for a configurable amount of time.
The flow works as follows:
/knock-knock) to request accessEnable the plugin in your Traefik static configuration:
# Static configurationexperimental:plugins:ipwhitelistshaper:moduleName: github.com/hhftechnology/ipwhitelistshaperversion: v1.0.3
Configure the middleware in your dynamic configuration:
# Dynamic configurationhttp:middlewares:my-ipwhitelistshaper:plugin:ipwhitelistshaper:# Base URL for approval linksapprovalURL: "https://your-traefik-instance.example.com"# URL for notifications (e.g., Discord webhook)notificationURL: "https://discord.com/api/webhooks/your-webhook-id"# Default endpoint for requesting accessknockEndpoint: "/knock-knock"# Allow private networks by default (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)defaultPrivateClassSources: true# Configure how long (in seconds) an approved IP should remain in the whitelistexpirationTime: 300# Depth to look into X-Forwarded-For header (set to 1 if behind CloudFlare or another proxy)ipStrategyDepth: 0# IPs to exclude from X-Forwarded-For processingexcludedIPs: []# Permanently whitelisted IPs (in addition to defaults)whitelistedIPs: []# Secret key for secure token generation (auto-generated if not provided)secretKey: ""# File-based storage configurationstorageEnabled: truestoragePath: "/plugins-storage/ipwhitelistshaper"saveInterval: 30 # Save every 30 seconds
This plugin now includes a file-based storage system to maintain state across Traefik instances and restarts. This ensures that approval tokens and whitelisted IPs remain valid even when Traefik instances are restarted or when requests are handled by different instances.
Persistent State: The plugin periodically saves its state to disk, including:
Automatic Recovery: When the plugin starts, it automatically loads the previously saved state from disk
Concurrent Safety: The storage system is designed to be thread-safe and handles concurrent access properly
You can configure the storage system with these parameters:
storageEnabled: Enable or disable file-based storage (default: true)storagePath: Directory where state files will be stored (default: "/plugins-storage/ipwhitelistshaper")saveInterval: How often to save state to disk in seconds (default: 30)To use file-based storage with Docker, you need to mount a volume to the storage path:
volumes:- ./traefik/plugins-storage:/plugins-storage:rw
This ensures that the storage directory is persistent and accessible to Traefik.
Apply the middleware to your HTTP routers to protect your services:
http:routers:# Main router that applies the whitelist protectionprotected-service:rule: "Host(`service.example.com`)"service: "my-service"middlewares:- "my-ipwhitelistshaper"# Special router to handle the knock-knock endpointknock-knock-router:rule: "Host(`service.example.com`) && Path(`/knock-knock`)"service: "my-service"middlewares:- "my-ipwhitelistshaper"priority: 110 # Higher priority than the main router# Special router to handle the approval endpointapprove-router:rule: "Host(`service.example.com`) && PathPrefix(`/approve/`)"service: "my-service"middlewares:- "my-ipwhitelistshaper"priority: 110 # Higher priority than the main router
Important: You must define separate routers for the knock-knock and approve endpoints with higher priority than your main router. This ensures that these special paths are correctly handled by the middleware.
The plugin sends two types of notifications:
To use Discord for notifications:
ipwhitelistshaper:notificationURL: "https://discord.com/api/webhooks/your-webhook-id/your-webhook-token"
The plugin automatically detects Discord webhook URLs and formats messages appropriately.
For other webhook-based notification services, simply provide the webhook URL:
ipwhitelistshaper:notificationURL: "https://your-notification-service.example.com/webhook"
Messages are sent with a message parameter in the request body.
If your Traefik instance runs behind another proxy (like CloudFlare), you'll need to adjust the IP strategy to correctly identify client IPs:
ipwhitelistshaper:# Use depth 1 to get the client IP from X-Forwarded-For when behind one proxyipStrategyDepth: 1
Or alternatively, use the excludedIPs strategy:
ipwhitelistshaper:# Exclude CloudFlare IPs from X-Forwarded-For processingexcludedIPs:- "103.21.244.0/22"- "103.22.200.0/22"# Add more CloudFlare IP ranges
- id: "ipwhitelistshaper"name: "IP Whitelist Shaper"type: "plugin"config:ipwhitelistshaper:knockEndpoint: "/knock-knock"approvalURL: "https://wallos.development.hhf.technology"notificationURL: "https://discord.com/api/webhooks/"defaultPrivateClassSources: trueexpirationTime: 300ipStrategyDepth: 0secretKey: ""excludedIPs: []whitelistedIPs:- "127.0.0.1/32"- "192.168.1.0/24"- "10.0.0.0/8"storageEnabled: truestoragePath: "/plugins-storage/ipwhitelistshaper"saveInterval: 30
providers:http:endpoint: "http://pangolin:3001/api/v1/traefik-config"pollInterval: "5s"file:directory: "/rules"watch: trueexperimental:plugins:badger:moduleName: "github.com/fosrl/badger"version: "v1.1.0"localPlugins:ipwhitelistshaper:moduleName: "github.com/hhftechnology/ipwhitelistshaper"
traefik:image: traefik:v3.3.3container_name: traefikrestart: unless-stoppednetwork_mode: service:gerbil # Ports appear on the gerbil servicedepends_on:pangolin:condition: service_healthycommand:- --configFile=/etc/traefik/traefik_config.ymlvolumes:- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs- ./traefik/plugins-storage:/plugins-storage:rw- ./traefik/plugins-storage:/plugins-local:rw- ./config/traefik/rules:/rules
[!Tip] I just loved the creation by l4rm4nd and i build a Traefik plugin around this idea. Original idea check it out here.
If you're seeing "Invalid token or IP address" errors when clicking approval links:
storagePath is set correctly and consistent across all instancesapprove-router is correctly configured in your Traefik configIf notifications aren't being sent:
notificationURL is correctIf state is not being properly maintained:
saveInterval to reduce disk operations