summaryrefslogtreecommitdiff
path: root/lib/chessh/ssh
diff options
context:
space:
mode:
authorSimponic <loganhunt@simponic.xyz>2022-12-31 16:32:56 -0700
committerSimponic <loganhunt@simponic.xyz>2022-12-31 16:33:12 -0700
commit52a3ed7c5700fa398efb8a4aff9d586a850e3d58 (patch)
tree0ac4b127c99d399265159182edf4dc63bd9ba675 /lib/chessh/ssh
parent58d0b1a89c461467c9ea6229f9a6b3d5ed573da5 (diff)
downloadchessh-52a3ed7c5700fa398efb8a4aff9d586a850e3d58.tar.gz
chessh-52a3ed7c5700fa398efb8a4aff9d586a850e3d58.zip
Better logging, close previous sessions once session threshold has been reached
Diffstat (limited to 'lib/chessh/ssh')
-rw-r--r--lib/chessh/ssh/client.ex23
-rw-r--r--lib/chessh/ssh/daemon.ex32
-rw-r--r--lib/chessh/ssh/server_key.ex4
-rw-r--r--lib/chessh/ssh/tui.ex154
4 files changed, 141 insertions, 72 deletions
diff --git a/lib/chessh/ssh/client.ex b/lib/chessh/ssh/client.ex
index 35893f1..eba188f 100644
--- a/lib/chessh/ssh/client.ex
+++ b/lib/chessh/ssh/client.ex
@@ -1,16 +1,27 @@
defmodule Chessh.SSH.Client do
- alias Chessh.SSH.Client
+ alias IO.ANSI
require Logger
use GenServer
- # TODO: tui_state_stack is like [:menu, :player_settings, :change_password] or [:menu, {:game, game_id}, {:game_chat, game_id}]
+ @default_message [
+ ANSI.clear(),
+ ANSI.reset(),
+ ANSI.home(),
+ ["Hello, world"]
+ ]
- defstruct [:tui_pid, :width, :height, :player_id, :tui_state_stack]
+ defmodule State do
+ defstruct tui_pid: nil,
+ width: nil,
+ height: nil,
+ player_session: nil,
+ state_statck: []
+ end
@impl true
- def init([tui_pid, width, height] = args) do
- Logger.debug("#{inspect(args)}")
- {:ok, %Client{tui_pid: tui_pid, width: width, height: height}}
+ def init([%State{tui_pid: tui_pid} = state]) do
+ send(tui_pid, {:send_data, @default_message})
+ {:ok, state}
end
end
diff --git a/lib/chessh/ssh/daemon.ex b/lib/chessh/ssh/daemon.ex
index 24ad259..1748e9e 100644
--- a/lib/chessh/ssh/daemon.ex
+++ b/lib/chessh/ssh/daemon.ex
@@ -1,6 +1,8 @@
defmodule Chessh.SSH.Daemon do
alias Chessh.{Repo, PlayerSession, Utils}
alias Chessh.Auth.PasswordAuthenticator
+ alias Chessh.SSH.{ServerKey, Tui}
+
use GenServer
import Ecto.Query
@@ -30,24 +32,30 @@ defmodule Chessh.SSH.Daemon do
String.Chars.to_string(password)
) do
false ->
+ Logger.debug(
+ "#{username} on bucket #{rateId} got their password wrong, or they don't exist! Point at them and laugh!!!!"
+ )
+
case Hammer.check_rate_inc(rateId, jail_timeout_ms, jail_attempt_threshold, 1) do
{:allow, _count} ->
+ Logger.debug("Bucket #{rateId} can continue to brute force though")
false
{:deny, _limit} ->
+ Logger.debug("Bucket #{rateId} ran out of password attempts")
:disconnect
end
x ->
- if PlayerSession.player_within_concurrent_sessions_and_satisfies(username, fn _player ->
- x
- end),
- do: true,
- else: :disconnect
+ PlayerSession.update_sessions_and_player_satisfies(username, fn _player ->
+ x
+ end)
+
+ x
end
end
- def pwd_authenticate(username, password, inet, _address),
+ def pwd_authenticate(username, password, inet, _state),
do: pwd_authenticate(username, password, inet)
def handle_cast(:start, state) do
@@ -57,19 +65,19 @@ defmodule Chessh.SSH.Daemon do
case :ssh.daemon(
port,
- # shell: fn _username, _peer -> Process.sleep(5000) end,
system_dir: key_dir,
pwdfun: &pwd_authenticate/4,
- key_cb: Chessh.SSH.ServerKey,
- ssh_cli: {Chessh.SSH.Tui, []},
- # connectfun: &on_connect/3,
+ key_cb: ServerKey,
+ ssh_cli: {Tui, [%Tui.State{}]},
disconnectfun: &on_disconnect/1,
id_string: :random,
- subsystems: [],
parallel_login: true,
- max_sessions: max_sessions
+ max_sessions: max_sessions,
+ subsystems: []
) do
{:ok, pid} ->
+ Logger.info("SSH server started on port #{port}, on #{inspect(pid)}")
+
Process.link(pid)
{:noreply, %{state | pid: pid}, :hibernate}
diff --git a/lib/chessh/ssh/server_key.ex b/lib/chessh/ssh/server_key.ex
index 5252624..eae9577 100644
--- a/lib/chessh/ssh/server_key.ex
+++ b/lib/chessh/ssh/server_key.ex
@@ -5,9 +5,9 @@ defmodule Chessh.SSH.ServerKey do
@behaviour :ssh_server_key_api
def is_auth_key(key, username, _daemon_options) do
- PlayerSession.player_within_concurrent_sessions_and_satisfies(
+ PlayerSession.update_sessions_and_player_satisfies(
username,
- &KeyAuthenticator.authenticate(&1, key)
+ fn player -> KeyAuthenticator.authenticate(player, key) end
)
end
diff --git a/lib/chessh/ssh/tui.ex b/lib/chessh/ssh/tui.ex
index 78360a9..c0fc910 100644
--- a/lib/chessh/ssh/tui.ex
+++ b/lib/chessh/ssh/tui.ex
@@ -1,45 +1,90 @@
defmodule Chessh.SSH.Tui do
- require Logger
-
- @behaviour :ssh_server_channel
+ alias Chessh.{Repo, PlayerSession, Utils, Player}
+ alias Chessh.SSH.Client
- def init(opts) do
- Logger.debug("#{inspect(opts)}")
+ alias IO.ANSI
- {:ok,
- %{
- channel: nil,
- cm: nil,
- pty: %{term: nil, width: nil, height: nil, pixwidth: nil, pixheight: nil, modes: nil},
- shell: false,
- client_pid: nil
- }}
- end
-
- @spec handle_msg(any, any) ::
- :ok
- | {:ok, atom | %{:channel => any, :cm => any, optional(any) => any}}
- | {:stop, any, %{:channel => any, :client_pid => any, optional(any) => any}}
- def handle_msg({:ssh_channel_up, channel_id, connection_handler}, state) do
- Logger.debug(
- "SSH CHANNEL UP #{inspect(connection_handler)} #{inspect(:ssh.connection_info(connection_handler))}"
- )
+ require Logger
- {:ok, %{state | channel: channel_id, cm: connection_handler}}
+ @behaviour :ssh_server_channel
+ @session_closed_message [
+ ANSI.clear(),
+ ["This session has been closed"]
+ ]
+
+ defmodule State do
+ defstruct channel_id: nil,
+ width: nil,
+ height: nil,
+ client_pid: nil,
+ connection_ref: nil,
+ player_session: nil
+ end
+
+ def init([%State{} = init_state]) do
+ :syn.add_node_to_scopes([:player_sessions])
+ {:ok, init_state}
+ end
+
+ def handle_msg({:ssh_channel_up, channel_id, connection_ref}, state) do
+ Logger.debug("SSH channel up #{inspect(:ssh.connection_info(connection_ref))}")
+
+ connected_player =
+ :ssh.connection_info(connection_ref)
+ |> Keyword.fetch!(:user)
+ |> String.Chars.to_string()
+
+ case Repo.get_by(Player, username: connected_player) do
+ nil ->
+ Logger.error("Killing channel #{channel_id} - auth'd user does not exist")
+ {:stop, channel_id, state}
+
+ player ->
+ case Repo.get_by(PlayerSession,
+ node_id: System.fetch_env!("NODE_ID"),
+ process: Utils.pid_to_str(connection_ref),
+ player_id: player.id
+ ) do
+ nil ->
+ Logger.error("Killing channel #{channel_id} - session does not exist")
+ {:stop, channel_id, state}
+
+ session ->
+ Logger.debug("Subscribing to session #{session.id}")
+ :syn.join(:player_sessions, {:session, session.id}, self())
+
+ {:ok,
+ %{
+ state
+ | channel_id: channel_id,
+ connection_ref: connection_ref,
+ player_session: session
+ }}
+ end
+ end
end
def handle_msg({:EXIT, client_pid, _reason}, %{client_pid: client_pid} = state) do
{:stop, state.channel, state}
end
- ### commands we expect from the client ###
- def handle_msg({:send_data, data}, state) do
- Logger.debug("DATA SENT #{inspect(data)}")
- :ssh_connection.send(state.cm, state.channel, data)
+ def handle_msg(
+ {:send_data, data},
+ %{connection_ref: connection_ref, channel_id: channel_id} = state
+ ) do
+ Logger.debug("Data was sent to TUI process #{inspect(data)}")
+ :ssh_connection.send(connection_ref, channel_id, data)
{:ok, state}
end
- ### catch all for what we haven't seen ###
+ def handle_msg(
+ :session_closed,
+ %{connection_ref: connection_ref, channel_id: channel_id} = state
+ ) do
+ :ssh_connection.send(connection_ref, channel_id, @session_closed_message)
+ {:stop, channel_id, state}
+ end
+
def handle_msg(msg, term) do
Logger.debug("Unknown msg #{inspect(msg)}, #{inspect(term)}")
end
@@ -49,28 +94,23 @@ defmodule Chessh.SSH.Tui do
state
) do
Logger.debug("DATA #{inspect(data)}")
- send(state.client_pid, {:data, data})
+ # send(state.client_pid, {:data, data})
{:ok, state}
end
def handle_ssh_msg(
{:ssh_cm, connection_handler,
- {:pty, channel_id, want_reply?, {term, width, height, pixwidth, pixheight, modes} = _pty}},
+ {:pty, channel_id, want_reply?, {_term, width, height, _pixwidth, _pixheight, _opts}}},
state
) do
+ Logger.debug("#{inspect(state.player_session)} has requested a PTY")
:ssh_connection.reply_request(connection_handler, want_reply?, :success, channel_id)
{:ok,
%{
state
- | pty: %{
- term: term,
- width: width,
- height: height,
- pixwidth: pixwidth,
- pixheight: pixheight,
- modes: modes
- }
+ | width: width,
+ height: height
}}
end
@@ -85,35 +125,37 @@ defmodule Chessh.SSH.Tui do
def handle_ssh_msg(
{:ssh_cm, _connection_handler,
- {:window_change, _channel_id, width, height, pixwidth, pixheight}},
+ {:window_change, _channel_id, width, height, _pixwidth, _pixheight}},
state
) do
Logger.debug("WINDOW CHANGE")
- # SSHnakes.Client.resize(state.client_pid, width, height)
-
+ # Chessh.SSH.Client.resize(state.client_pid, width, height)
{:ok,
%{
state
- | pty: %{
- state.pty
- | width: width,
- height: height,
- pixwidth: pixwidth,
- pixheight: pixheight
- }
+ | width: width,
+ height: height
}}
end
def handle_ssh_msg(
{:ssh_cm, connection_handler, {:shell, channel_id, want_reply?}},
- state
+ %{width: width, height: height, player_session: player_session} = state
) do
+ Logger.debug("Session #{player_session.id} requested shell")
:ssh_connection.reply_request(connection_handler, want_reply?, :success, channel_id)
{:ok, client_pid} =
- GenServer.start_link(Chessh.SSH.Client, [self(), state.pty.width, state.pty.height])
+ GenServer.start_link(Client, [
+ %Client.State{
+ tui_pid: self(),
+ width: width,
+ player_session: player_session,
+ height: height
+ }
+ ])
- {:ok, %{state | client_pid: client_pid, shell: true}}
+ {:ok, %{state | client_pid: client_pid}}
end
def handle_ssh_msg(
@@ -157,6 +199,14 @@ defmodule Chessh.SSH.Tui do
{:stop, channel_id, state}
end
+ def handle_ssh_msg(
+ msg,
+ %{channel_id: channel_id} = state
+ ) do
+ Logger.debug("UNKOWN MESSAGE #{inspect(msg)}")
+ {:stop, channel_id, state}
+ end
+
def terminate(_reason, _state) do
:ok
end