Skip to main content

Origin-side abuse signal middleware for FastAPI apps behind Cloudflare. Part of AppFirewall by Sireto.

Project description

appfirewall-fastapi

Origin-side abuse signal middleware for FastAPI apps behind Cloudflare.

Part of the AppFirewall platform by Sireto. Cloudflare protects your edge; AppFirewall sees what your CDN can't — parse failures, auth failures, and application-layer abuse signals at your origin — and closes the loop back to the edge.

Status: v0.1, pre-release. The public API (AppFirewallMiddleware, appfirewall.record) is stable; internals may change.

Install

pip install appfirewall-fastapi

Quick start

from fastapi import FastAPI
from appfirewall_fastapi import AppFirewallMiddleware

app = FastAPI()
app.add_middleware(
    AppFirewallMiddleware,
    api_key="afw_live_...",   # or set APPFIREWALL_API_KEY
)

That's it. The middleware will:

  • Resolve the real client IP from cf-connecting-ip (validated against Cloudflare's published IP ranges — never spoofable from outside).
  • Classify 404s as scanner / benign-miss / unknown using a pattern library of known probes (/wp-admin, /.env, /.git/config, path-traversal, etc.).
  • Ship events in batches (2s / 500 events, gzipped JSONL) to the AppFirewall ingest endpoint out-of-band, so your request path is never blocked.

Recording app-layer signals

The value AppFirewall provides over edge-only protection comes from signals your app can see but Cloudflare can't. Use appfirewall.record() inside your handlers:

from fastapi import HTTPException, UploadFile
from appfirewall_fastapi import appfirewall

@app.post("/upload")
async def upload(file: UploadFile):
    try:
        parsed = parse_flint(await file.read())
    except ParseError as e:
        appfirewall.record("upload.parse_failed", reason=str(e))
        raise HTTPException(400, "invalid format")
    appfirewall.record("upload.success", size=len(parsed))
    return {"ok": True}

record() is synchronous, non-blocking, and never raises — safe to sprinkle anywhere. Outside a request, it's a silent no-op.

Configuration

All options can be passed as keyword arguments to add_middleware or set via environment variables.

Option Env var Default Purpose
api_key APPFIREWALL_API_KEY (none) Bearer token. If unset, mode forces to "off".
endpoint APPFIREWALL_ENDPOINT https://ingest.appfirewall.io/v1/events Override for self-hosted ingest.
environment None Tag attached to every event. Useful for production/staging.
mode "ship" "ship" | "local" | "off".
local_log_path None In mode="local", write JSONL to this path instead of shipping.
trusted_proxies ("cloudflare",) Accept cf-connecting-ip / XFF from these peers.
classify_404 True Classify unknown 404s into scanner / benign / unknown.
rate_limit {"scanner": (10, 60.0)} Per-class limits: max per window, window seconds.
enforce_rate_limit False If True, send 429 when an IP exceeds its per-class limit in-process. Off by default — enforcement belongs at the edge.
on_error "ignore" "ignore" | "warn" | "raise".

Fail-open guarantees

This middleware is deliberately conservative:

  • If the middleware crashes, your app still serves the request.
  • If ingest is down, events are buffered and dropped silently when the buffer fills. A circuit breaker prevents retry storms.
  • If the API key is missing, mode flips to "off" with a single warning.
  • appfirewall.record() never raises, never blocks, never awaits.
  • Request latency overhead target: <1ms p99.

The tradeoff: the SDK's default is observation, not enforcement. Blocking bad actors happens at the Cloudflare edge via the AppFirewall control plane, which this SDK feeds. This is the design. For local enforcement in emergencies, set enforce_rate_limit=True.

Local development

Point mode="local" at a file on disk to iterate without an ingest endpoint:

app.add_middleware(
    AppFirewallMiddleware,
    api_key="dev",
    mode="local",
    local_log_path="/tmp/appfirewall.jsonl",
)

Tail the file to watch events as your app runs:

tail -f /tmp/appfirewall.jsonl | jq .

Development

This SDK lives in the appfirewall-sdk monorepo under python/appfirewall-fastapi/. From this directory:

pip install -e ".[dev]"
pytest
mypy src/
ruff check src/

All three must pass. See ../../docs/CONTRIBUTING.md for the cross-SDK PR workflow and docs/ARCHITECTURE.md for this SDK's module layout and design decisions. If you're an AI coding agent, start with ../../AGENTS.md.

License

Apache-2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

appfirewall_fastapi-0.2.0.tar.gz (37.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

appfirewall_fastapi-0.2.0-py3-none-any.whl (28.2 kB view details)

Uploaded Python 3

File details

Details for the file appfirewall_fastapi-0.2.0.tar.gz.

File metadata

  • Download URL: appfirewall_fastapi-0.2.0.tar.gz
  • Upload date:
  • Size: 37.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for appfirewall_fastapi-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b427939dd39dbd451f98a852c70b0537abfb560abbd263733b2e5f351f6857ab
MD5 52ff3bf00ac7a247a513bc6fa0cc2ef2
BLAKE2b-256 5ca29d920969401c78c724e80cb336ebefed559800287d8cafc136c8f934efa2

See more details on using hashes here.

Provenance

The following attestation bundles were made for appfirewall_fastapi-0.2.0.tar.gz:

Publisher: python-fastapi-publish.yml on cloudfirewall/appfirewall-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file appfirewall_fastapi-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for appfirewall_fastapi-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 84a768518281bf7aa325d2d28aa5797d472ab49beeeddb912a62ee6a5b820792
MD5 72dd64cc2c79c60614a36d1539f83f4c
BLAKE2b-256 2f0a377fb19caa128e51c7eeb5515c8b42d5fb2b733dce0f42b8466b5731bfe9

See more details on using hashes here.

Provenance

The following attestation bundles were made for appfirewall_fastapi-0.2.0-py3-none-any.whl:

Publisher: python-fastapi-publish.yml on cloudfirewall/appfirewall-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page