Configuration Keys¶
Every key the code reads, in one place. This page answers "what was that key called again" — press Cmd+F and search it.
For what a setting means and why you would change it, follow the link in the last column. Nothing here explains anything; that is deliberate.
The single most common mistake
FileStorage reads storage_file. FileRateLimitStorage reads file. They are different keys for the same idea, and getting it wrong does not error — the backend falls back to a hashed filename in the system temp directory, so the firewall appears to work while keeping its block list somewhere you did not choose and may lose on reboot.
Top level¶
| Key | Type | What it holds |
|---|---|---|
configs | list | Other files to merge in — paths, globs, URLs, {config_dir} / {presets_dir} tokens. Loading & Includes |
global | map | Site-wide behaviour. Table below |
storage | map | Where blocks are persisted. Table below |
logger | list | Monolog handlers. Logging |
challenge | map | Challenge flow settings. Table below |
tarpit | map | max_concurrent (default 5) and max_seconds (default 30) — see Tarpit |
plugins | list | The rules. Table below |
A configs: entry naming a file that does not exist empties the whole document
A missing include is a load failure, not a skipped line — every rule stops being configured. global.require_config: true turns that into a startup exception instead.
global:¶
| Key | Type | Default | ||
|---|---|---|---|---|
mode | string | block | block, log, exception, disabled, lockdown | Mode |
panic_file | string | unset | A file that overrides mode when it exists | Panic Switch |
banning_status_code | int | 400 | Status sent when a rule blocks | Status Code |
banning_message | string | built-in | Body template; {{request.id}} is substituted | Banning Message |
repeat_offender_status | int | 0 | Status for a client already on the block list | Global |
add_to_expire | int | 3600 | Seconds added to a ban on each repeat hit | Global |
blocking_escalation | list | [] | Window/offense/duration rules that lengthen bans | Multiple Offenses |
behind_proxy | bool | unset | Assert whether a proxy sits in front | Trusted Proxies |
require_trusted_proxies | bool | false | Refuse to start without setTrustedProxies() | Trusted Proxies |
require_config | bool | false | Refuse to start if any config input failed to load | Loading |
lockdown | bool | false | Refuse everyone but lockdown_allow | Lockdown |
lockdown_allow | list | (empty) | Addresses and CIDRs still served. Empty serves nobody | Lockdown |
lockdown_status | int | 503 | Lockdown | |
lockdown_retry_after | int | 300 | Seconds in Retry-After; 0 omits it | Lockdown |
lockdown_message | string | built-in | Supports {{request.id}} | Lockdown |
stale_source_error_after | int | 0 (off) | Seconds before an unrefreshed source is an error | Stale Rule Sources |
storage:¶
| Key | Type | |
|---|---|---|
type | class | FileStorage, DatabaseStorage, RedisStorage, InMemoryStorage |
config | map | Backend-specific, below |
| Backend | config keys | |
|---|---|---|
FileStorage | storage_file, offense_file | Paths; relative resolves against the YAML file |
DatabaseStorage | connection, storage_table, offenses_table, schema_check_probability | Storage |
RedisStorage | redis, instance | Requires ext-redis |
Every block-list backend also accepts record_request, which decides what a block record keeps of the visitor's cookies, headers, query and body. It defaults to an allowlist rather than everything. | InMemoryStorage | — | Per-process; nothing survives the request | | SharedStorage | shared, local | A fleet-wide list with a local copy underneath. Each is a {type, config} block — Storage |
Rate-limit counters are stored separately, under the RateLimit plugin's own metadata:
| Backend | config keys |
|---|---|
FileRateLimitStorage | file |
DatabaseRateLimitStorage | connection, schema_check_probability |
RedisRateLimitStorage | redis, instance, ttl |
CacheRateLimitStorage | adaptor, args, ttl |
InMemoryRateLimitStorage | — |
challenge:¶
| Key | Type | Default | ||
|---|---|---|---|---|
provider | string | required | math, altcha, turnstile, recaptcha, or a FQCN | Challenges |
secret | string | required | HMAC key for pass tokens. Startup fails if empty | Challenges |
path | string | /_firewall/challenge | The path a submission is recognised at, matched against getPathInfo() | Add a Challenge |
submit_url | string | path, prefixed with the request's base path | Where the interstitial's form POSTs to. Only set it if a proxy rewrites paths | Add a Challenge |
cookie_name | string | '' | Pass-token cookie; empty disables cookie delivery | Challenges |
header_name | string | '' | Header an SPA can send the token in | Challenges |
ttl | int | 3600 | How long a pass lasts: the default for rules naming none, and the ceiling for every rule and every submission | How long a pass lasts |
audience | string | provider name | aud claim, to scope tokens between instances | Scoping tokens |
passes_valid_from | int or date | unset | Refuse every pass issued before this moment. One value, no storage | Withdrawing a pass |
revocable | bool | false | Consult the per-nonce revocation list, at one storage read per valid pass | Withdrawing a pass |
provider_options | map | [] | Per-provider settings, keyed by provider name | Challenges |
plugins: — one entry¶
| Key | Type | Default | |
|---|---|---|---|
plugin | class | required | The rule class |
response | string | block | allow, block, challenge, record, redirect, mark, tarpit — decides the bucket, and buckets beat weights |
weight | int | 0 | Order within its bucket; lower runs first |
enable | bool | true | |
metadata | map | [] | How the rule behaves. Below |
config | list | [] | The rule's own entries — addresses, paths, patterns |
metadata: — common to every plugin¶
| Key | Type | Default | ||
|---|---|---|---|---|
name | string | class name | What the log calls it. Name every rule | Plugins |
mode | string | enforce | log observes this one rule without enforcing it | Observe mode |
status_code | int | banning_status_code | Per-rule override | Status Code |
default_expiration_time | int | challenge.ttl on a challenge rule, else 3600 | Ban length, or pass-token TTL. On a challenge rule it is capped by challenge.ttl | Global |
record | bool | true | false refuses without writing to the block list. On a redirect rule the default is false and true opts in | Evaluation Order |
mark_as | string | the rule's name | The signal response: mark raises, so several rules can raise one | |
mark_header | string | — | Also set this header on the request when marking | |
redirect_to | string | — | Required by response: redirect. Never built from the request, so it cannot become an open redirect | |
redirect_status | int | 302 | 301, 302, 307 or 308 | |
default_key | list | [client_ip, rule_pattern] | What rate limits count by, for rules declaring no key: | |
active | map | unset | timezone (default UTC), days, hours, from, until — when this rule is awake | Time Windows |
sources | list | [] | Pull this rule's entries from elsewhere | Rule Sources |
challenge_provider | string | challenge.provider | Per-rule provider override | Per-plugin providers |
tarpit_seconds | int | — | Required by response: tarpit. Capped by tarpit.max_seconds | Tarpit |
config | list | — | Legacy alias for the entry's config: | Legacy format |
Identity-verifying plugins (User Agent, and any implementing IdentityVerificationInterface) add verify, verify_cache, verify_ttl, verify_negative_ttl, verify_suffixes, verify_claim_wait_ms and verify_slow_threshold_ms — see User Agent.
Every sources: option is its own table in Rule Sources, including allow_catch_all, which decides whether a source may contribute an entry matching every address, max_size / max_entries, which bound how much a refresh may bring in, and checksum / signature, which decide whether the fetched bytes are checked against what the publisher asserted.
metadata: — edge signal rules¶
EdgeSignal reads what a CDN decided. See Edge Signals.
| Key | Type | Default | |
|---|---|---|---|
provider | string | custom | cloudflare, fastly, custom |
headers | map | [] | Signal to header name; layers over the profile |
Signals: bot_score, verified_bot, ja3, ja4.
config: — reputation rules¶
Reputation and AbuseIpdb take a map rather than a rule list. See Reputation.
| Key | Type | Default | |
|---|---|---|---|
provider | string | http (abuseipdb on AbuseIpdb) | abuseipdb, http, or a class implementing ReputationProviderInterface |
threshold | float | 75 | Score at or above which the rule matches, on the provider's scale |
cache_ttl | int | provider's | How long a verdict is reused |
error_cache_ttl | int | provider's | How long a failed lookup is remembered |
cache_dir | string | temp dir | Where verdicts are cached |
on_error | string | fail_open | fail_open, last_known_good |
subject | string | client_ip | What to score: client_ip, post.*, header.*, query.*, cookie.* |
subject_hash | string | unset | Send a digest of the subject instead of the value, e.g. sha256 |
block_status | int | 403 | Status returned when the rule blocks |
block_duration | int | 3600 | How long the address is remembered |
api_key | string | unset | abuseipdb: required, or the rule is inert |
max_age_in_days | int | 30 | abuseipdb: how far back reports count |
when | list | unset | Conditions gating the lookup — same syntax as any rule |
upstream | string or map | required | http: the endpoint, as a source declares one. Must contain {ip} |
format | string | json | http: json, txt, csv, tsv, ndjson, yaml, xml |
score_path | string | http: dot path to the score in the decoded body | |
score_pattern | string | http: or a regex with one capturing group, read from the raw body | |
trusted_path | string | unset | http: dot path to an "allow this one" flag |
trusted_pattern | string | unset | http: or a regex for the same |
content_type | string | application/json | http: sent with an upstream.body |
provider_name | string | see docs | http: what log lines call the service |
public_only | bool | true | http: false also looks up private addresses |
timeout | float | 2.0 | Seconds to wait before giving up |
PHP constants¶
Set before Firewall::create(). These exist because they must be readable before any YAML is parsed.
| Constant | Default | |
|---|---|---|
KANOPI_FIREWALL_CACHE_DIR | system temp | Where compiled config and source caches are written |
KANOPI_FIREWALL_CACHE_TTL | 3600 | Default rule-source TTL, when a source names none |
KANOPI_FIREWALL_CACHE_MAX_AGE | 30 days | Compiled-config entries older than this are swept; 0 disables |
KANOPI_FIREWALL_CACHE_MAX_STALE | — | How long a stale cached source may still be served |
KANOPI_FIREWALL_CACHE_TIMEOUT | — | Fetch timeout for remote sources |
KANOPI_FIREWALL_REQUIRE_CONFIG | false | Same as global.require_config |
KANOPI_FIREWALL_SOURCES_OFFLINE | false | Never fetch on the request path |
Environment Variables covers %env(...)% substitution, which works in any value above.