Edgeweir
Guides

Bans

Block clients by IP address or CIDR for a limited time. Bans travel over the node channel directly, take effect within seconds and create no configuration revision.

Concepts

TermDefinition
BanAn IP address or CIDR whose requests are blocked until it expires.
ScopeSite: one site only. Global: every site of every cluster, also dropped in the kernel by the nodes (see Kernel bans). In the API these are site and platform.
SourceManual: created on the Bans page or through the API. Automatic: created by a node on a trigger and reported to the console.
Expiry1 minute to 7 days. Block for longer with IP lists.

Ban an address

  1. Open Bans and click "New ban".

  2. Pick the "Scope": "Site" or "Global".

  3. For a site ban, pick the site under "Site"; with many sites, type a name or domain into "Search sites" first.

  4. Fill in "IP or CIDR", e.g. 203.0.113.7 or 198.51.100.0/24.

  5. Pick a "Reason" and a "Duration" (1 h, 6 h, 1 d, 3 d, 7 d) and click "Ban".

  6. Verify: request the site from the banned address:

    curl -sI -H 'Host: www.example.com' http://<node IP>/

    The response is 403 with X-Edgeweir-Error: ip-banned. With a site ban, other sites still answer the same address.

Unban: click "Unban" in the row and confirm. The nodes drop the ban within seconds. Manual and automatic bans can both be lifted.

Without typing it again: the access logs, the top IPs of the analytics, and the top addresses and automatic bans on a site's Security tab offer "Ban IP" under ⋯ at the end of the row, with the site and address filled in (the "Scope" can change to "Global"); ⌘K / Ctrl+K → "Ban an IP…" opens the same dialog (with the site filled in on a site's pages). The toast after a ban links to the list ("View bans"). The link /bans?site=<site ID>&ip=<address> opens the dialog filled in the same way, and the list shows only the bans that overlap that address (× beside the address clears it).

The list shows active bans only (neither expired nor lifted), newest first, and filters by scope, site and source:

ColumnContent
AddressCanonical CIDR; "Not applied on N nodes" when nodes could not hold the ban
SiteSite name; "Global" for global bans
ReasonWhy the address is banned
Source"Manual" and the operator; "Automatic" and the node and trigger (metric, observed / threshold, window in seconds); automatic bans that are not sent to other nodes are marked "Not shared"
ExpiresA status light and the time left; hover for the expiry time. A ban that expired since the list was last refreshed shows "Expired"

Rules

ItemBehavior
AddressIPv4 / IPv6 address or CIDR; a single address means /32 or /128; host bits are cleared, IPv6 is lowercased and compressed; ::ffff:a.b.c.d counts as the IPv4 address; leading zeros and zone IDs are refused
Shortest prefixIPv4 /16, IPv6 /48
Expiry1 minute to 7 days (the UI offers 1 hour to 7 days); nodes drop a ban when it expires, the console deletes it an hour later
ReasonManual: abuse, attack, scanning, spam, other. Automatic: per-IP request rate
Banning againOne active manual ban per address for global bans, and per site and address for site bans; banning it again sets the new reason and expiry and does not add a ban
Protected addressesA site ban may not cover an address of a node in the site's cluster, a global ban no node address at all; neither may cover loopback (127.0.0.0/8, ::1) or unspecified addresses (0.0.0.0/8, ::), nor overlap an allow list
Where it appliesAt the edge layer once the site is known, before rules: global bans first, then site bans; addresses on an allow list are never banned
DeliveryNo configuration revision, no configuration canary, no reload on the nodes
Auditban.create, ban.update (banned again), ban.delete; automatic bans are not audited

Limits

LimitCountsWhere
Limit of manual bansActive manual bans, global and site bans togetherProtection settings → Bans → Limit of manual bans; 10000 by default, 100–100000
Automatic bansActive automatic bans per clusterFixed at 10000; the oldest automatic bans lapse first

Automatic bans do not count toward the limit of manual bans. Once the limit is reached, a new ban gets BAN_PLATFORM_LIMIT ("At most N manual bans can be active"); banning an address that is still banned is not limited.

Automatic bans

A node bans on its own on a trigger (the per-IP QPS of CC mitigation, see Challenges and CC mitigation): a single IPv4 address, or the IPv6 /64 (one client usually holds a whole /64); loopback addresses are never banned. The ban applies on that node at once and is reported to the console in batches every 5 seconds.

ItemBehavior
SharingProtection settings → Bans → Share automatic bans in the cluster, on by default: on, the ban goes to every node of the cluster; off, it is kept for viewing only and marked "Not shared". A change applies to automatic bans added afterwards
MergingOne entry per node, site and address; a repeated report extends the expiry
ChecksThe site must belong to the node's cluster; single addresses only; at most 7 days after creation; protected addresses are not stored
UnbanLike a manual ban: click "Unban" in the row; a ban that was not shared is deleted within seconds by the node that created it (older nodes without support keep it until it expires), and a later ban of the same address by that node is not affected

Nodes

ItemBehavior
CapabilityBans need bans-v1; older nodes without it keep serving and do not enforce bans
SyncA node receives the bans of its cluster and the global bans. It keeps the sequence it applied and fetches only later changes; on its first connection or after a console database restore it gets a full snapshot. Nodes store bans on disk and load them before connecting after a restart
CapacityNode options --ban-capacity (100000 entries by default) and --ban-dict-mb (32 MiB by default). Short of room, the oldest automatic bans go first
Not appliedManual bans never lapse silently: a node that cannot hold one reports it, the list shows "Not applied on N nodes" (online nodes only), and the node retries every minute
StatusEvery heartbeat reports the node's ban state: applied sequence, entries, capacity, unapplied bans, kernel entries and evicted automatic bans. Read it as banStatus of GET /api/v1/nodes/{id}

