README.md

# Elixir SSHd

[![Module Version](https://img.shields.io/hexpm/v/esshd.svg)](https://hex.pm/packages/esshd)
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/esshd/)
[![Total Download](https://img.shields.io/hexpm/dt/esshd.svg)](https://hex.pm/packages/esshd)
[![License](https://img.shields.io/hexpm/l/esshd.svg)](https://github.com/jbenden/esshd/blob/master/LICENSE)
[![Last Updated](https://img.shields.io/github/last-commit/jbenden/esshd.svg)](https://github.com/jbenden/esshd/commits/master)

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

The package can be installed by adding `:esshd` to your list of dependencies in
`mix.exs`:

```elixir
def deps do
  [
    {:esshd, "~> 0.2.1"}
  ]
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: :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: :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 :: :erlang | :elixir | {m, f, a}`: The handler that should
  respond to incoming SSH connections. It may be one of the simple
  atoms, for simplicity; or, it may be a tuple containing a module,
  function, and arguments. Note that the arguments are not presently
  used. The module, function, and arguments option must implement
  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-2021 [Joseph Benden](mailto:joe@benden.us).

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.