traefik_warden

package module
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 15 Imported by: 0

README

RouteWarden Logo

RouteWarden

Lightweight Traefik middleware to block sensitive file exposure (.env, .git, backups), neutralize path-evasion tricks, whitelist trusted IPs, and respond cleanly before requests hit your backend.

GitHub Release CI Status Go Reference Documentation Site


Live Playground: Test rules, response modes, and bypass behaviors directly in your browser: https://routewarden.github.io/docs/?playground=open
Documentation & Guides: https://routewarden.github.io/docs/
Example Scenarios: examples/ (Docker Compose and Kubernetes CRDs)


Supported Traefik Versions

Traefik Version Status Notes
Traefik v3.x (v3.0, v3.1, v3.2+) Supported Runs via standard Yaegi runtime, Docker labels, and Kubernetes CRDs
Traefik v2.x (v2.8 – v2.11+) Supported Compatible with Traefik v2 plugin mechanism
Traefik v1.x Not Supported Traefik v1 does not support plugins

What is RouteWarden?

Web apps accidentally expose sensitive files and administration paths all the time. Automated bots and scanners crawl the internet looking for these files around the clock.

RouteWarden sits directly inside Traefik to catch these requests before they ever reach your upstream application. Written in pure Go with zero external dependencies, it adds minimal overhead while giving you fine-grained control over how scanner probes are handled.

Key Capabilities

  • Block Common Sensitive Files: Protects .env*, .git, .aws, .sql, .bak, .conf, .yaml, server logs, and debug endpoints out of the box.
  • Normalize Sneaky Paths: Stops common evasion techniques like double URL-encoding (%252e%252e), path traversal, matrix parameters (/;param/.env), Windows backslashes, and null bytes before evaluating rules.
  • Whitelist Trusted IPs: Let office networks, VPNs, or internal subnets bypass inspection using single IPs or CIDR blocks (10.0.0.0/8, 100.64.0.0/10).
  • Flexible Response Actions: Choose how to answer blocked requests. Return a simple 404 Not Found so attackers think the path doesn't exist, send 403 Forbidden, render custom JSON or HTML, issue honeypot redirects, require Cloudflare Turnstile or hCaptcha challenges, silently drop TCP connections, or trigger an active gzip bomb against scanners.

Quick Start: Global Protection via EntryPoints (Protect All Services)

Instead of manually attaching routewarden to every individual router across dozens of microservices or containers, attaching RouteWarden directly to Traefik's entryPoints (e.g. web on :80 and websecure on :443) enforces security inspection globally for all incoming requests before any router or backend is reached.

Option A: Docker Compose (Global EntryPoint Shield)

All containers routed through Traefik are protected automatically—no router labels required on developer services:

services:
  traefik:
    image: traefik:v3.3
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--entrypoints.web.address=:80"
      # Attach routewarden globally to entryPoint 'web'
      - "--entrypoints.web.http.middlewares=warden-shield@docker"
      - "--experimental.plugins.routewarden.modulename=github.com/routewarden/traefik-warden"
      - "--experimental.plugins.routewarden.version=v1.2.1"
    ports:
      - "80:80"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - "traefik.enable=true"
      # Global EntryPoint middleware definition
      - "traefik.http.middlewares.warden-shield.plugin.routewarden.enabled=true"
      - "traefik.http.middlewares.warden-shield.plugin.routewarden.enableDefaultPatterns=true"
      - "traefik.http.middlewares.warden-shield.plugin.routewarden.response.mode=text"
      - "traefik.http.middlewares.warden-shield.plugin.routewarden.response.statusCode=404"
      - "traefik.http.middlewares.warden-shield.plugin.routewarden.response.body=404 page not found"

  # Any upstream service is now shielded automatically:
  webapp:
    image: nginx:alpine
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webapp.rule=Host(`localhost`)"
      - "traefik.http.routers.webapp.entrypoints=web"

Option B: Traefik Static & Dynamic File Configuration

1. Static Configuration (traefik.yml)

Attach routewarden@file directly to your global entryPoints:

entryPoints:
  web:
    address: ":80"
    http:
      middlewares:
        - warden-shield@file
  websecure:
    address: ":443"
    http:
      middlewares:
        - warden-shield@file

providers:
  file:
    filename: /etc/traefik/dynamic_conf.yml

