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

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

All three must pass. See docs/CONTRIBUTING.md for the PR workflow and docs/ARCHITECTURE.md for the 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.1.1.tar.gz (50.5 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.1.1-py3-none-any.whl (28.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: appfirewall_fastapi-0.1.1.tar.gz
  • Upload date:
  • Size: 50.5 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.1.1.tar.gz
Algorithm Hash digest
SHA256 b0968263ca864db3719df27b78fbd23750ed49eefaaf6f855c2c23ca70a8bb74
MD5 805d7f060061d8669c75d988ff23a863
BLAKE2b-256 d751d7d04a680c790b28961a3a700657800b41440e5ad38748608125578ce104

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on cloudfirewall/appfirewall-fastapi

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.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for appfirewall_fastapi-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 578ca8577dcc1a61ec2972aa094a0ed4be51a315d8013b41a6c51810ccb8d3bb
MD5 db95b13271f55edf37f63d8df9cd4769
BLAKE2b-256 eb231a0707b6bb4ca9053b2a5e480363582304c026354b8cc223ca10c2b9ece5

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on cloudfirewall/appfirewall-fastapi

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