Counts failures per key (for example failed logins per client address) and bans a key for a while after too many of them.
children = [
{Sovite.Abuse.Penalty, name: MyApp.AuthPenalty, max_failures: 5, window: 600_000, ban_time: 3_600_000}
]
Penalty.banned?(MyApp.AuthPenalty, ip)
Penalty.failure(MyApp.AuthPenalty, ip)A key with :max_failures failures within :window milliseconds is
banned for :ban_time milliseconds. Successes do not reset the count,
so an attacker who owns one account cannot use it to keep guessing
others.
banned?/2 reads an ETS table and never waits on the process, so it
is cheap to call on every connection. State is in memory: a restart
forgets all bans.
Options
:name- an atom, also used as the ETS table name. Required.:max_failures- required.:window- milliseconds. Required.:ban_time- milliseconds. Required.:cleanup_interval- milliseconds between purges of old entries. Defaults to one minute.:replicate- whentrue, failures are also kept fortake_deltas/1. Defaults tofalse.
Sharing failures
Several nodes can count failures together: each takes the failures it
recorded (take_deltas/1) now and then, and sends them to the others,
which count them too (merge/2), so an attacker cannot spread guesses
over the nodes. A key banned by merged failures is banned on every
node. At most 10000 keys are kept between two calls.
Telemetry
[:sovite, :abuse, :penalty, :banned]-%{failures},%{penalty, key, ban_time}, when a key gets banned.
Summary
Functions
Returns whether key is banned now.
Records a failure for key. Returns :banned if key is banned now.
Counts failures recorded on other nodes (see take_deltas/1).
Forgets key, lifting any ban.
Starts the counter.
Returns the failures recorded since the last call, by key, for
merge/2 on other nodes. Empty unless started with replicate: true.
Functions
Returns whether key is banned now.
Records a failure for key. Returns :banned if key is banned now.
@spec merge(atom(), %{required(term()) => pos_integer()}) :: :ok
Counts failures recorded on other nodes (see take_deltas/1).
Forgets key, lifting any ban.
@spec start_link(keyword()) :: GenServer.on_start()
Starts the counter.
@spec take_deltas(atom()) :: %{required(term()) => pos_integer()}
Returns the failures recorded since the last call, by key, for
merge/2 on other nodes. Empty unless started with replicate: true.