experimental:
  plugins:
    routewarden:
      moduleName: github.com/routewarden/traefik-warden
      version: v1.2.1
2. Dynamic Configuration (dynamic_conf.yml)

Define the RouteWarden middleware once in your dynamic provider:

http:
  middlewares:
    warden-shield:
      plugin:
        routewarden:
          enabled: true
          enableDefaultPatterns: true
          # Block internal or admin endpoints
          pathPatterns:
            - '(?i)^/admin(/.*)?$'
            - '(?i)^/api/internal(/.*)?$'
          # Allow specific public paths or health checks
          allowPatterns:
            - '(?i)^/api/internal/health$'
            - '(?i)^/robots\.txt$'
          # Whitelist internal office / VPN ranges
          allowedIps:
            - "127.0.0.1"
            - "10.0.0.0/8"
          # Return 404 for blocked requests
          response:
            mode: text
            statusCode: 404
            body: "404 page not found"

  routers:
    # Router needs no middleware declaration—it is protected globally by the entryPoint!
    app-router:
      rule: "Host(`app.example.com`)"
      entryPoints:
        - web
      service: app-service

Configuration Reference

Option Type Default Description
enabled bool true Enables or disables the middleware.
enableDefaultPatterns bool true Blocks common sensitive files (.env*, .git, .aws, .sql, .bak, .log, configs).
enableDefaultAllowPatterns bool true Keeps standard crawler and discovery files accessible (/robots.txt, /sitemap.xml, /.well-known/*).
pathPatterns []string [] Additional custom regular expressions to block.
allowPatterns []string [] Regular expressions for paths that should always bypass blocking.
allowedIps []string [] Trusted IPv4/IPv6 addresses or CIDR blocks allowed to bypass path inspection.
methods []string ["GET"] HTTP request methods to inspect (for example: ["GET", "POST"]). Other methods pass through.
checkQuery bool false When true, also inspects query parameters against blocked patterns.
debug bool false When true, enables verbose debug logging to standard output.
securityLog bool true When true, emits structured JSON security audit logs on block (CrowdSec / SIEM compatible).
response.mode string "text" Action to take when a request is blocked: "text", "json", "html", "xml", "captcha", "redirect", "proxy", "silentDrop", "gzipBomb", "tarpit", "fakeSuccess", "rateLimitChallenge", or "infiniteStream".
response.statusCode int 403 HTTP status code returned to the client (such as 404, 403, 401, or 429).
response.body string "" Custom payload returned in the response body.

For the complete list of settings (including Captcha keys, custom HTML templates, and header injection), read the Full Configuration Reference.
Note on gzipBomb: Use this mode only on verified honeypot paths or endpoints targeted exclusively by bots (such as /.env or /wp-login.php). Never use it on shared generic routes where normal users or legitimate crawlers might get caught. Always keep enableDefaultAllowPatterns: true to avoid blocking /robots.txt.


CLI & Config Generation

You can use the official rwarden CLI tool to test path rules offline, validate configurations, and automatically generate Traefik dynamic YAML or Docker Compose labels directly from a unified routewarden.json schema:

# Install RouteWarden CLI
curl -fsSL https://routewarden.github.io/cli/install.sh | bash

# Or run via Docker
docker run --rm ghcr.io/routewarden/cli:latest version

Generating Traefik Configurations:

# Generate Traefik dynamic YAML middleware definition (dynamic.yml)
rwarden generate --target traefik-yaml --config routewarden.json > dynamic.yml

# Generate Traefik dynamic TOML middleware definition (dynamic.toml)
rwarden generate --target traefik-toml --config routewarden.json > dynamic.toml

# Generate Docker Compose labels block
rwarden generate --target traefik-labels --config routewarden.json

# Test a suspicious probe path against rules offline
rwarden test --path "/.env"

For complete documentation on the CLI, installation methods, and options, visit the RouteWarden CLI Documentation.


Documentation & Guides

For detailed setup instructions, architecture deep dives, and production examples, check the documentation:


Testing & Quality

RouteWarden is tested against automated data races and maintains 98.4% statement test coverage:

Test Suite Scope Command CI Status
Go Unit & Race Tests Core engine, IP CIDR filter, path normalization, response modes, and evasion vectors go test -v -race ./... CI
Statement Coverage Full test coverage report across all packages (98.4%) go test -coverprofile=coverage.out ./... 98.4% Coverage
# Run tests with the Go race detector
go test -v -race ./...

# Generate coverage profile
go test -coverprofile=coverage.out ./... && go tool cover -func=coverage.out

License

This project is licensed under the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultAllowPatterns = []string{
	`(?i)^/robots\.txt$`,
	`(?i)^/sitemap.*\.xml$`,
	`(?i)^/ads\.txt$`,
	`(?i)^/security\.txt$`,
	`(?i)^/\.well-known(/.*)?$`,
}

DefaultAllowPatterns contains typical legitimate endpoints that might otherwise match broad patterns.

View Source
var DefaultBlockPatterns = []string{

	`(?i)(^|/)(\.env.*|.*\.(txt|log|bak|backup|sql|conf|config|ini|yaml|yml))$`,

	`(?i)(^|/)\.(git|svn|hg|bzr|cvs)(/.*|$)`,

	`(?i)(^|/)\.(aws|ssh|kube|docker)(/.*|$)`,

	`(?i).*\.(tar|tar\.gz|tgz|zip|rar|7z|gz|bz2|iso|dump|sqlite|sqlite3|db)$`,

	`(?i)(^|/)(phpinfo\.php|info\.php|server-status|server-info|actuator(/.*)?|metrics|heapdump|trace|env)$`,

	`(?i)(^|/)(composer\.(json|lock)|package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Pipfile|Pipfile\.lock|requirements\.txt)$`,

	`(?i).*\.(pem|key|crt|pfx|p12|jks|kdb)$`,

	`(?i)(^|/)(dockerfile.*|docker-compose.*\.ya?ml)$`,

	`(?i)(^|/)\.ds_store$`,

	`(?i)(^|/)(wp-config\.php.*|configuration\.php.*|settings\.py|local_settings\.py)$`,
}

DefaultBlockPatterns contains well-known sensitive endpoints and file extensions.

Functions

func ExtractCandidatePaths

func ExtractCandidatePaths(rawPath, pathStr, requestURI string) []string

ExtractCandidatePaths normalizes and extracts all representations of a request URI path, neutralizing common evasion techniques like double encoding, backslash substitution, matrix parameters, and null bytes.

func ExtractClientIP

func ExtractClientIP(req *http.Request) string

ExtractClientIP extracts the client IP address from proxy headers or RemoteAddr socket.

func New

func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error)

New creates a new RouteWarden plugin handler.

Types

type CaptchaConfig

type CaptchaConfig struct {
	Provider string `json:"provider,omitempty"` // "turnstile", "hcaptcha", "recaptcha", or "custom"
	SiteKey  string `json:"siteKey,omitempty"`  // Public site key
	Title    string `json:"title,omitempty"`    // Challenge page title
	Template string `json:"template,omitempty"` // Custom HTML template
}

CaptchaConfig holds captcha configuration options.

type Config

type Config struct {
	Enabled                    bool            `json:"enabled,omitempty"`
	EnableDefaultPatterns      bool            `json:"enableDefaultPatterns,omitempty"`
	EnableDefaultAllowPatterns bool            `json:"enableDefaultAllowPatterns,omitempty"` // Controls built-in whitelist (robots.txt, sitemap.xml, .well-known)
	PathPatterns               []string        `json:"pathPatterns,omitempty"`               // Synonym for blockPatterns
	BlockPatterns              []string        `json:"blockPatterns,omitempty"`
	AllowPatterns              []string        `json:"allowPatterns,omitempty"`
	AllowedIPs                 []string        `json:"allowedIps,omitempty"` // Whitelist of IPs or CIDR subnets exempt from blocking
	Methods                    []string        `json:"methods,omitempty"`    // HTTP verbs to inspect (defaults to ["GET"])
	StatusCode                 int             `json:"statusCode,omitempty"`
	CustomResponseText         string          `json:"customResponseText,omitempty"`
	Action                     string          `json:"action,omitempty"` // Convenience alias for response mode (e.g. "silentDrop", "fakeSuccess", "json")
	Mode                       string          `json:"mode,omitempty"`   // Convenience alias for response mode
	CheckQuery                 bool            `json:"checkQuery,omitempty"`
	CheckHeaders               []string        `json:"checkHeaders,omitempty"` // Optional headers to inspect (e.g. X-Forwarded-Uri, X-Rewrite-URL)
	Debug                      bool            `json:"debug,omitempty"`        // Enable verbose debug logging to stdout/stderr
	SecurityLog                bool            `json:"securityLog,omitempty"`  // Emit structured JSON security audit events (CrowdSec/SIEM compatible) on block
	Response                   *ResponseConfig `json:"response,omitempty"`
}

Config holds the plugin configuration.

func CreateConfig

func CreateConfig() *Config

CreateConfig creates the default plugin configuration.

type IPFilter

type IPFilter struct {
	// contains filtered or unexported fields
}

IPFilter evaluates incoming requests against an IP or CIDR subnet whitelist.

func NewIPFilter

func NewIPFilter(allowedIPs []string) (*IPFilter, error)

NewIPFilter parses and creates an IPFilter from a list of IP strings and CIDR notation subnets.

func (*IPFilter) IsAllowed

func (f *IPFilter) IsAllowed(req *http.Request) bool

IsAllowed returns true if the client IP in the request matches any whitelisted IP or subnet.

type ResponseConfig

type ResponseConfig struct {
	Mode                     string            `json:"mode,omitempty"`                     // "text", "json", "html", "captcha", "redirect"
	StatusCode               int               `json:"statusCode,omitempty"`               // HTTP status code (e.g. 403, 404, 429)
	ContentType              string            `json:"contentType,omitempty"`              // Custom Content-Type header override
	Body                     string            `json:"body,omitempty"`                     // Response payload (JSON string, HTML, or text)
	Headers                  map[string]string `json:"headers,omitempty"`                  // Custom response headers (e.g. Retry-After, X-Blocked-By)
	RedirectURL              string            `json:"redirectUrl,omitempty"`              // Target URL when Mode is "redirect"
	ProxyURL                 string            `json:"proxyUrl,omitempty"`                 // Target backend honeypot URL when Mode is "proxy"
	Captcha                  *CaptchaConfig    `json:"captcha,omitempty"`                  // Captcha settings when Mode is "captcha"
	GzipBombMB               int               `json:"gzipBombMB,omitempty"`               // Uncompressed size in Megabytes for gzipBomb mode (default: 10)
	RetryAfterSeconds        int               `json:"retryAfterSeconds,omitempty"`        // Seconds for Retry-After header when Mode is "rateLimitChallenge" (default: 300)
	TarpitDelayMs            int               `json:"tarpitDelayMs,omitempty"`            // Milliseconds between bytes for tarpit mode (default: 1000)
	TarpitMaxDurationSeconds int               `json:"tarpitMaxDurationSeconds,omitempty"` // Max seconds before terminating tarpit connection (default: 60)
	StreamSizeMB             int               `json:"streamSizeMB,omitempty"`             // Size in Megabytes for infiniteStream/garbageStream mode (default: 100)
}

ResponseConfig defines how blocked requests should be answered.

type ResponseHandler

type ResponseHandler struct {
	// contains filtered or unexported fields
}

ResponseHandler manages custom response execution (JSON, HTML, Captcha, Redirect, Text).

func NewResponseHandler

func NewResponseHandler(respCfg *ResponseConfig, topStatusCode int, topCustomText string, silentDrop bool) (*ResponseHandler, error)

NewResponseHandler initializes a ResponseHandler with compiled templates and proxy handlers.

func (*ResponseHandler) ServeBlockedRequest

func (h *ResponseHandler) ServeBlockedRequest(w http.ResponseWriter, req *http.Request)

ServeBlockedRequest handles writing the configured response to the client.

func (*ResponseHandler) SetCaptchaTemplateForTest added in v0.3.3

func (h *ResponseHandler) SetCaptchaTemplateForTest(tmpl *template.Template)

SetCaptchaTemplateForTest allows unit tests to inject custom/faulty captcha templates.

func (*ResponseHandler) SetProxyHandlerForTest

func (h *ResponseHandler) SetProxyHandlerForTest(p http.Handler)

SetProxyHandlerForTest allows unit tests to inject a mock reverse proxy handler without listening on network sockets.

type RouteWarden

type RouteWarden struct {
	// contains filtered or unexported fields
}

RouteWarden is the Traefik middleware plugin handler.

func (*RouteWarden) ServeHTTP

func (rw *RouteWarden) ServeHTTP(w http.ResponseWriter, req *http.Request)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL