Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bans and rate limits

An example

[sftp.ban]
max_failures = 5
window_secs = 300
ban_duration_secs = 600
whitelist_ips = ["10.0.0.0/8"]

[admin]
listen = "0.0.0.0:8080"

[admin.ban]
max_failures = 10
trusted_proxies = ["10.0.0.5"]   # the reverse proxy in front of the API

[api]
enabled = true

[api.ban]
max_failures = 5
trusted_proxies = ["10.0.0.5"]   # the same one: a single listener

Without the section, the door bans nothing.

One list per door

ListSectionDoors
sftp[sftp.ban]SFTP
api[api.ban]File API, download tickets, file explorer
admin[admin.ban]Admin API and admin console

The three lists are independent: an address banned on the file API still opens the admin console, and the other way around.

The keys of a ban

The same under [sftp.ban], [api.ban] and [admin.ban], read at startup.

All the keys: Reference [sftp.ban], [api.ban], [admin.ban].

What counts as a failure

A failure is a credential presented and judged wrong, whatever the account tried.

DoorCounts
SFTPwrong password or unknown name; JWT token judged wrong; key signature refused; a connection that ends without authenticating after declined keys (one failure per connection, not per key)
File APIwrong or malformed Basic; empty Bearer or token judged wrong
Admin console and admin APIwrong static token; empty Bearer, JWT or session token judged wrong; POST /admin/login: wrong password, unknown name or account without an admin role

These do not count: a disabled method, an expired or not yet valid token, a refused download ticket, whatever it is, a refusal after an accepted credential (no role, session limit), a refusal by the rate limiter, the refusal of an address already banned. A successful connection does not reset the counter.

With [api] cors_origins = ["*"], any web page can make a visitor’s browser send wrong credentials, which count toward the ban of the visitor’s address: list the origins of your applications.

The life of a ban

MomentEffect
max_failures failures within window_secsthe address is banned for ban_duration_secs; an ip_banned audit line
with [cluster]the failures the other instances hold in their window add up toward the threshold, read every second (≈ 1 s of delay); an unreachable instance stops counting after 5 s (2 × peer_timeout_ms + 1 s if longer)
on SFTP, at the verdictthe open SFTP sessions of the address are cut, transfers included
one more failure during the banthe end moves back to ban_duration_secs after it
the endthe ban ends, with no line
DELETE /admin/bans/{protocol}/{ip}the ban is lifted

An IPv4-mapped address (::ffff:10.1.2.3) is the same as 10.1.2.3, for the ban, the whitelist and the proxies.

Lifting a ban

GET /admin/bans (Bans tab of the admin console) lists them; lifting one requires the unban permission, {protocol} is sftp, api or admin:

curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/admin/bans/sftp/203.0.113.7

A lift is a dated decision: it cancels the verdicts decided before it, never a later verdict. It does not reset the counter.

Sharing bans between instances

BackendKeysSharing
memorynoneeach instance has its own bans, lost on restart; with [cluster], a lift is relayed to every instance, as with a file
filepersist_fileread at startup, published every 30 s, read again on a change and every reread_interval_secs; written atomically; on a shared volume (NFS, EFS) reading again is enough
ConfigMapbackend = "configmap", ban_configmap_namewatched through the Kubernetes API (feature k8s, a Role): Kubernetes

The three lists can share a file or a ConfigMap. The merge keeps, per address, the longest ban and the most recent verdict; a ban learned from a peer cuts the SFTP sessions of the address, with no ip_banned line (the one from the instance that decided it is enough). On shutdown, each list is published, 5 s at most each.

The address of an HTTP client: trusted_proxies

The HTTP doors take the TCP peer as the address. When this peer is in the trusted_proxies of their list ([api.ban] for the file API, [admin.ban] for the admin API), they take the rightmost address of X-Forwarded-For that is not a trusted proxy, failing that X-Real-IP. The ban, the limiter, the share of the hashing queue and the audit use this address. The SFTP door reads only the TCP peer.

Without [admin] control_listen, the file API and the admin API share a listener: [api.ban] and [admin.ban] then carry the same trusted_proxies.

Rate limits

A token bucket per address: burst connections or requests in a row, then the rate per minute. Without the section, no limit.

[sftp.rate_limit]
connections_per_minute = 30
burst = 10

[api.rate_limit]
requests_per_minute = 600

All the keys: Reference [sftp.rate_limit], [api.rate_limit].

The admin API has no rate limit. A rate refusal does not count toward the ban.

What you will see

WhenLine
a ban decidedaudit ip_banned; INFO IP banned after auth failures
sessions cut by a banINFO cut the sessions of a banned address; audit session_end reason=banned
a peer publishes bansINFO merged persisted bans from file