Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add remaining ban time to fail2ban #22

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions lib/rule.ex
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ defmodule PlugAttack.Rule do
Be careful not to use the same `key` for different rules that use the same
storage.

Passes `{:fail2ban, key}`, as the data to `block_action` calls when an
Passes `{:fail2ban, key, remaining_ban}`, as the data to `block_action` calls when an
abusive request is detected. Each misbehaving client is blocked after each
call and tracked for `:period` time. If more than `:limit` abusive requests
are detected within the `:period`, the client is banned for `:ban_for`.
Expand Down Expand Up @@ -129,15 +129,17 @@ defmodule PlugAttack.Rule do
ban_for = Keyword.fetch!(opts, :ban_for)
now = System.system_time(:millisecond)

if banned?(key, storage, now) do
{:block, {:fail2ban, :banned, key}}
else
track_fail2ban(key, storage, limit, period, ban_for, now)
case banned?(key, storage, now) do
{true, remaining_ban} -> {:block, {:fail2ban, :banned, key, remaining_ban}}
false -> track_fail2ban(key, storage, limit, period, ban_for, now)
end
end

defp banned?(key, {mod, opts}, now) do
mod.read(opts, {:fail2ban_banned, key}, now) == {:ok, true}
case mod.read(opts, {:fail2ban_banned, key}, now) do
{:ok, true, remaining_ban} -> {true, remaining_ban}
_ -> false
end
end

defp track_fail2ban(key, {mod, opts}, limit, period, ban_for, now) do
Expand Down
2 changes: 1 addition & 1 deletion lib/storage/ets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ defmodule PlugAttack.Storage.Ets do
def read(name, key, now) do
case :ets.lookup(name, key) do
[{^key, value, expires_at}] when expires_at > now ->
{:ok, value}
{:ok, value, expires_at - now}

_ ->
:error
Expand Down
2 changes: 1 addition & 1 deletion test/rules_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ defmodule PlugAttack.RuleTest do
:timer.sleep(1)
assert {:allow, {:fail2ban, :counting, :key}} = fail2ban()
:timer.sleep(100)
assert {:block, {:fail2ban, :banned, :key}} = fail2ban()
assert {:block, {:fail2ban, :banned, :key, 99}} = fail2ban()
:timer.sleep(200)
assert {:allow, {:fail2ban, :counting, :key}} = fail2ban()
end
Expand Down
2 changes: 1 addition & 1 deletion test/storage/ets_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule PlugAttack.Storage.EtsTest do
test "read/write" do
assert :error = Ets.read(__MODULE__, :foo, now())
Ets.write(__MODULE__, :foo, true, expires_in(20))
assert {:ok, true} == Ets.read(__MODULE__, :foo, now())
assert {:ok, true, 20} == Ets.read(__MODULE__, :foo, now())
:timer.sleep(30)
assert :error = Ets.read(__MODULE__, :foo, now())
end
Expand Down