# 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.