Skip to content

Configuration

rescaled WAF is being configured with a central YAML configuration file that needs to be provided when starting the processor.

The application accepts its configuration from the following sources (highest priority to the lowest priority):

  • CLI flags
  • Environment variables
  • This YAML config file
  • Compiled defaults

Full Configuration Example

server:
  # Host address to listen on for gRPC requests.
  host: "127.0.0.1"
  # Port to listen on for gRPC requests.
  port: 50051

log:
  # Log level: debug, info, warn, error.
  level: "info"
  # Log format: json (recommended) or text.
  format: "json"

# HTTP health/readiness endpoint.
health:
  # Host address for the HTTP health server.
  host: "127.0.0.1"
  # Port for the /healthz endpoint.
  port: 8081

# Feature toggles. Each feature can be independently enabled/disabled.
features:
  # GeoIP geolocation lookup using a MMDB database.
  geoip:
    # Master toggle for the GeoIP feature.
    enabled: false

    # Database configuration.
    database:
      # Source type: "file" (local path) or "url" (HTTP download).
      source: "file"
      # Path to the MMDB file (required when source is "file").
      path: "/data/GeoLite2-City.mmdb"
      # Download URL for the MMDB file (required when source is "url").
      # url: "https://example.com/GeoLite2-City.mmdb"
      # How often to re-download/reload the database.
      # Go duration string (e.g. "24h", "6h30m"). "0" = no refresh.
      refresh_interval: "0"

    # Inject X-Rescaled-Geo-* headers into upstream requests.
    upstream_headers:
      enabled: true

    # Make GeoIP data available for policy/rule evaluation.
    policy_usage:
      enabled: true

    # Header containing the client's real IP address.
    # Default: x-envoy-external-address (set by Envoy from XFF processing).
    client_ip_header: "x-envoy-external-address"

  # ASN (Autonomous System Number) lookup using a MMDB database.
  asn:
    # Master toggle for the ASN feature.
    enabled: false

    # Database configuration.
    database:
      # Source type: "file" (local path) or "url" (HTTP download).
      source: "file"
      # Path to the MMDB file (required when source is "file").
      path: "/data/GeoLite2-ASN.mmdb"
      # Download URL for the MMDB file (required when source is "url").
      # url: "https://example.com/GeoLite2-ASN.mmdb"
      # How often to re-download/reload the database.
      # Go duration string (e.g. "24h", "6h30m"). "0" = no refresh.
      refresh_interval: "0"

    # Inject X-Rescaled-ASN-* headers into upstream requests.
    upstream_headers:
      enabled: true

    # Make ASN data available for policy/rule evaluation.
    policy_usage:
      enabled: true

    # Header containing the client's real IP address.
    # Default: x-envoy-external-address (set by Envoy from XFF processing).
    client_ip_header: "x-envoy-external-address"

  # Inject a response header reporting total WAF processing time in milliseconds.
  processing_duration:
    # Master toggle for the processing duration header.
    enabled: false
    # Name of the response header to inject.
    header_name: "x-rescaled-processing-time-ms"

  # Prometheus metrics endpoint. When enabled, serves metrics on the
  # health server (same port as /healthz).
  metrics:
    enabled: false
    # Path for the metrics endpoint.
    path: "/metrics"

  # Automatic challenge issuance based on accumulated request weight.
  # When enabled, WEIGH rules adjust a per-request weight score during
  # evaluation. If no terminal action fires and the accumulated weight
  # meets or exceeds a threshold, the visitor is automatically challenged.
  auto_challenge:
    # Master toggle for weight-based auto-challenge.
    enabled: false
    # Weight thresholds. The highest threshold that the accumulated weight
    # meets or exceeds is selected. Each threshold specifies the challenge
    # algorithm ("metarefresh", "preact", or "pow") and difficulty (0–64).
    # thresholds:
    #   - name: light-check
    #     threshold: 10
    #     challenge: metarefresh
    #     difficulty: 1
    #   - name: moderate-check
    #     threshold: 25
    #     challenge: preact
    #     difficulty: 3
    #   - name: heavy-check
    #     threshold: 40
    #     challenge: pow
    #     difficulty: 6

  # IP blocking based on accumulated HIT scores from policy rules.
  # When enabled, HIT rules increment a per-IP suspicion score that persists
  # across requests within the configured time window. When a threshold is
  # reached, the IP is blocked for the configured duration.
  ip_blocking:
    # Master toggle for IP blocking.
    enabled: false
    # How long HIT scores persist per IP. Each new HIT refreshes the TTL.
    score_ttl: "1h"
    # Block thresholds (sorted by severity). The highest matching threshold
    # determines the block duration.
    # thresholds:
    #   - name: light-offender
    #     threshold: 10
    #     duration: "5m"
    #   - name: heavy-offender
    #     threshold: 50
    #     duration: "1h"
    #   - name: persistent-offender
    #     threshold: 100
    #     duration: "24h"
    #
    # Named IP lists to block on a static basis (references ip_lists keys).
    # static_lists:
    #   - "known-bad"
    #
    # Custom response for blocked IPs. Falls back to policy.defaults.deny.
    # response:
    #   status_code: 403
    #   body_from_file: "/etc/rescaled-waf/pages/blocked.html"
    #   headers:
    #     X-Blocked-By: rescaled-waf
    #
    # Persist blocklist and scores to disk on shutdown, reload on startup.
    persistence:
      enabled: false
      # file_path: "/var/lib/rescaled-waf/ip-block-state.json"

