A very simple way to add SSH server capabilities to an Elixir application.
- 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.
At runtime :esshd
only requires the Erlang runtime and OTP.
During the development of :esshd
, some third-party software is used
and required. This third-party software is ONLY used as supporting
material, for quality assurance purposes. No output or files from the
third-party software has been included in any final release software.
The package can be installed by adding :esshd
to your list of dependencies in
mix.exs
:
def deps do
[
{:esshd, "~> 0.3.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
This Elixir application offers a number of use-cases; and we recommend selecting the solution that best matches your project's desired goal.
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: :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 -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.
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: :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 -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.
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. Note that this currently only applies to password authentication, and is only applied after the connection is accepted.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 anyIO
to-and-from standard input and output streams. An example of such a shell is included, as it is a mildly complex topic.
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 theSshd.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 theSshd.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 theSshd.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 underlyingdefault_algorithms/0
function. For information about configuring this complex setting, please read the similarily named configuration option within the functiondaemon/2
.priv_dir :: string()
: specifies the location to your own application'spriv
directory, or any other directory. This directory is utilized for the SSH host keys and is utilized by theSshd.PublicKeyAuthenticator.AuthorizedKeys
module for both user keys and theauthorized_keys
file.public_key_authenticator :: string(Sshd.PublicKeyAuthenticator.Default)
: A string containing the fully qualified module that implements theSshd.PublicKeyAuthenticator
behavior.
Copyright (C) 2017-2021 Joseph Benden.
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.