diff --git a/config/config.exs b/config/config.exs index c919a33..945f905 100644 --- a/config/config.exs +++ b/config/config.exs @@ -8,7 +8,7 @@ config :chessh, config :chessh, RateLimits, jail_timeout_ms: 5 * 60 * 1000, - jail_threshold: 15, + jail_attempt_threshold: 15, max_concurrent_user_sessions: 5 config :hammer, diff --git a/config/test.exs b/config/test.exs index bdaf9f1..69b6c93 100644 --- a/config/test.exs +++ b/config/test.exs @@ -2,7 +2,7 @@ import Config config :chessh, RateLimits, jail_timeout_ms: 1000, - jail_threshold: 2 + jail_attempt_threshold: 3 config :chessh, Chessh.Repo, database: "chessh-test", diff --git a/lib/chessh/ssh/daemon.ex b/lib/chessh/ssh/daemon.ex index cd0d466..9f17f75 100644 --- a/lib/chessh/ssh/daemon.ex +++ b/lib/chessh/ssh/daemon.ex @@ -22,24 +22,24 @@ defmodule Chessh.SSH.Daemon do end def pwd_authenticate(username, password, inet) do - [jail_timeout_ms, jail_threshold] = + [jail_timeout_ms, jail_attempt_threshold] = Application.get_env(:chessh, RateLimits) - |> Keyword.take([:jail_timeout_ms, :jail_threshold]) + |> Keyword.take([:jail_timeout_ms, :jail_attempt_threshold]) |> Keyword.values() {ip, _port} = inet rateId = "failed_password_attempts:#{Enum.join(Tuple.to_list(ip), ".")}" - case Hammer.check_rate(rateId, jail_timeout_ms, jail_threshold) do - {:allow, _count} -> - pwd_authenticate(username, password) || - (fn -> - Hammer.check_rate_inc(rateId, jail_timeout_ms, jail_threshold, 1) - false - end).() + if pwd_authenticate(username, password) do + true + else + case Hammer.check_rate_inc(rateId, jail_timeout_ms, jail_attempt_threshold, 1) do + {:allow, _count} -> + false - {:deny, _limit} -> - :disconnect + {:deny, _limit} -> + :disconnect + end end end diff --git a/test/auth/password_test.exs b/test/auth/password_test.exs index 5e1b1d8..8c93ea9 100644 --- a/test/auth/password_test.exs +++ b/test/auth/password_test.exs @@ -5,7 +5,8 @@ defmodule Chessh.Auth.PasswordAuthenticatorTest do @valid_user %{username: "logan", password: "password"} setup_all do - :ok = Ecto.Adapters.SQL.Sandbox.checkout(Chessh.Repo) + Ecto.Adapters.SQL.Sandbox.checkout(Repo) + Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()}) {:ok, _user} = Repo.insert(Player.registration_changeset(%Player{}, @valid_user)) diff --git a/test/auth/pubkey_test.exs b/test/auth/pubkey_test.exs index d8236e3..da2518b 100644 --- a/test/auth/pubkey_test.exs +++ b/test/auth/pubkey_test.exs @@ -9,7 +9,8 @@ defmodule Chessh.Auth.PublicKeyAuthenticatorTest do } setup_all do - :ok = Ecto.Adapters.SQL.Sandbox.checkout(Chessh.Repo) + Ecto.Adapters.SQL.Sandbox.checkout(Repo) + Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()}) {:ok, player} = Repo.insert(Player.registration_changeset(%Player{}, @valid_user)) diff --git a/test/ssh/ssh_auth_test-emacs-elixir-format.exs b/test/ssh/ssh_auth_test-emacs-elixir-format.exs deleted file mode 100644 index cb07259..0000000 --- a/test/ssh/ssh_auth_test-emacs-elixir-format.exs +++ /dev/null @@ -1,81 +0,0 @@ -defmodule Chessh.SSH.AuthTest do - use ExUnit.Case - alias Chessh.{Player, Repo, Key} - - @localhost '127.0.0.1' - @key_name "The Gamer Machine" - @valid_user %{username: "logan", password: "password"} - @client_test_keys_dir Path.join(Application.compile_env!(:chessh, :key_dir), "client_keys") - @client_pub_key 'id_ed25519.pub' - - setup_all do - case Ecto.Adapters.SQL.Sandbox.checkout(Repo) do - :ok -> nil - {:already, :owner} -> nil - end - - Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()}) - - {:ok, player} = Repo.insert(Player.registration_changeset(%Player{}, @valid_user)) - - {:ok, key_text} = File.read(Path.join(@client_test_keys_dir, @client_pub_key)) - - {:ok, _key} = - Repo.insert( - Key.changeset(%Key{}, %{key: key_text, name: @key_name}) - |> Ecto.Changeset.put_assoc(:player, player) - ) - - :ok - end - - test "Password attempts are rate limited" do - assert :disconnect == - Enum.reduce( - 1..Application.fetch_env!(:chessh, RateLimits, :jail_threshold), - fn _, _ -> - Chessh.SSH.Daemon.pwd_authenticate( - @valid_user.username, - 'wrong_password', - @localhost - ) do - end - ) - end - - test "INTEGRATION - Can ssh into daemon with password or public key" do - {:ok, sup} = Task.Supervisor.start_link() - test_pid = self() - - Task.Supervisor.start_child(sup, fn -> - {:ok, _pid} = - :ssh.connect(@localhost, Application.fetch_env!(:chessh, :port), - user: String.to_charlist(@valid_user.username), - password: String.to_charlist(@valid_user.password), - auth_methods: 'password', - silently_accept_hosts: true - ) - - send(test_pid, :connected_via_password) - end) - - Task.Supervisor.start_child(sup, fn -> - {:ok, _pid} = - :ssh.connect(@localhost, Application.fetch_env!(:chessh, :port), - user: String.to_charlist(@valid_user.username), - auth_methods: 'publickey', - silently_accept_hosts: true, - user_dir: String.to_charlist(@client_test_keys_dir) - ) - - send(test_pid, :connected_via_public_key) - end) - - assert_receive(:connected_via_password, 500) - assert_receive(:connected_via_public_key, 500) - end - - test "INTEGRATION - User cannot have more than specified concurrent sessions" do - :ok - end -end diff --git a/test/ssh/ssh_auth_test.exs b/test/ssh/ssh_auth_test.exs index cb07259..27d5e4c 100644 --- a/test/ssh/ssh_auth_test.exs +++ b/test/ssh/ssh_auth_test.exs @@ -3,17 +3,14 @@ defmodule Chessh.SSH.AuthTest do alias Chessh.{Player, Repo, Key} @localhost '127.0.0.1' + @localhost_inet {{127, 0, 0, 1}, 1} @key_name "The Gamer Machine" @valid_user %{username: "logan", password: "password"} @client_test_keys_dir Path.join(Application.compile_env!(:chessh, :key_dir), "client_keys") @client_pub_key 'id_ed25519.pub' setup_all do - case Ecto.Adapters.SQL.Sandbox.checkout(Repo) do - :ok -> nil - {:already, :owner} -> nil - end - + Ecto.Adapters.SQL.Sandbox.checkout(Repo) Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()}) {:ok, player} = Repo.insert(Player.registration_changeset(%Player{}, @valid_user)) @@ -30,15 +27,19 @@ defmodule Chessh.SSH.AuthTest do end test "Password attempts are rate limited" do + jail_attempt_threshold = + Application.get_env(:chessh, RateLimits) + |> Keyword.get(:jail_attempt_threshold) + assert :disconnect == Enum.reduce( - 1..Application.fetch_env!(:chessh, RateLimits, :jail_threshold), + 0..(jail_attempt_threshold + 1), fn _, _ -> Chessh.SSH.Daemon.pwd_authenticate( - @valid_user.username, - 'wrong_password', - @localhost - ) do + @valid_user.username, + "wrong_password", + @localhost_inet + ) end ) end @@ -75,7 +76,8 @@ defmodule Chessh.SSH.AuthTest do assert_receive(:connected_via_public_key, 500) end - test "INTEGRATION - User cannot have more than specified concurrent sessions" do - :ok - end + # TODO + # test "INTEGRATION - User cannot have more than specified concurrent sessions" do + # :ok + # end end