beach

Types

ssh client connection.

pub type Connection =
  @internal Connection

ssh client connection information.

pub type ConnectionInfo {
  ConnectionInfo(username: String, ip_address: String, port: Int)
}

Constructors

  • ConnectionInfo(username: String, ip_address: String, port: Int)
pub type StartError {
  AddressInUse
  SshApplicationNotStarted
  HostKeyNotFound
  SshDaemonFault(String)
}

Constructors

  • AddressInUse

    Port in use by another application

  • SshApplicationNotStarted

    Erlang ssh application is not running

  • HostKeyNotFound

    ssh host key files not found

  • SshDaemonFault(String)

    unexpected other term() provided by ssh:daemon (which should be converted to strict value, open issue/pr if found)

Values

pub fn auth_anonymous() -> @internal Auth

Allow anyone to connect without requiring a password or public key

pub fn auth_password(
  auth: fn(String, String) -> Bool,
) -> @internal Auth

Provide a password challenge for users to complete

Example

fn user_login(username: String, password: String) -> Bool {
  case username, password {
    "Joe", "Hello!" -> True
    _, _ -> False
  }
}

fn main() {
  let auth = auth_password(user_login)
  config(auth:, ..)
}
pub fn auth_public_key(
  auth: fn(String, @internal PublicKey) -> Bool,
) -> @internal Auth

Provide public key challenge. public key must be present in authorized_keys file held in the user_directory folder.

Requires a callback function which containers the username on successful authentication. Can be used for further validation, logging, etc.

Note: It is safe to have this function always return True e.g. fn(_) { True }

Example

fn user_login(username: String, public_key: PublicKey) -> Bool {
   let challenge =
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOE7rwqgX3K2Cj8wY/gAOiEQ0T9lEINdNwFq9HEVXB71 username@shore"
     |> public_key
   challenge == public_key && username == "Joe"
}

fn main() {
  let auth = auth_public_key(user_login)
  config(auth:, ..)
}
pub fn auth_public_key_or_password(
  password_auth password_auth: fn(String, String) -> Bool,
  key_auth key_auth: fn(String, @internal PublicKey) -> Bool,
) -> @internal Auth

Provide public key challenge, falling back to password challenge if no matching public key.

Alternatively, uses can set their preferred auth method via the -o PreferredAuthentications=password,publickey.

See individual examples for auth_public_key and auth_password for implementation.

pub fn config(
  port port: Int,
  host_key_directory host_key_directory: String,
  auth auth: @internal Auth,
  on_connect on_connect: fn(
    @internal Connection,
    process.Subject(@internal Event(msg)),
  ) -> Nil,
  on_disconnect on_disconnect: fn(
    @internal Connection,
    process.Subject(@internal Event(msg)),
  ) -> Nil,
  max_sessions max_sessions: option.Option(Int),
) -> @internal Config(msg)

Configuration for a beach ssh server

port: defines the port to expose the ssh server on

host_key_directory: the directory which contains an ssh public and private host key

auth: the authentication challenge type to use (anonymous, password, keys) can be created from cli with ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N ''

on_connect: a callback to run on a new connection, exposes the ssh username as well as the newly started shore subject (this can be sent messages via actor.send)

on_disconnect: a callback to run when a connection terminates for any reason, exposes the ssh username as well as the specific shore subject.

max_sessions: the maximum number of ssh sessions that can be open at once, this includes login attempts.

Example

beach.config(
  port: 2222,
  host_key_directory: ".",
  auth: beach.auth_anonymous(),
  on_connect: fn(_connection, _shore) { Nil },
  on_disconnect: fn(_connection, _shore) { Nil },
  max_sessions: Some(1000),
)
pub fn connection_info(
  info: @internal Connection,
) -> ConnectionInfo

Provides information from an ssh connection.

pub fn public_key(public_key: String) -> @internal PublicKey

Converts an OpenSSH public key string into a PublicKey type for comparison

Example

public_key("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOE7rwqgX3K2Cj8wY/gAOiEQ0T9lEINdNwFq9HEVXB71 username@shore")
pub fn start(
  spec: @internal Spec(model, msg),
  config: @internal Config(msg),
) -> Result(process.Pid, StartError)

Starts an ssh server serving shore application to connecting clients.

Example

pub fn main() {
  let spec =
    shore.spec(
      init:,
      update:,
      view:,
      exit: process.new_subject(),
      keybinds: shore.default_keybinds(),
      redraw: shore.on_timer(16),
    )
  let config =
    beach.config(
      port: 2222,
      host_key_directory: ".",
      auth: beach.auth_anonymous(),
      on_connect: fn(_connection, _shore) { Nil },
      on_disconnect: fn(_connection, _shore) { Nil },
      max_sessions: Some(1000),
    )
  let assert Ok(_) = beach.start(spec, config)
  process.sleep_forever()
}
Search Document