Skip to content

WAF Reference

WAF rules are global public proxy rules evaluated before rate limits, traffic shapers, route resolution, and target forwarding.

In the management UI, choose Traffic Policy -> WAF. The WAF tab contains separate dense tables for WAF rules and captcha providers, a text and enabled-state filter for rules, and the Visitor identity & GeoIP settings below them. The shared Request Tester can supply a synthetic country result to preview geographic targeting without sending a request through a public listener.

Exact Fields And Defaults

Reserved WAF endpoints:

PathUse
/.p2pstream/waf/captcha/verifyCaptcha form verification.
/.p2pstream/waf/waiting-roomWaiting-room page endpoint.
/.p2pstream/waf/waiting-room/statusWaiting-room status and admission check.

ACME HTTP challenges bypass the WAF before these reserved endpoints are handled.

WAF rule defaults:

SettingDefault or limit
Namewaf-rule when empty
Priority100 in database defaults
ActionBlock
Activation modeAlways
Captcha pass TTL1800000 ms, 30 minutes
Captcha pass TTL range1 minute to 24 hours
Default keyremote IP
Block status403
Block body sourceInline
Block content typetext/plain; charset=utf-8
Block bodyRequest blocked\n
Block body limit64 KiB
Captcha page templateNone
Waiting-room page templateNone

Waiting-room defaults:

SettingDefaultRange
Max admitted sessions501 to 1,000,000
Admission rate10/sec1 to 100,000/sec
Admission session TTL600000 ms1 minute to 24 hours
Queue poll interval5000 ms1 to 60 seconds
Queue timeout1800000 ms1 minute to 24 hours
Page titleWaiting roomnon-empty custom text
Page bodyTraffic is high. You will be admitted automatically.non-empty custom text

Automatic activation defaults:

SignalDefault
Request window10000 ms
Minimum request rate50 rps
Traffic spike multiplier4
Proxy active requests100
Target active requests100
Agent active requests50
Server CPU85%
Agent CPU85%
Minimum active duration30000 ms
Quiet period60000 ms

Validation Rules

Choose Add Captcha Provider under Traffic Policy -> WAF to configure Cloudflare Turnstile, hCaptcha, or Google reCAPTCHA v2 checkbox. Provider secret keys are required, stored server-side, and represented only as saved/not-saved state after creation; they are not sent back to the UI. Captcha rules require an enabled provider.

p2pstream captcha provider drawer showing provider type, site key, secret key saved state, and enabled state
The captcha provider drawer stores the credentials used by captcha WAF rules. Saved secret keys are represented by state, not echoed back in full.
p2pstream Traffic Policy WAF tab showing a filterable WAF rule table, captcha providers, policy counts, execution order, Request Tester, and Visitor identity and GeoIP settings
The dedicated WAF tab keeps rules and captcha providers in separate compact tables. Policy counts and the execution-order strip show where WAF acts relative to the other policy tabs.

Block response template mode requires a selected generic_body response template.

Captcha page templates can only be selected for captcha WAF rules. The selected template must have kind waf_captcha_page and include {{ .captcha_element_html }}.

Waiting-room page templates can only be selected for waiting-room WAF rules. The selected template must have kind waf_waiting_room_page and include both {{ .queue_position }} and {{ .retry_after_seconds }}.

WAF rules use request-only CEL match_rule rules. Empty match rules match every request. See CEL Policy Matching for variables, helper functions, builder behavior, limits, and examples.

Route data, target data, target health, and load-balancer state are not available inside WAF match CEL. p2pstream may perform a route-only path security match before WAF, but WAF rules still run before route target selection.

WAF path matching uses p2pstream's decoded request path. On routes that allow encoded separators for upstream compatibility, avoid WAF rules that depend on decoded slash boundaries for authorization.

WAF key parts reuse rate-limit key sources: remote IP, host, method, path, protocol, header, cookie, and query parameter.

REMOTE_IP is the built-in client-IP identity source. With no trusted proxy enabled, it is the peer address seen by p2pstream and all client-IP headers are ignored. When the immediate peer belongs to an explicitly enabled trusted CDN or custom proxy source, p2pstream strictly resolves the visitor address from that source's configured header. A missing, malformed, or contradictory trusted header produces an unknown visitor address instead of trusting attacker-controlled input.

HEADER key parts remain supported for application headers such as X-Plan, but they cannot use forwarding or client-IP headers such as Forwarded, X-Forwarded-For, X-Real-IP, X-Forwarded-Host, X-Forwarded-Proto, X-Forwarded-Port, or common client-IP variants. Configure visitor identity through Visitor identity & GeoIP instead.

Before upgrading from an older version that allowed arbitrary header key parts, inspect stored WAF rules:

sql
SELECT id, name, key_parts_json
FROM public_waf_rules
WHERE lower(key_parts_json) LIKE '%forwarded%'
   OR lower(key_parts_json) LIKE '%x-real-ip%'
   OR lower(key_parts_json) LIKE '%client-ip%'
   OR lower(key_parts_json) LIKE '%connecting-ip%';

Automatic trigger thresholds accept 0 to disable individual signals. CPU percentages are 0 to 100.

Country Restrictions

Country restrictions use an automatically refreshed local MaxMind GeoLite2 Country database. They are disabled until an administrator supplies a MaxMind account ID and license key and enables GeoIP under Traffic Policy -> WAF, in Visitor identity & GeoIP. Database credentials are stored server-side and the license key is never returned by the management API.

