summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh
diff options
context:
space:
mode:
authorLogan Hunt <loganhunt@simponic.xyz>2022-12-29 18:37:51 -0700
committerGitHub <noreply@github.com>2022-12-29 18:37:51 -0700
commitc143bb549c53f2737c41cdfce6cc2598c5489bdc (patch)
tree33853e2a9c30ff7c670aa4ff5b73df005d6e6fec /lib/chessh/ssh
parentf7c2ccbe26dc808e4a7eae9a378e6c382220961a (diff)
parent479ca815e3a1760c71e8977674434b15f94ae833 (diff)
downloadchessh-c143bb549c53f2737c41cdfce6cc2598c5489bdc.tar.gz
chessh-c143bb549c53f2737c41cdfce6cc2598c5489bdc.zip
Merge pull request #1 from Simponic/erlang_ssh_server
Diffstat (limited to 'lib/chessh/ssh')
-rw-r--r--lib/chessh/ssh/daemon.ex77
-rw-r--r--lib/chessh/ssh/server.ex0
-rw-r--r--lib/chessh/ssh/server_key.ex12
3 files changed, 89 insertions, 0 deletions
diff --git a/lib/chessh/ssh/daemon.ex b/lib/chessh/ssh/daemon.ex
new file mode 100644
index 0000000..9f17f75
--- /dev/null
+++ b/lib/chessh/ssh/daemon.ex
@@ -0,0 +1,77 @@
+defmodule Chessh.SSH.Daemon do
+ alias Chessh.Auth.PasswordAuthenticator
+ use GenServer
+
+ def start_link(_) do
+ GenServer.start_link(__MODULE__, %{
+ pid: nil
+ })
+ end
+
+ def init(state) do
+ GenServer.cast(self(), :start)
+ {:ok, state}
+ end
+
+ def pwd_authenticate(username, password) do
+ # TODO - check concurrent sessions
+ PasswordAuthenticator.authenticate(
+ String.Chars.to_string(username),
+ String.Chars.to_string(password)
+ )
+ end
+
+ def pwd_authenticate(username, password, inet) do
+ [jail_timeout_ms, jail_attempt_threshold] =
+ Application.get_env(:chessh, RateLimits)
+ |> Keyword.take([:jail_timeout_ms, :jail_attempt_threshold])
+ |> Keyword.values()
+
+ {ip, _port} = inet
+ rateId = "failed_password_attempts:#{Enum.join(Tuple.to_list(ip), ".")}"
+
+ 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
+ end
+ end
+ end
+
+ def pwd_authenticate(username, password, inet, _address),
+ do: pwd_authenticate(username, password, inet)
+
+ def handle_cast(:start, state) do
+ port = Application.fetch_env!(:chessh, :port)
+ key_dir = String.to_charlist(Application.fetch_env!(:chessh, :key_dir))
+ max_sessions = Application.fetch_env!(:chessh, :max_sessions)
+
+ case :ssh.daemon(
+ port,
+ system_dir: key_dir,
+ pwdfun: &pwd_authenticate/4,
+ key_cb: Chessh.SSH.ServerKey,
+ # disconnectfun:
+ id_string: :random,
+ subsystems: [],
+ parallel_login: true,
+ max_sessions: max_sessions
+ ) do
+ {:ok, pid} ->
+ Process.link(pid)
+ {:noreply, %{state | pid: pid}, :hibernate}
+
+ {:error, err} ->
+ raise inspect(err)
+ end
+
+ {:noreply, state}
+ end
+
+ def handle_info(_, state), do: {:noreply, state}
+end
diff --git a/lib/chessh/ssh/server.ex b/lib/chessh/ssh/server.ex
deleted file mode 100644
index e69de29..0000000
--- a/lib/chessh/ssh/server.ex
+++ /dev/null
diff --git a/lib/chessh/ssh/server_key.ex b/lib/chessh/ssh/server_key.ex
new file mode 100644
index 0000000..72a4fbb
--- /dev/null
+++ b/lib/chessh/ssh/server_key.ex
@@ -0,0 +1,12 @@
+defmodule Chessh.SSH.ServerKey do
+ alias Chessh.Auth.KeyAuthenticator
+ @behaviour :ssh_server_key_api
+
+ def is_auth_key(key, username, _daemon_options) do
+ KeyAuthenticator.authenticate(username, key)
+ end
+
+ def host_key(algorithm, daemon_options) do
+ :ssh_file.host_key(algorithm, daemon_options)
+ end
+end