Kernel bans

The node agent also writes global bans into nftables, which drops the banned addresses' packets in the kernel: a banned client cannot even complete the TCP and TLS handshakes. Site bans apply at the HTTP layer only. L4 apps are subject to kernel bans only: site bans and HTTP-layer bans do not reach layer-4 traffic.

ItemBehavior
RequirementsThe agent has CAP_NET_ADMIN and the host has nft. The agent tries to create its table at start and reports the capability kernel-ban-v1 only when that works; otherwise it bans at the HTTP layer only and logs why
TableThe agent manages only its own table inet edgeweir (sets ban4, ban6, allow4, allow6, an input chain), removes leftovers at start and deletes the table on exit
Never bannedConsole addresses, the node's own addresses, loopback, allow lists
Outbound connectionsEvery inbound packet from a banned address is dropped, so the node cannot connect to that address either (for example if it happens to be an origin)

The default systemd unit and node image do not grant CAP_NET_ADMIN. Enable it as follows when needed.

systemd nodes

  1. Install nftables:

    Node
    sudo apt-get install -y nftables    # Debian, Ubuntu
    sudo dnf install -y nftables        # RHEL family
  2. Add a drop-in that grants CAP_NET_ADMIN next to the existing CAP_NET_BIND_SERVICE:

    /etc/systemd/system/edgeweir-node.service.d/kernel-ban.conf
    [Service]
    AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
    CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
  3. Reload and restart:

    Node
    sudo systemctl daemon-reload
    sudo systemctl restart edgeweir-node
  4. Verify: sudo nft list table inet edgeweir lists the sets ban4 and ban6; supportedFeatures of GET /api/v1/nodes/{id} contains kernel-ban-v1.

OpenResty, started by the agent, inherits the ambient capabilities as well.

Container nodes

  1. Build the node image with the build argument NFT_CAPABILITY=true. The image gives nft a file capability; the agent still runs as a non-root user:

    edgeweir-node source directory
    docker build --build-arg NFT_CAPABILITY=true -t edgeweir-node:nft .
  2. Run the container with --cap-add NET_ADMIN (Compose: cap_add: [NET_ADMIN]).

  3. Verify: docker exec <container> nft list table inet edgeweir lists the sets ban4 and ban6; supportedFeatures of GET /api/v1/nodes/{id} contains kernel-ban-v1.

Load balancers in front of the nodes

The cluster's client IP setting decides the visitor address (ip.src) of the HTTP and HTTPS listeners:

Client IPHTTP-layer bans, CC, rules, logsKernel bans
Direct (default)The TCP peer; behind a load balancer that is the balancer, and an automatic ban blocks all of itThe TCP peer
PROXY protocolThe visitor address from the PROXY headerStill the TCP peer only (the balancer): bans of visitor addresses do not take effect in the kernel
Trusted proxy headerThe visitor address the trusted proxies' header names; addresses inside the trusted CIDRs are never banned and not counted per address by CCLikewise the TCP peer only

With the last two, the Bans page shows "Clusters {clusters} take client addresses from proxies: kernel bans match the TCP peer only". In direct mode behind a load balancer, add the balancers' addresses to an allow list: they can then never be banned or dropped in the kernel.

L4 apps accept the PROXY protocol on their own listeners, independently of the client IP setting of the HTTP and HTTPS listeners.

API

Paths are under /api/v1.

ProcedureEndpointNotes
bans.listGET /bansActive bans; query parameters scope (site / platform), siteId, source (manual / auto), address (an IP or CIDR: the bans that cover it or lie inside it), page, pageSize
bans.createPOST /banssiteId is required when scope is site and not allowed when it is platform; also cidr, reason, durationSeconds
bans.deleteDELETE /bans/{id}Lifts a manual or automatic ban

Read-only AccessKeys can call only bans.list; service accounts cannot call the ban procedures (403 SERVICE_ACCOUNT_FORBIDDEN). Fields and examples: API and endpoints.

Troubleshooting

SymptomCauseFix
"Invalid IP address or CIDR"Not an IP address or CIDR, or it has leading zeros or a zone IDUse the standard notation
"The prefix is too short; the shortest allowed is /16" (or /48)The prefix is shorter than the minimumSplit it into longer prefixes, or use an IP list
"A ban lasts from 1 minute to 7 days"Expiry out of rangeUse an IP list to block for longer
"The ban covers the protected address …"The ban covers a node address, loopback or an unspecified address, or overlaps an allow listNarrow it
"At most N manual bans can be active"Limit of manual bans reachedUnban addresses no longer needed, or raise Protection settings → Bans → Limit of manual bans
"Choose a site"The scope is "Site" but no site is pickedPick a site, or set the scope to "Global"
"Ban not found or no longer active"The ban expired or was liftedRefresh the list
The list shows "Not applied on N nodes"Node ban capacity or memory exhaustedRaise the node's --ban-capacity and --ban-dict-mb, or ban less
A banned client still gets throughThe node lacks bans-v1; the address is on an allow listUpgrade the node; check the allow lists
Global bans do not apply in the kernelThe node lacks kernel-ban-v1Grant CAP_NET_ADMIN and install nftables as in Kernel bans
Edit on GitHub

On this page