Set Up GeoLite2 Country

  1. Create a free GeoLite account and accept the terms that apply to your use.
  2. Generate a MaxMind license key. Record it when it is shown; MaxMind displays a new key only once.
  3. Enter the numeric account ID and license key in Visitor identity & GeoIP, select Enable GeoIP country lookups, and choose Save GeoIP settings. Saving enabled settings performs the initial database download; the save fails if no usable database can be installed.
  4. Monitor database state, build date, last successful refresh, last attempt, and any error shown in the UI. p2pstream checks hourly and refreshes a ready database after 24 hours; failed downloads leave the last valid database active.
  5. Use Refresh database only when you need to force a later refresh. Pending form changes must be saved or discarded first.

Permit outbound HTTPS to download.maxmind.com and MaxMind's current presigned-download host. MaxMind documents that host and its redirect behavior in Updating GeoIP and GeoLite Databases.

Operators remain responsible for the GeoLite license terms, including any attribution and data-retention obligations. MaxMind currently requires prompt updates and destruction of superseded databases within 30 days; investigate stale or failed refresh warnings rather than relying indefinitely on an old file.

This product includes GeoLite Data created by MaxMind, available from MaxMind.

Each WAF rule can target:

  • selected countries, which applies the rule action only to listed country codes; or
  • countries outside the selection, which is an allow-only pattern for a block rule.

The country restriction is combined with the ordinary request match using AND. For example, a selected-country restriction plus host app.example.com only targets visitors from those countries requesting that host. Rules still use normal priority and first-match behavior. An allow-only country rule does not skip later WAF rules if it does not match.

Unknown-country behavior is selected per rule:

BehaviorRuntime effect
Apply ruleFail closed. The configured block, captcha, or waiting-room action applies.
Bypass geo restrictionFail open. This rule does not match the unresolved visitor.

A country is unknown when p2pstream cannot resolve a trusted visitor IP, the database has no record, the IP is private or special-use, or no usable database is loaded. GeoIP is inherently approximate and should not be treated as proof of a person's physical location.

Trusted Proxies And CDNs

No proxy source is trusted by default. Built-in presets are available for Cloudflare, Bunny, and Amazon CloudFront, and each one must be explicitly enabled by an administrator. Provider address ranges are downloaded from the provider's official endpoint and refreshed in the background; the last valid range set remains active if a refresh fails.

The managed contracts are:

PresetPeer ranges and client-IP contract
CloudflareCloudflare IP ranges API and the single-value CF-Connecting-IP header.
Bunny CDNBunny's origin IP allowlist and single-value X-Real-IP header.
Amazon CloudFrontAWS CLOUDFRONT_ORIGIN_FACING ranges, corresponding to its origin-facing managed prefix list, and CloudFront's append-only-at-the-edge X-Forwarded-For behavior. Viewer-edge-only CLOUDFRONT ranges are not trusted.

Fastly is intentionally custom-only. Fastly publishes origin-facing ranges, but its default Fastly-Client-IP header is not protected from client modification. Configure Fastly VCL or an edge rule to overwrite a dedicated header, then add that header and the documented Fastly ranges as a custom single-IP source.

Custom trusted sources require one or more peer CIDRs, a header name, and a parser mode:

ParserRequirement
Single IPThe header must contain exactly one IP literal. Use this when the proxy overwrites a dedicated header.
Trusted chainParses an IP-only comma-separated chain from right to left, removes hops from enabled trusted-chain sources using the same canonical header, and selects the first untrusted address. Single-IP and different-header sources never enlarge that chain's trust domain.

If the immediate peer is not trusted, forwarding headers never affect request identity. If multiple matching trusted sources disagree, identity resolution fails closed to unknown. Resolved visitor identity is used consistently by GeoIP, remote_ip CEL, rate-limit and shaper keys, captcha verification, and generated upstream client-IP headers. The network peer remains available internally for connection logging and reserved-endpoint abuse protection.

Application-level proxy trust does not prevent an attacker from reaching an exposed origin directly. Restrict the origin firewall to the selected CDN ranges whenever the deployment requires all traffic to traverse that CDN.

p2pstream WAF rule drawer showing match builder, geographic targeting, action, activation mode, key parts, response template, captcha, and waiting-room settings
The WAF rule drawer combines match rules, optional country targeting, key parts, action settings, custom responses, captcha provider selection, and waiting-room automation thresholds.

Runtime Effects

Rules are ordered by priority, then ID. The first enabled matching rule wins.

p2pstream verifies captcha tokens against the provider siteverify endpoint with a 3 second timeout. After success, it sets a signed p2pstream_waf_<rule_id> pass cookie and redirects with 303 See Other.

Waiting-room state is in memory. Admission and queue identity are stored in signed cookies. Valid admission cookies continue to pass after restart until expiry; queue cookies are accepted after restart, but visitors are re-enqueued because FIFO state is not persisted.

Custom WAF page templates are rendered with html/template. Normal placeholder values are escaped. The captcha element placeholder is trusted server-generated HTML so the provider widget and form can render.

Captcha and waiting-room passes only satisfy the matching WAF rule. The request still continues through rate limits, traffic shaping, route resolution, and target forwarding.

The original request body is never replayed after a captcha challenge or waiting-room admission.

Examples

Login captcha rule:

text
Action: Captcha
Host pattern: app.example.com
Path prefix: /login
Methods: POST
Key: remote IP
Captcha pass TTL: 1800000 ms

Automatic waiting room:

text
Action: Waiting room
Activation mode: Automatic
Host pattern: app.example.com
Minimum request rate: 50 rps
Target active requests: 100

Operations documentation for self-hosted p2pstream deployments.