kanopi/firewall
Kanopi Firewall is a lightweight PHP/Laravel package for controlling access by IP address. It offers configurable allow/deny rules and middleware integration to protect routes, block unwanted traffic, and manage firewall behavior from application configuration.
Architecture fit. kanopi/firewall is a Laravel/PHP request-firewall package whose crawler verification rules perform reverse-DNS (PTR) then forward-confirmation lookups. v2.38.0 introduces a resolver abstraction for those lookups: a global.reverse_dns.provider config key selecting a built-in DNS-over-HTTPS provider (cloudflare, google) or a custom provider/resolver class implementing ReverseDnsResolverInterface. This fits the package's existing config-driven, Laravel-service-provider shape: it is additive, opt-in, per-rule overridable (verify_provider, verify_timeout_ms), and validated at boot and by the existing firewall-check --lint / firewall-doctor commands. Critically, the resolver returns DNS data only — never a verdict — so the security decision path (hostname validation, domain check, forward confirmation) is unchanged. That is the right boundary for a security control.
Integration feasibility. Low effort for the common case: a single-config site that does not set reverse_dns.provider gets byte-for-byte previous behaviour (PHP's own lookups, no third-party traffic). Opting in requires only the PHP curl extension (widely available) plus three config lines. The real integration work is the config-merge semantics change, not the feature.
Technical risk.
Firewall::create([$base, $site]) now replace rather than append. trusted_proxies is the sharp edge: a second config that previously added proxies now removes the first config's proxies, which can break client-IP resolution, rate limiting, and rule matching in ways that look like unrelated bugs. lockdown_allow: [] in an included file now genuinely clears an allowlist — correct, but a live-traffic surprise if the include meant "nothing to add."Key questions.
Firewall::create(), and is any list (trusted_proxies, lockdown_allow, verify_suffixes, priority, enable) split across them?[] intending "add nothing"?curl present in every environment (web, queue, CLI, CI, container base images)?timeout_ms and verify_claim_wait_ms against the chosen provider's observed latency?Stack fit. Native. Configuration lives in the package's published config file alongside existing firewall settings; providers are selected declaratively and can be overridden per rule. Custom resolvers are plain classes bound through global.reverse_dns.providers or resolver + resolver_options, so an internally hosted DoH resolver is a supported first-class option — useful where egress to a public resolver is not permitted. Validation failures (unknown provider name, provider and resolver both set, unbuildable resolver) fail fast at startup rather than degrading silently.
Migration path.
Firewall::create() call sites and enumerate every config array passed in. Diff list-valued keys across those configs. Any key set in two configs must be consolidated into one before upgrading.[] on list keys; confirm each means "clear" or convert to omission.firewall-check --lint and firewall-doctor in staging; firewall-doctor reports where each verifying rule's lookups go, which doubles as the post-change assertion.Compatibility. PHP 8.1–8.5 supported — verify the platform matrix, especially if any environment is still on 8.0. No curl requirement unless a DoH provider is enabled. Legacy block:/bypass: entries change merge behaviour for embedded lists (whole replacement instead of positional), and a legacy rule's config: still appends; check any such rules with list values.
Sequencing. Treat this as two releases: (a) the merge-semantics change, which is a correctness fix with breaking potential and should ship alone behind normal change control; (b) the DoH provider, which is opt-in, privacy-gated, and should ship after legal review and a staged latency measurement. Do not bundle them in one deploy — if verification behaviour shifts, you want to know which change caused it.
Maintenance. Low steady-state. The package maintainers run a weekly canary covering reverse lookup, forward lookup, a no-record name, and full verification for each built-in provider, opening an issue on failure. Our own obligation is monitoring: track verification success/failure rates and breaker trip counts per resolver, and watch for provider IP changes given the pinned-address design.
Support. firewall-doctor gives support a single command to answer "where do this rule's lookups go," which materially shortens triage. The failure messages are specific (unknown provider, conflicting config, unbuildable resolver), so misconfiguration surfaces at boot rather than as mysterious 403s. Expect some support load from the upgrade itself: teams that relied on list-appending will see allow-list or trusted-proxy behaviour change with no error.
Scaling. Clear win. Per-lookup cost drops from a potential 10 s worker occupancy to a bounded 300 ms including connect and TLS, with warm-connection verification measured at 19–40 ms and first-connection at 66–93 ms. That improves worker utilisation and tail latency under crawler-heavy traffic, and keeps the circuit breaker from being tripped by a single hostile PTR owner (now capped at two forward lookups per address).
Failure modes.
Ramp-up. Low for the feature, moderate for the upgrade. An engineer can enable a provider in minutes from the documented YAML; the Reverse DNS docs include provider identity, data received, stated logging, and terms, which is exactly what a reviewer needs. Budget real time for the multi-config audit and the privacy review, and require a documented latency measurement per environment before setting timeout_ms — the guidance is to set it well above the provider's usual latency because the limit includes the TLS handshake.
How can I help you explore Laravel packages today?