Add new Hass.io add-on: SSH - Secure Shell

This commit is contained in:
Franck Nijhof 2017-08-16 22:24:14 +02:00
parent 9e91c043fc
commit 92ae28dfd8
No known key found for this signature in database
GPG key ID: D62583BA8AB11CA3
16 changed files with 1169 additions and 0 deletions

View file

@ -39,6 +39,21 @@ control your Home Assistant through via Apple devices (including Siri).
[:books: Homebridge add-on documentation](homebridge)
### ✓ [SSH - Secure Shell](ssh)
![Latest Version][ssh-version-shield]
![Supports armhf Architecture][ssh-armhf-shield]
![Supports aarch64 Architecture][ssh-aarch64-shield]
![Supports amd64 Architecture][ssh-amd64-shield]
![Supports i386 Architecture][ssh-i386-shield]
![Docker Pulls][ssh-pulls-shield]
Log in to your Hass.io Home Assistant instance using SSH.
This is an enhanced version of the provided SSH add-on by Home Assistant and
focusses on security, usability and flexibility.
[:books: SSH add-on documentation](ssh)
## Changelog
This add-on repository itself does not keep a change log, the individual
@ -103,3 +118,9 @@ SOFTWARE.
[project-stage-shield]: https://img.shields.io/badge/Project%20Stage-Development-yellowgreen.svg
[semver]: http://semver.org/spec/v2.0.0.html
[third-party-addons]: https://home-assistant.io/hassio/installing_third_party_addons/
[ssh-aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg
[ssh-amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg
[ssh-armhf-shield]: https://img.shields.io/badge/armhf-yes-green.svg
[ssh-i386-shield]: https://img.shields.io/badge/i386-yes-green.svg
[ssh-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/ssh-armhf.svg
[ssh-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/ssh-armhf.svg

0
bin/create_all.sh Normal file → Executable file
View file

0
bin/create_hassio_addon.sh Normal file → Executable file
View file

14
ssh/CHANGELOG.md Normal file
View file

@ -0,0 +1,14 @@
# Hass.io Add-on Changelog: SSH - Secure Shell
All notable changes to this add-on will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## Unreleased
-
## 1.0.0 - 2017-08-16
### Added
- First version of the SSH Add-on
- This CHANGELOG file

29
ssh/Dockerfile Executable file
View file

@ -0,0 +1,29 @@
FROM %%BASE_IMAGE%%
MAINTAINER Franck Nijhof <frenck@geekchimp.com>
# Add env
ENV LANG="C.UTF-8" \
TERM="xterm-256color"
# Copy data
COPY files/run.sh /
COPY files/hassio /usr/bin/
COPY files/sshd_config /etc/ssh/
COPY files/motd /etc/
COPY files/.zshrc /root/
COPY files/.tmux.conf /root/
COPY files/wheel /etc/sudoers.d/
# Setup base
RUN apk add --no-cache jq openssh sudo zsh openssl libxml2-utils ncurses \
curl wget net-tools bind-tools rsync git nmap wget awake \
nano vim mosquitto-clients mariadb-client tmux && \
git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh && \
chmod a+x /run.sh /usr/bin/hassio && \
sed -i -e "s/bin\/ash/bin\/zsh/" /etc/passwd && \
touch /etc/ssh/authorized_keys && \
chmod 0644 /etc/ssh/authorized_keys && \
touch /var/log/btmp && \
chmod 0600 /var/log/btmp
CMD [ "/run.sh" ]

261
ssh/README.md Normal file
View file

@ -0,0 +1,261 @@
# Hass.io Add-on: SSH - Secure Shell
![Project Stage][project-stage-shield]
![Maintenance][maintenance-shield]
![Awesome][awesome-shield]
[![License][license-shield]](LICENSE.md)
This add-on allows you to log in to your Hass.io Home Assistant instance using
SSH. Giving you to access your Hass.io folders and also includes a
command-line tool to do things like restart, update, and check your instance.
This is an enhanced version of the provided
[SSH add-on by Home Assistant][hass-ssh] and focusses on security,
usability and flexibility.
## Docker status
[![Docker Architecture][armhf-arch-shield]][armhf-dockerhub]
[![Docker Version][armhf-version-shield]][armhf-microbadger]
[![Docker Layers][armhf-layers-shield]][armhf-microbadger]
[![Docker Pulls][armhf-pulls-shield]][armhf-dockerhub]
[![Docker Architecture][aarch64-arch-shield]][aarch64-dockerhub]
[![Docker Version][aarch64-version-shield]][aarch64-microbadger]
[![Docker Layers][aarch64-layers-shield]][aarch64-microbadger]
[![Docker Pulls][aarch64-pulls-shield]][aarch64-dockerhub]
[![Docker Architecture][amd64-arch-shield]][amd64-dockerhub]
[![Docker Version][amd64-version-shield]][amd64-microbadger]
[![Docker Layers][amd64-layers-shield]][amd64-microbadger]
[![Docker Pulls][amd64-pulls-shield]][amd64-dockerhub]
[![Docker Architecture][i386-arch-shield]][i386-dockerhub]
[![Docker Version][i386-version-shield]][i386-microbadger]
[![Docker Layers][i386-layers-shield]][i386-microbadger]
[![Docker Pulls][i386-pulls-shield]][i386-dockerhub]
## Features
This add-on, of course, provides an SSH server, based on [OpenSSH][openssh].
Additionally, it comes out of the box with the following:
- A secure default configuration of SSH:
- Only allows login by the configured user, even if more users are created.
- Only uses known secure ciphers and algorithms.
- Limits login attempts to hold of brute-force attacks better.
- Many more security tweaks, *this addon passes all [ssh-audit] checks
without warnings!*
![Result of SSH-Audit](images/ssh-audit.png)
- Port number is configurable (You could use `1982` instead of port `22`).
- SFTP support is disabled by default but is user configurable.
- Username is configurable, so `root` is no longer mandatory.
- Debug mode for allowing you to triage issues easier.
- Compatible if Hass.io was installed via the generic Linux installer.
- Have Alpine packages installed on start. This will allow you to install your
favorite tools, which will be available every single time you log in.
- Execute custom commands on start automatically, so you can customize the shell
to your likings.
- [ZSH][zsh] as its default shell. Easier to use for the beginner, more advanced
for the more experienced user. It even comes preloaded with
["Oh My ZSH"][ohmyzsh], with some plugins enabled as well.
- Contains a sensible set of tools right out of the box: curl, Wget, RSync, GIT,
Nmap, Mosquitto client, MariaDB/MySQL client, Awake (“wake on lan”), Nano, Vim,
tmux, and a bunch commonly used networking tools.
## Installation
The installation of this add-on is pretty straight forward and not different in
comparison to installing any other Hass.io add-on.
1. If you installed the "SSH server" add-on from the built-in add-on, then
that one first.
2. [Add our Hass.io add-ons repository][repository] to your Hass.io instance
3. Install the "SSH - Secure Shell" add-on from our repository
3. Start the "SSH - Secure Shell" add-on
4. Check the logs of the "SSH - Secure Shell" add-on to see if everything
went well.
Please read the rest of this document further instructions.
## Configuration
_Please remember to restart the add-on when the configuration changes._
Example add-on configuration:
```json
{
"debug": true,
"port": 22,
"username": "hass",
"password": "",
"authorized_keys": [
"ssh-rsa AASDJKJKJFWJFAFLCNALCMLAK234234....."
],
"sftp": false,
"packages": [
"python",
"python-dev",
"py-pip",
"build-base"
],
"init_commands": [
"pip install virtualenv",
"pip install yamllint"
]
}
```
_*Note*: This is just an example, don't copy and past it! Create your own!_
**Option: `debug`**
When set to `true` the addon will output more information in the logs of the
add-on. The add-on will also start the SSH daemon in debug mode. While SSH is
running in daemon mode, it will be only able to accept one single connection. As
soon as this session is ended, the addon will stop running.
This might be useful when you're dealing with an unknown issue. It is
recommended leaving to option set to `false`, unless you are troubleshooting.
**Option: `port`**
The default port for SSH is `22`, some security guides actually recommend to
change the port to something else. Sometimes you'd just like to have it on
another port. Remember, if you change to port, be sure it is not in use
already!
**Option: `username`**
This option allows you to change to username the use when you log in via SSH.
It is only used for the authentication, you will be the `root` user after
you have authenticated. Using `root` as the username is possible, but not
recommended.
_*Note*: Due to limitations, you will need to set this option to `root` in
order to be able to enable the SFTP capabilities._
**Option: `password`**
Sets the password to log in with. Leaving it empty would disable the posibility
to authenticate with a password. We would highly recommend not to use this
option from a security point of view.
**Option: `authorized_keys`**
Add one or more public keys to your SSH server to use with authentication.
This is the recommended over setting a password.
Please take a look at the awesome [documentation created by GitHub][github-ssh]
about using public/private keypairs and how to create them.
**Option: `sftp`**
When set to `true` the addon will enable SFTP support on the SSH daemon.
Please only enable it when you plan to use it.
_*Note*: Due to limitations, you will need to set the username to `root` in
order to be able to enable the SFTP capabilities._
**Option: `packages`**
Allows you to specify additional [Alpine packages][alpine-packages] to be
installed in your shell environment (e.g. Python, Joe, Irssi).
_*Note*: Adding many packages will result in a longer start-up
time for the add-on._
**Option: `init_commands`**
Customize your shell environment even more with the `init_commands` option.
Add one or more shell commands to the list, and they will be executed every
single time this add-on starts.
## Known issues and limitations
- When SFTP is enabled, the username MUST be set to `root`.
## Support
Got questions? Got some unexpected behavior caused by this plugin?
Please [open an issue on our GitHub repository][issues] and we'll do our best
to help you out.
## Credits
A big shout out to the following people, without them this add-on wasn't possible:
- The team & community of [Home Assistant][home-assistant] for developing such an
excellent home automation toolkit
Thank you all!
## More Hass.io add-ons
Do you like this add-on? Want some more functionality to your Hass.io Home
Assistant instance?
We've created multiple add-ons for Hass.io. For a full list, check out
our [GitHub Repository][repository].
## License
MIT License
Copyright (c) 2017 Franck Nijhof
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
[aarch64-arch-shield]: https://img.shields.io/badge/architecture-aarch64-blue.svg
[aarch64-dockerhub]: https://hub.docker.com/r/hassioaddons/ssh-aarch64
[aarch64-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/ssh-aarch64.svg
[aarch64-microbadger]: https://microbadger.com/images/hassioaddons/ssh-aarch64
[aarch64-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/ssh-aarch64.svg
[aarch64-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/ssh-aarch64.svg
[alpine-packages]: https://pkgs.alpinelinux.org/packages
[amd64-arch-shield]: https://img.shields.io/badge/architecture-amd64-blue.svg
[amd64-dockerhub]: https://hub.docker.com/r/hassioaddons/ssh-amd64
[amd64-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/ssh-amd64.svg
[amd64-microbadger]: https://microbadger.com/images/hassioaddons/ssh-amd64
[amd64-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/ssh-amd64.svg
[amd64-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/ssh-amd64.svg
[armhf-arch-shield]: https://img.shields.io/badge/architecture-armhf-blue.svg
[armhf-dockerhub]: https://hub.docker.com/r/hassioaddons/ssh-armhf
[armhf-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/ssh-armhf.svg
[armhf-microbadger]: https://microbadger.com/images/hassioaddons/ssh-armhf
[armhf-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/ssh-armhf.svg
[armhf-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/ssh-armhf.svg
[awesome-shield]: https://img.shields.io/badge/awesome%3F-yes-brightgreen.svg
[github-ssh]: https://help.github.com/articles/connecting-to-github-with-ssh/
[hass-ssh]: https://home-assistant.io/addons/ssh/
[home-assistant]: https://home-assistant.io
[i386-arch-shield]: https://img.shields.io/badge/architecture-i386-blue.svg
[i386-dockerhub]: https://hub.docker.com/r/hassioaddons/ssh-i386
[i386-layers-shield]: https://images.microbadger.com/badges/image/hassioaddons/ssh-i386.svg
[i386-microbadger]: https://microbadger.com/images/hassioaddons/ssh-i386
[i386-pulls-shield]: https://img.shields.io/docker/pulls/hassioaddons/ssh-i386.svg
[i386-version-shield]: https://images.microbadger.com/badges/version/hassioaddons/ssh-i386.svg
[issues]: https://github.com/hassio-addons/repository/issues
[license-shield]: https://img.shields.io/github/license/hassio-addons/repository.svg
[maintenance-shield]: https://img.shields.io/maintenance/yes/2017.svg
[ohmyzsh]: http://ohmyz.sh/
[openssh]: https://www.openssh.com/
[project-stage-shield]: https://img.shields.io/badge/Project%20Stage-Development-yellowgreen.svg
[repository]: https://github.com/hassio-addons/repository
[ssh-audit]: https://github.com/arthepsy/ssh-audit
[zsh]: https://en.wikipedia.org/wiki/Z_shell

39
ssh/config.json Executable file
View file

@ -0,0 +1,39 @@
{
"name": "SSH - Secure Shell",
"version": "1.0.0",
"slug": "ssh",
"description": "Allows SSH connections to your Home Assistant instance",
"url": "https://github.com/hassio-addons/repository/tree/master/ssh",
"startup": "services",
"boot": "auto",
"host_network": true,
"hassio_api": true,
"map": [
"config:rw",
"ssl:rw",
"addons:rw",
"share:rw",
"backup:rw"
],
"options": {
"debug": false,
"port": 22,
"username": "hass",
"password": "",
"authorized_keys": [],
"sftp": false,
"packages": [],
"init_commands": []
},
"schema": {
"debug": "bool",
"port": "int",
"username": "str",
"password": "str",
"authorized_keys": ["str"],
"sftp": "bool",
"packages": ["str"],
"init_commands": ["str"]
},
"image": "hassioaddons/ssh-{arch}"
}

23
ssh/files/.tmux.conf Normal file
View file

@ -0,0 +1,23 @@
set-option -g default-shell /bin/zsh
set-option -g default-terminal $TERM
set-option -g base-index 1
setw -g pane-base-index 1
setw -g window-status-format "#[fg=white]#[bg=blue] #I #[bg=blue]#[fg=white] #W "
setw -g window-status-current-format "#[bg=brightmagenta]#[fg=white] *#I #[fg=white,bold]#[bg=cyan] [#W] "
set -g pane-border-fg black
set -g pane-active-border-fg blue
set -g status-fg white
set -g status-bg blue
set -g status-left ''
set -g status-right '%a %m-%d %H:%M'
unbind C-b
set-option -g prefix C-a
bind-key C-a send-prefix
bind | split-window -h
bind - split-window -v
unbind '"'
unbind %
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

85
ssh/files/.zshrc Executable file
View file

@ -0,0 +1,85 @@
# If you come from bash you might have to change your $PATH.
# export PATH=$HOME/bin:/usr/local/bin:$PATH
# Path to your oh-my-zsh installation.
export ZSH=$HOME/.oh-my-zsh
# Set name of the theme to load. Optionally, if you set this to "random"
# it'll load a random theme each time that oh-my-zsh is loaded.
# See https://github.com/robbyrussell/oh-my-zsh/wiki/Themes
ZSH_THEME="robbyrussell"
# Uncomment the following line to use case-sensitive completion.
# CASE_SENSITIVE="true"
# Uncomment the following line to use hyphen-insensitive completion. Case
# sensitive completion must be off. _ and - will be interchangeable.
# HYPHEN_INSENSITIVE="true"
# Uncomment the following line to disable bi-weekly auto-update checks.
DISABLE_AUTO_UPDATE="true"
# Uncomment the following line to change how often to auto-update (in days).
# export UPDATE_ZSH_DAYS=13
# Uncomment the following line to disable colors in ls.
# DISABLE_LS_COLORS="true"
# Uncomment the following line to disable auto-setting terminal title.
# DISABLE_AUTO_TITLE="true"
# Uncomment the following line to enable command auto-correction.
# ENABLE_CORRECTION="true"
# Uncomment the following line to display red dots whilst waiting for completion.
COMPLETION_WAITING_DOTS="true"
# Uncomment the following line if you want to disable marking untracked files
# under VCS as dirty. This makes repository status check for large repositories
# much, much faster.
# DISABLE_UNTRACKED_FILES_DIRTY="true"
# Uncomment the following line if you want to change the command execution time
# stamp shown in the history command output.
# The optional three formats: "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
# HIST_STAMPS="mm/dd/yyyy"
# Would you like to use another custom folder than $ZSH/custom?
# ZSH_CUSTOM=/path/to/new-custom-folder
# Which plugins would you like to load? (plugins can be found in ~/.oh-my-zsh/plugins/*)
# Custom plugins may be added to ~/.oh-my-zsh/custom/plugins/
# Example format: plugins=(rails git textmate ruby lighthouse)
# Add wisely, as too many plugins slow down shell startup.
plugins=(extract git tmux nmap rsync)
source $ZSH/oh-my-zsh.sh
# User configuration
# export MANPATH="/usr/local/man:$MANPATH"
# You may need to manually set your language environment
# export LANG=en_US.UTF-8
# Preferred editor for local and remote sessions
# if [[ -n $SSH_CONNECTION ]]; then
# export EDITOR='vim'
# else
# export EDITOR='mvim'
# fi
# Compilation flags
# export ARCHFLAGS="-arch x86_64"
# ssh
# export SSH_KEY_PATH="~/.ssh/rsa_id"
# Set personal aliases, overriding those provided by oh-my-zsh libs,
# plugins, and themes. Aliases can be placed here, though oh-my-zsh
# users are encouraged to define aliases within the ZSH_CUSTOM folder.
# For a full list of active aliases, run `alias`.
#
# Example aliases
# alias zshconfig="mate ~/.zshrc"
# alias ohmyzsh="mate ~/.oh-my-zsh"

101
ssh/files/hassio Executable file
View file

@ -0,0 +1,101 @@
#!/bin/bash
set -e
# helppage
if [ "$1" == "help" ] || [ "$#" -lt 2 ]; then
cat << EOF
---- Hass.IO Cli ----
HomeAssistant:
$ hassio homeassistant logs
$ hassio homeassistant restart
$ hassio homeassistant update
$ hassio homeassistant check
Host:
$ hassio host hardware
$ hassio host reboot
$ hassio host shutdown
$ hassio host update
Supervisor
$ hassio supervisor logs
$ hassio supervisor info
$ hassio supervisor reload
$ hassio supervisor update
EOF
fi
function call_api() {
get_cmd=('info' 'logs' 'hardware')
command='POST'
if [[ ${get_cmd[*]} =~ $2 ]]; then
command='GET'
fi
if ! api_ret="$(curl -s -X $command "http://hassio/$1/$2")"; then
echo "API error: $api_ret"
return 1
fi
if [ "$2" == "logs" ]; then
echo "$api_ret"
return 0
fi
result="$(echo "$api_ret" | jq --raw-output ".result")"
# On error
if [ "$result" == "error" ]; then
message="$(echo "$api_ret" | jq --raw-output ".message // empty")"
echo "Error on $1/$2:" "$message"
return 0
fi
# On success
data="$(echo "$api_ret" | jq 'if .data == {} then empty else .data end')"
if [ ! -z "$data" ]; then
echo "$data" | jq .
else
echo "Success $1/$2"
fi
return 0
}
######
# homeassistant functions
if [ "$1" == "homeassistant" ]; then
hass_cmd=('logs' 'restart' 'update' 'check')
if [[ ! ${hass_cmd[*]} =~ $2 ]]; then
echo "No homeassistant command '$2' found!"
exit 1
fi
call_api "$1" "$2"
fi
######
# host functions
if [ "$1" == "host" ]; then
hass_cmd=('reboot' 'shutdown' 'update' 'hardware')
if [[ ! ${hass_cmd[*]} =~ $2 ]]; then
echo "No host command '$2' found!"
exit 1
fi
call_api "$1" "$2"
fi
######
# supervisor functions
if [ "$1" == "supervisor" ]; then
hass_cmd=('logs' 'reload' 'update' 'info')
if [[ ! ${hass_cmd[*]} =~ $2 ]]; then
echo "No supervisor command '$2' found!"
exit 1
fi
call_api "$1" "$2"
fi

12
ssh/files/motd Executable file
View file

@ -0,0 +1,12 @@
_ _ _____ ____
| | | | |_ _/ __ \
| |__| | __ _ ___ ___ | || | | |
| __ |/ _` / __/ __| | || | | |
| | | | (_| \__ \__ \_ _| || |__| |
|_| |_|\__,_|___/___(_)_____\____/
Our Cli:
$ hassio help

512
ssh/files/run.sh Executable file
View file

@ -0,0 +1,512 @@
#!/usr/bin/env bash
# ==============================================================================
# Hass.io Add-on for Home Assistant: SSH
# ==============================================================================
set -o errexit # Exit script when a command exits with non-zero status
set -o nounset # Exit script on use of an undefined variable
set -o pipefail # Return exit status of the last command in the pipe that failed
# ==============================================================================
# GLOBALS
# ==============================================================================
# Exit codes
readonly EX_OK=0 # Successful termination
readonly EX_NO_USERNAME=3 # No username specified in configuration
readonly EX_NO_LOGIN=4 # No login data in user configuration
readonly EX_SFTP_USERNAME=5 # SFTP is only possible when username is root
readonly EX_RSA_HOST_KEY=6 # Failed to generate SSH RSA host key
readonly EX_ED25519_HOST_KEY=7 # Failed to generate SSH ED25519 host key
readonly EX_USER_ADD=8 # Failed to create user account
readonly EX_USER_PROFILE=9 # Failed to create user profile
readonly EX_COMMON_LINK=10 # Failed to link common directory
readonly EX_APK_UPDATE=11 # Alpine update failed
readonly EX_PACKAGE_FAILED=12 # Alpine package installation failed
readonly EX_INIT_COMMAND=13 # Failed executing custom init command
# Configuration paths
readonly CONFIG_PATH=/data/options.json
readonly SSH_CONFIG_PATH=/etc/ssh/sshd_config
# SSH authorized_keys files
readonly SSH_AUTHORIZED_KEYS_PATH=/etc/ssh/authorized_keys
# SSH host keys
readonly SSH_HOST_RSA_KEY=/data/ssh_host_rsa_key
readonly SSH_HOST_ED25519_KEY=/data/ssh_host_ed25519_key
# Shell history files
readonly ZSH_HISTORY_FILE=/root/.zsh_history
readonly ZSH_HISTORY_PERSISTANT_FILE=/data/.zsh_history
# Debug mode?
readonly DEBUG=$(jq --raw-output ".debug" $CONFIG_PATH)
# ==============================================================================
# UTILITY
# ==============================================================================
# ------------------------------------------------------------------------------
# Displays a status message
# Globals:
# None
# Arguments:
# $* Status message to display
# Returns:
# None
# ------------------------------------------------------------------------------
display_status_message() {
local status=$*
echo "[ADD-ON] $status"
}
# ------------------------------------------------------------------------------
# Displays a debug message
# Globals:
# None
# Arguments:
# $* Status message to display
# Returns:
# None
# ------------------------------------------------------------------------------
display_debug_message() {
local status=$*
if [[ "$DEBUG" == "true" ]];
then
display_status_message "[DEBUG] $status"
fi
}
# ------------------------------------------------------------------------------
# Displays a error message and is able to terminate te script execution
# Globals:
# None
# Arguments:
# $1 Error message
# $2 Exitcode, script will continue execution when omitted
# Returns:
# None
# ------------------------------------------------------------------------------
display_error_message() {
local status=$1
local exitcode=${2:-0}
echo "[ADD-ON][ERROR] An error occured!" >&2
echo >&2
echo "!!! ERROR: $status" >&2
echo >&2
if [[ $exitcode -ne 0 ]];
then
exit "$exitcode"
fi
}
# ------------------------------------------------------------------------------
# Gets a configuration parameter form the configuration file
# Globals:
# CONFIG_PATH
# Arguments:
# $1 Key of the configuration parameter to get
# $2 'true' If the configuration value is an array, false otherwise
# Returns:
# The configuration value
# ------------------------------------------------------------------------------
get_config() {
local key=$1
local is_array=$2
if $is_array;
then
command jq --raw-output ".$key[]" $CONFIG_PATH
else
command jq --raw-output ".$key" $CONFIG_PATH
fi
}
# ==============================================================================
# SCRIPT LOGIC
# ==============================================================================
# ------------------------------------------------------------------------------
# Ensures user configuration contains required parameters
# Globals:
# EX_NO_LOGIN
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
ensure_config_requirements() {
local authorized_keys
local password
local username
local sftp
display_status_message 'Ensuring configuration requirements are met'
username=$(get_config 'username' false)
if [[ -z "$username" ]];
then
display_error_message \
'Setting a username in the mandatory!' "$EX_NO_USERNAME"
fi
authorized_keys=$(get_config 'authorized_keys' true)
password=$(get_config 'password' false)
if [[ -z "$authorized_keys" ]] && [[ -z "$password" ]];
then
display_error_message \
'Configuring a password or authorized keys is mandatory!' \
"$EX_NO_LOGIN"
fi
sftp=$(get_config 'sftp' false)
if [[ "$sftp" == "true" ]] && [[ "$username" != "root" ]];
then
display_error_message \
'You can only enable SFTP when the username is set to "root"' \
"$EX_SFTP_USERNAME"
fi
}
# ------------------------------------------------------------------------------
# Ensures all SSH host keys exists, if not, it will create them
# Globals:
# $EX_ED25519_HOST_KEY
# $EX_RSA_HOST_KEY
# $SSH_HOST_ED25519_KEY
# $SSH_HOST_RSA_KEY
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
ensure_host_keys_exist() {
display_debug_message 'Ensuring SSH host keys exists'
if [[ ! -f "$SSH_HOST_RSA_KEY" ]];
then
display_status_message 'Generating RSA host key'
command ssh-keygen -t rsa -f "$SSH_HOST_RSA_KEY" -N "" || \
display_error_message 'Failed to generate RSA host key' \
"$EX_RSA_HOST_KEY"
fi
if [[ ! -f "$SSH_HOST_ED25519_KEY" ]];
then
display_status_message 'Generating ED25519 host key'
command ssh-keygen -t ed25519 -f "$SSH_HOST_ED25519_KEY" -N "" || \
display_error_message 'Failed to generate ED25519 host key' \
"$EX_ED25519_HOST_KEY"
fi
}
# ------------------------------------------------------------------------------
# Creates a user account that is used to log in
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
setup_user() {
local username
username=$(get_config 'username' false)
username=${username,,}
if [[ "$username" != "root" ]];
then
display_status_message "Creating user to use for login"
(command adduser -D "$username" -s "/bin/sh" && \
command adduser "$username" wheel) || \
display_error_message 'Failed creating an user account for login' \
"$EX_USER_ADD"
echo 'exec sudo su -l' > "/home/$username/.profile" || \
display_error_message 'Failed configuring user profile' \
"$EX_USER_PROFILE"
command sed -i "s/AllowUsers\ .*/AllowUsers\ $username/" \
"$SSH_CONFIG_PATH"
else
display_status_message 'WARNING! username = root, this is a bad idea!'
command sed -i "s/PermitRootLogin\ .*/PermitRootLogin\ yes/" \
"$SSH_CONFIG_PATH"
fi
}
# ------------------------------------------------------------------------------
# Sets up the authorized keys
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
setup_authorized_keys() {
local authorized_keys
authorized_keys=$(get_config 'authorized_keys' true)
if [[ ! -z "$authorized_keys" ]];
then
display_status_message 'Setting up authorized_keys'
display_debug_message 'Adding authorized keys to file'
while read -r key;
do
display_debug_message "Adding public key: $key"
echo "$key" >> "$SSH_AUTHORIZED_KEYS_PATH"
done <<< "$authorized_keys"
else
display_status_message 'Login using public/private keypair is disabled.'
fi
}
# ------------------------------------------------------------------------------
# Sets up password authentication
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
setup_password_authentication() {
local password
local username
password=$(get_config 'password' false)
username=$(get_config 'username' false)
if [[ ! -z "$password" ]]; then
display_status_message 'Setting up and enabling password login'
echo "$username:$password" | chpasswd 2&> /dev/null
command sed -i "s/PasswordAuthentication.*/PasswordAuthentication\ yes/" \
"$SSH_CONFIG_PATH"
else
display_status_message 'Login using a password is disabled'
fi
}
# ------------------------------------------------------------------------------
# Configures the SSHd port
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
configure_port() {
local port
port=$(get_config 'port' false)
if [[ ! -z "$port" ]];
then
display_status_message "Setting SSH port to: $port"
command sed -i "s/Port\ .*/Port\ $port/" "$SSH_CONFIG_PATH"
else
display_status_message 'Setting SSH port to: 22'
command sed -i "s/Port\ .*/Port\ 22/" "$SSH_CONFIG_PATH"
fi
}
# ------------------------------------------------------------------------------
# Configures SFTP access
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
configure_sftp() {
local sftp
sftp=$(get_config 'sftp' false)
if [[ "$sftp" == "true" ]];
then
display_status_message "SFTP access is enabled"
else
display_status_message "SFTP access is disabled"
command sed -i '/Subsystem sftp/s/^/#/g' "$SSH_CONFIG_PATH"
fi
}
# ------------------------------------------------------------------------------
# Link a set of common used directories in the home folder
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
link_common_directories() {
local directories
display_status_message 'Linking common directories into the home folder'
directories="addons backup config share ssl"
for dir in $directories;
do
display_debug_message "Linking common directory $dir"
command ln -s "/$dir" "$HOME/$dir" || display_error_message \
"Failed linking common directory: $dir" "$EX_COMMON_LINK"
done;
}
# ------------------------------------------------------------------------------
# Persist all shell history in the permanent data storage directory
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
persist_shell_history() {
display_status_message 'Configuring shell to persist history'
command touch "$ZSH_HISTORY_PERSISTANT_FILE"
command chmod 600 "$ZSH_HISTORY_PERSISTANT_FILE"
command ln -s -f "$ZSH_HISTORY_PERSISTANT_FILE" "$ZSH_HISTORY_FILE"
}
# ------------------------------------------------------------------------------
# Install list of user requested Alpine packages
# Globals:
# EX_PACKAGE_FAILED
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
install_packages() {
local packages
packages=$(get_config 'packages' true)
if [[ ! -z "$packages" ]];
then
display_status_message 'Installing user requested Alpine packages'
display_status_message 'Updating Alpine packages repository indexes'
command apk update || display_error_message \
'Failed updating Alpine packages repository indexes' "$EX_APK_UPDATE"
for package in $packages;
do
display_status_message "Installing Alpine package: $package"
command apk add "$package" || display_error_message \
"Failed installing package $package" $EX_PACKAGE_FAILED
done
fi
}
# ------------------------------------------------------------------------------
# Runs configured custom commands on startup of the add-on
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
run_init_commands() {
local commands
commands=$(get_config 'init_commands' true)
if [[ ! -z "$commands" ]];
then
display_status_message 'Running custom init commands'
while read -r cmd;
do
display_debug_message "Running command: $cmd"
eval "$cmd" || display_error_message \
"Failed executing init command: $cmd" "$EX_INIT_COMMAND"
done <<< "$commands"
else
display_debug_message 'No custom init commands configured'
fi
}
# ------------------------------------------------------------------------------
# Starts the SSH daemon
# Globals:
# $DEBUG
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
start_ssh_server() {
display_status_message 'Starting SSH daemon'
if [[ "$DEBUG" == "true" ]];
then
command /usr/sbin/sshd -D -d -e < /dev/null
else
command /usr/sbin/sshd -D -e < /dev/null
fi
}
# ------------------------------------------------------------------------------
# Cleanup function after execution is of the script is stopped. (trap)
# Globals:
# None
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
cleanup() {
true
}
# ==============================================================================
# RUN LOGIC
# ------------------------------------------------------------------------------
# Globals:
# EX_OK
# HOMEBRIDGE_CONFIG_FILE
# Arguments:
# None
# Returns:
# None
# ------------------------------------------------------------------------------
main() {
trap cleanup ERR
trap cleanup EXIT
ensure_config_requirements
ensure_host_keys_exist
setup_user
setup_authorized_keys
setup_password_authentication
configure_port
configure_sftp
link_common_directories
persist_shell_history
install_packages
run_init_commands
start_ssh_server
exit "$EX_OK"
}
main

71
ssh/files/sshd_config Normal file
View file

@ -0,0 +1,71 @@
# Basics
# ===================
Port 22
AddressFamily any
TCPKeepAlive no
ClientAliveInterval 600
ClientAliveCountMax 3
UseDNS yes
# Loggin
# ===================
SyslogFacility AUTH
LogLevel VERBOSE
# Security
# ===================
Protocol 2
StrictModes yes
HostKey /data/ssh_host_rsa_key
HostKey /data/ssh_host_ed25519_key
# Cryptography
# ===================
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
# Authentication
# ===================
LoginGraceTime 120
MaxAuthTries 6
MaxSessions 10
MaxStartups 10:30:100
PermitRootLogin no
AllowUsers root
PubkeyAuthentication yes
IgnoreRhosts yes
HostbasedAuthentication no
IgnoreUserKnownHosts no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
PasswordAuthentication no
AuthorizedKeysFile /etc/ssh/authorized_keys
# Environment
# ===================
PermitUserEnvironment no
# Forwarding / Tunnel
# ===================
X11Forwarding no
X11DisplayOffset 10
X11UseLocalhost yes
PermitTunnel no
AllowTcpForwarding no
AllowAgentForwarding no
GatewayPorts no
# Messages
# ===================
Banner none
PrintMotd yes
# SFTP
=====================
Subsystem sftp /usr/lib/ssh/sftp-server
# Misc
# ===================
AcceptEnv LANG LC_*

1
ssh/files/wheel Normal file
View file

@ -0,0 +1 @@
%wheel ALL=(ALL) NOPASSWD: ALL

BIN
ssh/images/ssh-audit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
ssh/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB