Offline ASN-Based Filtering & Allowlisting
Routewarden supports Autonomous System Number (ASN) based filtering across all gateway plugins (Traefik Warden, Caddy Warden, NGINX Warden) and Layer 4 proxy infrastructure (TCP Warden and the CLI daemon).
ASN filtering allows security teams and DevOps operators to allowlist or admit traffic originating from entire telecommunications providers, cloud platforms (e.g., AWS, GCP, Cloudflare, Fastly), or corporate WANs without maintaining brittle, constantly churning lists of thousands of individual IP prefixes.
Key Highlights
- 100% Offline & In-Memory: ASN routing data is compiled into a local JSON routing snapshot and loaded into memory at startup. Zero DNS queries and zero runtime HTTP calls occur during live traffic inspection.
- Hierarchical Longest-Prefix Matching: Evaluates routes using longest-prefix radix matching. If an allowed parent network delegates a more specific
/24or/48subnet to another ASN, the child route does not inherit the parent's bypass. - Conflict Prevention: Snapshots containing conflicting origin declarations for the exact same prefix are rejected during startup validation.
- Dual-Stack IPv4 & IPv6: Seamlessly supports standard IPv4, IPv6, and IPv4-mapped IPv6 client addresses (
::ffff:192.0.2.1). - Resilient & Anti-Spoofing: Client-supplied ASN headers (e.g.
CF-Connecting-ASN,X-Forwarded-ASN) are never trusted. Routewarden strictly analyzes verified socket peer addresses or sanitized proxy forwarding headers viatrustedProxies/trusted_proxies.
Behavioral Semantics: HTTP vs Layer 4
Routewarden employs deliberately distinct semantics between HTTP reverse proxy middleware and Layer 4 proxy shields:
| Dimension | HTTP Gateways (Traefik, Caddy, NGINX) | Layer 4 Proxy (TCP Warden / CLI Proxy) |
|---|---|---|
| Primary Role | Inspection Allowlist / Bypass | Admission Gate & Perimeter Shield |
| ASN Match | Bypasses path and body inspection (identical to allowed_ips). | Admitted to security pipeline. Connection is still evaluated against IP filters, geo-blocking, rate limits, CrowdSec, and protocol guards. |
| ASN Non-Match | Continues normal inspection. Benign requests to legitimate paths pass through unhindered; malicious probes are blocked. | Connection Rejected Immediately. Emits asn_denied security audit event. |
| Unknown Origin (Private / RFC 1918) | Evaluated as an unknown ASN; continues to normal HTTP path inspection. | Rejected. Private addresses absent from the snapshot are denied admission unless listed in ip_filter.allow. |
| Configuration Keys | allowedASNs, asnDB (camelCase / snake_case) | asn_db, allowed_asns (global & per-service) |
1. Building the Routing Snapshot
Routewarden ASN resolution relies on an offline JSON snapshot file created with the bundled Python 3 utility scripts/build-asn-db.py.
Step-by-Step Generation
- Download the free GeoLite2 ASN CSV (or commercial GeoIP2 ASN CSV) archive from MaxMind:
GeoLite2-ASN-Blocks-IPv4.csvGeoLite2-ASN-Blocks-IPv6.csv
- Run
scripts/build-asn-db.pyto compile the IPv4 and IPv6 block CSVs into a unified snapshot:
# 1. Download GeoLite2 or GeoIP2 ASN CSV archives from MaxMind# Unpack GeoLite2-ASN-Blocks-IPv4.csv and GeoLite2-ASN-Blocks-IPv6.csv# 2. Compile into an optimized offline JSON routing snapshot:python3 scripts/build-asn-db.py -o /etc/routewarden/asn.json \ GeoLite2-ASN-Blocks-IPv4.csv GeoLite2-ASN-Blocks-IPv6.csv# 3. Verify snapshot file structure:head -n 12 /etc/routewarden/asn.jsonSnapshot Database Properties
- File Format: A compact JSON object mapping ASN strings (e.g.
"64500") to arrays of CIDR strings (["192.0.2.0/24", "2001:db8::/32"]). - Snapshot Immutability: The routing database is read into memory at process boot. To update routing tables in production, rebuild the JSON file and restart the gateway container or recreate the middleware instance.
- Container Deployments: Mount
/etc/routewarden/asn.jsoninto your gateway or proxy container as a read-only volume (:ro).
2. HTTP Reverse Proxy Middleware
In Traefik, Caddy, and NGINX, an ASN match acts as an allowlist exemption identical to allowed_ips:
- Matching clients bypass sensitive file checks, query string inspections, and honeypot triggers.
- Non-matching clients continue through regular pattern analysis. Legitimate public traffic is served normally, while scanners probing for
.env,.git, or SQL dumps receive your configured defense response.
Configuration Across Gateways
# dynamic_conf.ymlhttp: middlewares: asn-shield: plugin: routewarden: enabled: true # List of allowed ASNs (accepts "64500" or "AS64500") allowedASNs: - "AS13335" # Cloudflare - "64500" # Corporate WAN # Path to the mounted JSON routing snapshot asnDB: /etc/routewarden/asn.json # Upstream reverse proxies trusted for X-Forwarded-For resolution trustedProxies: - "10.0.0.0/8" - "172.16.0.0/12" response: mode: json statusCode: 403Configuration Syntax Reference
Traefik Warden
allowedASNs:[]string— List of ASN strings (e.g.["AS13335", "64500"]). Case-insensitive.asnDB:string— Absolute filesystem path to the compiledasn.jsonrouting snapshot.trustedProxies:[]string— Upstream proxy CIDRs. When set, evaluates the sanitized client IP fromX-Forwarded-For. Without this setting, evaluates the direct TCP peer socket.- Dynamic JSON configurations (
configFile) accept bothallowedASNs/asnDBandallowed_asns/asn_db.
Caddy Warden
allowed_asns <asns...>: Directive inroutewarden { ... }block.asn_db <path>: Directive pointing to the routing snapshot file.trusted_proxies <cidrs...>: Trusted proxy CIDR blocks.- Clearing Inherited Lists: Declaring a bare
allowed_asnsdirective with no arguments in a nested block explicitly clears an inherited allowlist. - Native Caddy JSON API accepts
allowed_asnsandasn_db.
NGINX Warden (OpenResty)
allowed_asns = { "AS13335", "64500" }: Lua table insideroutewarden.new({ ... }).asn_db = "/etc/routewarden/asn.json": Path to snapshot file.trusted_proxies = { "10.0.0.0/8" }: CIDRs trusted forX-Forwarded-For.- Shared JSON policy files accept camelCase and snake_case aliases.
Routewarden CLI & Simulator
routewarden.jsonschema supports"allowedASNs"and"asnDB".- Running
rwarden test --path "/.env" --ip 192.0.2.1evaluates ASN routing rules offline and reportsverdict: "ALLOW"with reasonasn_whitelisted. - Running
rwarden generatetranslates ASN rules into valid Traefik YAML/TOML, Caddyfile blocks, NGINX Lua tables, and TCP Warden YAML.
3. Layer 4 Admission Gating (TCP Warden)
In TCP Warden and the CLI daemon proxy (rwarden tcp), ASN allowlisting operates as an Admission Gate:
# tcp-warden.yamlglobal: max_connections: 10000 # Global routing snapshot database asn_db: /etc/routewarden/asn.json # Global default allowed ASNs (e.g. corporate WAN) allowed_asns: - "64500" log_level: "warn" log_file: "/var/log/routewarden/tcp-warden.jsonl"services: # Inherits global.allowed_asns (only AS64500 is admitted) internal_postgres: listen: ":5432" upstream: "10.0.1.20:5432" protocol: "postgres" response: mode: "reject" # Explicit service override: replaces global allowed_asns partner_api: listen: ":8443" upstream: "127.0.0.1:9443" protocol: "tcp" transport: "both" # TCP and UDP allowed_asns: - "AS13335" # Cloudflare - "64501" # Dedicated Partner ASN response: mode: "drop"Admission Gate Mechanics
- Perimeter Admission:
- When an effective ASN allowlist is active, incoming TCP connections or UDP datagrams must originate from an ASN in the allowlist.
- Connections from non-matching ASNs or unknown origins (such as private RFC 1918 subnets not declared in the snapshot) are denied admission immediately according to
response.mode(drop,reject,silent, ortarpit). - Rejections emit structured security audit events:json
{ "timestamp": "2026-10-27T08:00:00Z", "event": "asn_denied", "service": "internal_postgres", "client_ip": "198.51.100.99", "asn": "64599", "action": "reject" }
- Deep Pipeline Defense:
- Matching an allowed ASN merely admits the client to the proxy pipeline.
- It does not bypass security: The connection is still evaluated against IP filter rules (
ip_filter), country blocks (geo_block), rate limits (rate_limit), persistent bans, CrowdSec threat intelligence (crowdsec), and protocol-specific payload validation (SSH banner checks, Redis command filtering, SMTP sender domain blocks).
- Inheritance & Overrides:
- If a service defines its own
allowed_asns, it replaces theglobal.allowed_asnslist for that service. - If a service leaves
allowed_asnsempty or omitted, it inheritsglobal.allowed_asns. - To admit internal private networks (e.g.
10.0.0.0/8) alongside an ASN allowlist, specify them inip_filter.allow.
- If a service defines its own
Troubleshooting & Best Practices
TIP
Keep All ASNs in the Snapshot Do not prune un-allowed ASNs when creating asn.json. Longest-prefix matching depends on the presence of more specific routes owned by other ASNs to prevent inadvertent bypasses of parent subnets.
WARNING
Private IP Addresses & Layer 4 Admission MaxMind ASN databases do not include private RFC 1918 addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). In TCP Warden, if you configure an ASN admission gate and clients connect over private networks or VPNs, declare those private CIDRs in ip_filter.allow so they are not rejected with asn_denied.
IMPORTANT
Mounting Paths Across Containers Paths specified in asnDB / asn_db are resolved relative to the container filesystem. Ensure the JSON snapshot is mounted at the exact destination specified in your configuration (e.g., -v /opt/routewarden/asn.json:/etc/routewarden/asn.json:ro).