# Named IP lists for use in policy rules.
# Each key is the list name that rules reference.
ip_lists:
  # Example: inline list of known IPs/subnets.
  # known-bad:
  #   source: "inline"
  #   entries:
  #     - "192.168.1.0/24"
  #     - "10.0.0.1"
  #     - "2001:db8::/32"
  #
  # Example: URL-sourced list with periodic refresh.
  # tor-exits:
  #   source: "url"
  #   url: "https://datafeed.waf.rescaled.com/tor-exits"
  #   refresh_interval: "1h"
  rfc1918:
    source: "inline"
    entries:
      - 10.0.0.0/8
      - 172.16.0.0/12
      - 192.168.0.0/16

  ipv6-ula-link-local:
    source: "inline"
    entries:
      - fc00::/7
      - fe80::/10

# Policy rules define how requests are evaluated and acted upon.
# Rules are evaluated in order; the first terminal match (ALLOW, DENY,
# CHALLENGE) determines the outcome. Non-terminal actions (HIT, LOG) are
# collected along the way. If no terminal action is triggered, the request
# is being passed through upstream.
policy:
  # Challenge operational settings. Required when CHALLENGE rules are configured.
  challenge:
    # HMAC-SHA256 secret for signing challenge tokens and pass cookies.
    # Must be at least 32 characters. Required when any CHALLENGE rule exists.
    secret: ""
    # How long a pass cookie remains valid after successful verification.
    cookie_ttl: "24h"
    # How long a challenge token is valid (time to complete the challenge).
    challenge_ttl: "1m"
    # Cookie domain (empty = current domain only).
    cookie_domain: ""
    # Set Secure flag on the pass cookie (requires HTTPS).
    cookie_secure: true
    # Path to the HTML file for the meta refresh challenge page.
    # The file must contain the placeholder {{META_REFRESH_TAG}}.
    # Required when any CHALLENGE rule uses the "metarefresh" algorithm.
    metarefresh_html: ""
    # Path to the HTML file for the preact challenge page.
    # The file must contain the placeholders {{CHALLENGE_DATA}} and {{CHALLENGE_SCRIPT}}.
    # Required when any CHALLENGE rule uses the "preact" algorithm.
    preact_html: ""
    # Path to the HTML file for the proof-of-work challenge page.
    # The file must contain the placeholders {{CHALLENGE_DATA}} and {{CHALLENGE_SCRIPT}}.
    # Required when any CHALLENGE rule uses the "pow" algorithm
    # (including when pow is the resolved default).
    pow_html: ""

  # Default parameters for each action type. Individual rules may override.
  defaults:
    challenge:
      # Challenge algorithm: "pow" (proof-of-work), "preact", or "metarefresh".
      algorithm: "pow"
      # Difficulty level (0–64).
      difficulty: 4
    deny:
      # HTTP status code for DENY responses.
      status_code: 403
      # Path to an HTML file to serve as the DENY response body.
      body_file: ""
      # Inline plain-text body (alternative to body_file; mutually exclusive).
      # plain_text_body: "Access Denied"
      # Default response headers for all DENY responses.
      # Per-rule response.headers fully replace these (no merging).
      # headers:
      #   X-Blocked-By: rescaled-waf
    hit:
      # Default suspicion score increment per HIT (if no amount specified).
      amount: 1
    log:
      # Default log level for LOG actions (debug, info, warn, error).
      level: "info"

  # Rule definitions. Each rule needs a unique name, an action, and exactly
  # one scope field that determines which requests match.
  #
  # Supported actions: ALLOW, DENY, CHALLENGE, HIT, LOG, WEIGH
  #
  # Supported scope fields:
  #   all: {}                  - matches every request
  #   path: "/exact/path"      - exact path match
  #   path_regex: "^/api/.*"   - regex path match
  #   user_agent: "ExactBot"   - exact User-Agent match
  #   user_agent_regex: "bot"  - regex User-Agent match
  #   ip_list: "list-name"     - client IP in a named IP list
  #   geoip: "US"              - GeoIP country code (or list: ["US", "CA"])
  #   networks: 15169          - ASN number (or list: [15169, 13335])
  #   expression: '<CEL expr>' - single CEL expression
  #   expression:              - multiple CEL expressions (all must match)
  #     all:
  #       - 'method == "POST"'
  #       - 'path.startsWith("/api")'
  #   expression:              - multiple CEL expressions (any must match)
  #     any:
  #       - 'geoCountry == "CN"'
  #       - 'geoCountry == "RU"'
  #
  # CEL expression variables:
  #   remoteAddress, host, method, path, userAgent, contentLength,
  #   headers (map), query (map)
  #   geoCountry, geoCountryName, geoCity, geoContinent (when GeoIP enabled)
  #   asnNumber, asnOrg (when ASN enabled)
  #
  # CEL custom functions:
  #   missingHeader(headers, "Name") - true if header is absent
  #   randInt(n)                     - random int in [0, n)
  #   regexSafe(s)                   - escape regex metacharacters
  #   ip_list("name")                - IP list container for 'in' operator
  #
  # Rules can also be imported from external YAML files:
  #   - import: "/path/to/rules.yaml"
  rules: []