Simple SSH server for Elixir and Erlang applications.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.vscode
config
lib
test
.editorconfig
.gitignore
CHANGELOG.md
LICENSE
README.md
coveralls.json
mix.exs

README.md

Elixir SSHd

A very simple way to add SSH server capabilities to an Elixir application.

Features

  • Simple way of adding SSH version 2.0 server capabilities to one's application, in a secured manner.
  • Acceptable for production systems; due to the secured nature of SSH version 2.0, and the ability of fine-grain access control and authentication methods available.
  • Quick way to drop in to an Elixir or Erlang REPL.
  • Easiest way to create remote accessible custom shell-like programs.

Installation

If available in Hex, the package can be installed by adding esshd to your list of dependencies in mix.exs:

def deps do
  [{:esshd, "~> 0.1.0"}]
end

After adding esshd as a dependency, ensure it is started before your own application in mix.exs:

def application do
  [extra_applications: [:esshd]]
end

Usage

This Elixir application offers a number of use-cases; and we recommend selecting the solution that best matches your project's desired goal.

Drop-in Secure Remote Elixir REPL

Once installed, add the following configuration to your project:

app_dir = Application.app_dir(:myapp)
priv_dir = Path.join([app_dir, "priv"])

config :esshd,
  enabled: true,
  priv_dir: priv_dir,
  handler: "Sshd.ShellHandler.Elixir",
  port: 10_022,
  public_key_authenticator: "Sshd.PublicKeyAuthenticator.AuthorizedKeys"

Once the above configuration is added, your application will require OpenSSH compatible SSH host keys and an authorized_keys file stored inside of your application's priv directory.

To generate the needed OpenSSH host keys, change in to your application's priv directory and execute an appropriate command. An example of such command sequences are as follows:

$ [ -d priv ] || mkdir priv
$ chmod 700 priv
$ cd priv
$ ssh-keygen -b 256 -t ecdsa -f ssh_host_ecdsa_key
$ echo "===> Please press ENTER/RETURN for *ALL* passphrase entries!!!!!"
$ ssh-keygen -b 256  -t ecdsa -f ssh_host_ecdsa_key
$ ssh-keygen -b 1024 -t dsa -f ssh_host_dsa_key
$ ssh-keygen -b 2048 -t rsa -f ssh_host_rsa_key
$ echo 127.0.0.1,127.0.0.1 `cat ssh_host_ecdsa_key.pub` > known_hosts
$ chmod 644 known_hosts

Finally, add all OpenSSH public keys to be accepted in to the authorized_keys file within your application's priv directory.

Drop-in Secure Remote Erlang REPL

Once installed, add the following configuration to your project:

app_dir = Application.app_dir(:myapp)
priv_dir = Path.join([app_dir, "priv"])

config :esshd,
  enabled: true,
  priv_dir: priv_dir,
  handler: "Sshd.ShellHandler.Erlang",
  port: 10_022,
  public_key_authenticator: "Sshd.PublicKeyAuthenticator.AuthorizedKeys"

Once the above configuration is added, your application will require OpenSSH compatible SSH host keys and an authorized_keys file stored inside of your application's priv directory.

To generate the needed OpenSSH host keys, change in to your application's priv directory and execute an appropriate command. An example of such command sequences are as follows:

$ [ -d priv ] || mkdir priv
$ chmod 700 priv
$ cd priv
$ ssh-keygen -b 256 -t ecdsa -f ssh_host_ecdsa_key
$ echo "===> Please press ENTER/RETURN for *ALL* passphrase entries!!!!!"
$ ssh-keygen -b 256  -t ecdsa -f ssh_host_ecdsa_key
$ ssh-keygen -b 1024 -t dsa -f ssh_host_dsa_key
$ ssh-keygen -b 2048 -t rsa -f ssh_host_rsa_key
$ echo 127.0.0.1,127.0.0.1 `cat ssh_host_ecdsa_key.pub` > known_hosts
$ chmod 644 known_hosts

Finally, add all OpenSSH public keys to be accepted in to the authorized_keys file within your application's priv directory.

Custom Access Control and Authorization

esshd was designed around the concept of easily changing the methods employed in each of access control and authorization by changing the utilized "handler" of each component - by way of Elixir Behaviors.

The following behaviors exist and may be implemented and easily configured for use, at application boot time.

  • Sshd.AccessList: offers control over the connecting remote IP address and ports, by simple return of a boolean value that states if the remote connection is accepted. While it may seem simplistic, at first, a behavior may be as complex as time- based, quantity of already connected peers, etc.
  • Sshd.PasswordAuthenticator: offers a means for username and password verification. The behavior offers NO throttling or any such complexity and MUST be securely interfaced to a trusted library that performs correct password handling, even under the case of an invalid password - to prevent detection of actual valid user accounts.
  • Sshd.PublicKeyAuthenticator: offers a means for username and public key verification. While many authentication libraries may not offer this ability - when tied to also tasked with password authentication - one could still tie to their user back-end storage to accommodate this behavior. Also, much like password authentication, correct handling it imperative; see above.
  • Sshd.ShellHandler: offers a means for custom, do-it-yourself remote shells, whereby you are in full control, and may implement any IO to-and-from standard input and output streams. An example of such a shell is included, as it is a mildly complex topic.

Configuration Options

The following configuration options are available, with the default setting shown:

  • access_list :: string(Sshd.AccessList.Default): A string containing the fully qualified module that implements the Sshd.AccessList behavior.
  • enabled :: boolean(true): Determines if the SSH server is enabled or not. Useful in complex applications to disable all incoming SSH connection functionality, without a full recompile and deploy.
  • handler :: string(Sshd.ShellHandler.Default): A string containing the fully qualified module that implements the Sshd.ShellHandler behavior.
  • idle_time :: integer(86_400_000 * 3): The amount of time, in milliseconds, an idle connection may remain, before being automatically disconnected. This does not effect actively utilized connections.
  • max_sessions :: integer(50): The maximum number of simultaneous users connected at one time.
  • negotiation_timeout :: integer(11_000): The amount of time, in milliseconds, that a connection has to begin correct phases of entering in to a valid SSH connection, before being flat out disconnected. This setting helps to keep server utilization down due to port scans and other similar problems.
  • parallel_login :: boolean(false): Determines if simultaneous connections are permitted in the authentication phase. This does not effect if multiple users may be connected simultaneously.
  • password_authenticator :: string(Sshd.PasswordAuthenticator.Default): A string containing the fully qualified module that implements the Sshd.PasswordAuthenticator behavior.
  • port :: integer(10_022): The TCP port number of the SSH server process.
  • preferred_algorithms :: tuple: The acceptable hashes, ciphers, and key exchange mechanizisms of the SSH server. The default settings are the same as the underlying default_algorithms/0 function. For information about configuring this complex setting, please read the similarily named configuration option within the function daemon/2.
  • priv_dir :: string(): specifies the location to your own application's priv directory, or any other directory. This directory is utilized for the SSH host keys and is utilized by the Sshd.PublicKeyAuthenticator.AuthorizedKeys module for both user keys and the authorized_keys file.
  • public_key_authenticator :: string(Sshd.PublicKeyAuthenticator.Default): A string containing the fully qualified module that implements the Sshd.PublicKeyAuthenticator behavior.

License

Copyright (C) 2017 Joseph Benden.

Licensed under the Apache 2.0 License.