# 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](https://hex.pm/docs/publish), the package can be installed
by adding `esshd` to your list of dependencies in `mix.exs`:
```elixir
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`:
```elixir
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:
```elixir
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:
```sh
$ [ -d priv ] || mkdir priv
$ chmod 700 priv
$ cd priv
$ ssh-keygen -N "" -b 256 -t ecdsa -f ssh_host_ecdsa_key
$ ssh-keygen -N "" -b 1024 -t dsa -f ssh_host_dsa_key
$ ssh-keygen -N "" -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:
```elixir
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:
```sh
$ [ -d priv ] || mkdir priv
$ chmod 700 priv
$ cd priv
$ ssh-keygen -N "" -b 256 -t ecdsa -f ssh_host_ecdsa_key
$ ssh-keygen -N "" -b 1024 -t dsa -f ssh_host_dsa_key
$ ssh-keygen -N "" -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`](http://erlang.org/doc/man/ssh.html#default_algorithms-0)
function. For information about configuring this complex setting,
please read the similarily named configuration option within
the function [`daemon/2`](http://erlang.org/doc/man/ssh.html#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](mailto:joe@benden.us).
Licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).