do some major refactoring and add a lot more features

This commit is contained in:
Dennis Hermsmeier 2019-06-19 21:54:09 +02:00
parent 2315d9ec80
commit 51a2397c0a
4 changed files with 348 additions and 49 deletions

View File

@ -1,19 +1,32 @@
ARG ALPINE_VERSION=${ALPINE_VERSION:-3.9}
FROM alpine:${ALPINE_VERSION}
ARG ALPINE_VERSION="${ALPINE_VERSION:-3.9}"
FROM alpine:"${ALPINE_VERSION}"
LABEL maintainer="https://github.com/hermsi1337"
ARG OPENSSH_VERSION=${OPENSSH_VERSION:-7.9_p1-r5}
ENV OPENSSH_VERSION=${OPENSSH_VERSION} \
ROOT_PASSWORD=root \
KEYPAIR_LOGIN=false
ARG OPENSSH_VERSION="${OPENSSH_VERSION:-7.9_p1-r5}"
ENV CONF_VOLUME="/conf.d"
ENV OPENSSH_VERSION="${OPENSSH_VERSION}" \
CACHED_SSH_DIRECTORY="${CONF_VOLUME}/ssh" \
AUTHORIZED_KEYS_VOLUME="${CONF_VOLUME}/authorized_keys" \
ROOT_KEYPAIR_LOGIN_ENABLED="false" \
ROOT_LOGIN_UNLOCKED="false" \
USER_LOGIN_SHELL="/bin/bash" \
USER_LOGIN_SHELL_FALLBACK="/bin/ash"
RUN apk add --upgrade --no-cache \
bash \
bash-completion \
rsync \
openssh=${OPENSSH_VERSION} \
&& \
mkdir -p /root/.ssh "${CONF_VOLUME}" "${AUTHORIZED_KEYS_VOLUME}" \
&& \
cp -a /etc/ssh "${CACHED_SSH_DIRECTORY}" \
&& \
rm -rf /var/cache/apk/*
COPY entrypoint.sh /
RUN apk add --upgrade --no-cache openssh=${OPENSSH_VERSION} \
&& chmod +x /entrypoint.sh \
&& mkdir -p /root/.ssh \
&& rm -rf /var/cache/apk/* /tmp/*
COPY conf.d/etc/ /etc/
EXPOSE 22
VOLUME ["/etc/ssh"]
ENTRYPOINT ["/entrypoint.sh"]

View File

@ -1,7 +1,7 @@
## Make your OpenSSH fly on Alpine
### Overview
Use this Dockerfile / -image to start a sshd-server upon a lightweight Alpine container.
Use this Dockerfile / -image to start a slim and highly customizable sshd-server with `bash` and `rsync` installed.
### Regular builds, automagically
[![Build Status](https://travis-ci.com/Hermsi1337/docker-sshd.svg?branch=master)](https://travis-ci.com/Hermsi1337/docker-sshd)
@ -11,23 +11,19 @@ Thanks to [Travis-CI](https://travis-ci.com/) this image is pushed weekly and cr
For recent tags check [Dockerhub](https://hub.docker.com/r/hermsi/alpine-sshd/tags/).
### Features
* Always installs the latest OpenSSH-Version available for Alpine
* Password of "root"-user can be changed when starting the container using --env
* You can choose between ssh-keypair- and password auth
* `bash`-shell and `rsync` installed
* Default `.bashrc` from `ubuntu`
* Desired shell is configurable by --env
* En- or disable `root`-user by --env
* Choose between keypar and password auth for `root`
* Password for `root` is configurable by --env
* Additional ssh-users can be created by --env
* Authentication for additional users is done by keypair
* Beautifully colored log output
### Extending this image
This image is designed to be as slim and vanilla as possible.
If you need additional Tools like `git` or `bash`-shell, I definetly recommend to build your own image on top of `alpine-sshd`:
```Dockerfile
FROM hermsi/alpine-sshd:latest
RUN apk add --no-cache --upgrade \
git \
bash
```
### Basic Usage
#### Authentication by password
```
### Usage examples
#### Authentication as root by password
```bash
$ docker run --rm \
--publish=1337:22 \
--env ROOT_PASSWORD=MyRootPW123 \
@ -35,21 +31,71 @@ hermsi/alpine-sshd
```
After the container is up you are able to ssh in it as root with the in --env provided password for "root"-user.
```
```bash
$ ssh root@mydomain.tld -p 1337
```
#### Authentication by ssh-keypair
```
#### Authentication as root by ssh-keypair
```bash
$ docker run --rm \
--publish=1337:22 \
--env KEYPAIR_LOGIN=true \
--env ROOT_KEYPAIR_LOGIN_ENABLED=true \
--volume /path/to/authorized_keys:/root/.ssh/authorized_keys \
hermsi/alpine-sshd
```
After the container is up you are able to ssh in it as root with a private-key which matches the provided public-key in authorized_keys for "root"-user.
```
```bash
$ ssh root@mydomain.tld -p 1337 -i /path/to/private_key
```
#### Authenticate as additional user by ssh-keypair
$ docker run --rm \
--publish=1337:22 \
--env SSH_USERS="hermsi:1000:1000" \
--volume /path/to/hermsi_public_key:/conf.d/authorized_keys/hermsi \
hermsi/alpine-sshd
```
After the container is up you are able to ssh in it as the given user with a private-key that matches the provided public-key in authorized_keys for your created user.
```bash
$ ssh mydomain.tld -l hermsi -p 1337 -i /path/to/hermsi_private_key
```
#### Create multiple, additional users with keypair
$ docker run --rm \
--publish=1337:22 \
--env SSH_USERS="hermsi:1000:1000,dennis:1001:1001" \
--volume /path/to/hermsi_public_key:/conf.d/authorized_keys/hermsi \
--volume /path/to/dennis_public_key:/conf.d/authorized_keys/dennis \
hermsi/alpine-sshd
```
After the container is up you are able to ssh in it as one of the given users with a private-key that matches the provided public-key in authorized_keys for your desired user.
```bash
$ ssh root@mydomain.tld -p 1337 -i /path/to/private_key
```
### Configuration
While beeing very slim and vanilla this image is still highly customizable.
#### Environment variables
| Variable | Possible Values | Default value | Explanation |
|:-----------------:|:-----------------:|:----------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------:|
| ROOT_LOGIN_UNLOCKED | 'true' or 'false' | 'false' | Whether to enable or disable login as 'root' user |
| ROOT_KEYPAIR_LOGIN_ENABLED | 'true' or 'false' | 'false' | Enable login as 'root' by keypair (implies `ROOT_LOGIN_UNLOCKED`). Must mount public-key into container: `/root/.ssh/authorized_keys` |
| USER_LOGIN_SHELL | any existing shell | `/bin/bash` | Choose the desired default shell for all additional users. If the configured shell is not existent, a fallback to `/bin/ash` is applied. |
### Extending this image
This image is designed to be as slim and vanilla as possible.
If you need additional Tools like `git` , I definetly recommend to build your own image on top of `alpine-sshd`:
```Dockerfile
FROM hermsi/alpine-sshd:latest
RUN apk add --no-cache \
git
```
### Use with docker-compose
I built this image in order to use it along with a nginx and fpm-php container for transferring files via sftp.
If you are interested in a Dockerfile which fulfills this need: [this way](https://github.com/Hermsi1337/docker-compose/blob/master/full_php_dev_stack/docker-compose.yml)

View File

@ -0,0 +1,103 @@
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
# don't put duplicate lines in the history. See bash(1) for more options
# ... or force ignoredups and ignorespace
HISTCONTROL=ignoredups:ignorespace
# append to the history file, don't overwrite it
shopt -s histappend
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000
# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize
# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then
debian_chroot=$(cat /etc/debian_chroot)
fi
# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
xterm-color) color_prompt=yes;;
esac
# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes
if [ -n "$force_color_prompt" ]; then
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
# We have color support; assume it's compliant with Ecma-48
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
# a case would tend to support setf rather than setaf.)
color_prompt=yes
else
color_prompt=
fi
fi
if [ "$color_prompt" = yes ]; then
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt
# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
;;
*)
;;
esac
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
alias ls='ls --color=auto'
#alias dir='dir --color=auto'
#alias vdir='vdir --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
# Add an "alert" alias for long running commands. Use like so:
# sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
. /etc/bash_completion
fi

View File

@ -1,26 +1,163 @@
#!/bin/sh
#!/usr/bin/env sh
if [ "${ROOT_PASSWORD}" == "root" ] || [ -z "${ROOT_PASSWORD}" ]; then
export ROOT_PASSWORD="$(hexdump -e '"%02x"' -n 16 /dev/urandom)"
echo "Successfully generated a random password for root"
set -e
# enable debug mode if desired
if [ "${DEBUG}" = "true" ]; then
set -x
fi
echo "root:${ROOT_PASSWORD}" | chpasswd
log() {
LEVEL="${1}"
TO_LOG="${2}"
WHITE='\033[1;37m'
YELLOW='\033[1;33m'
RED='\033[1;31m'
NO_COLOR='\033[0m'
if [ "${LEVEL}" = "warning" ]; then
LOG_LEVEL="${YELLOW}WARN${NO_COLOR}"
elif [ "${LEVEL}" = "error" ]; then
LOG_LEVEL="${RED}ERROR${NO_COLOR}"
else
LOG_LEVEL="${WHITE}INFO${NO_COLOR}"
if [ -z "${TO_LOG}" ]; then
TO_LOG="${1}"
fi
fi
echo -e "[${LOG_LEVEL}] ${TO_LOG}"
}
ensure_mod() {
FILE="${1}"
MOD="${2}"
U_ID="${3}"
G_ID="${4}"
chmod "${MOD}" "${FILE}"
chown "${U_ID}"."${G_ID}" "${FILE}"
}
generate_passwd() {
hexdump -e '"%02x"' -n 16 /dev/urandom
}
# ensure backward comaptibility for earlier versions of this image
if [ -n "${KEYPAIR_LOGIN}" ] && [ "${KEYPAIR_LOGIN}" = "true" ]; then
ROOT_KEYPAIR_LOGIN_ENABLED="${KEYPAIR_LOGIN}"
fi
if [ -n "${ROOT_PASSWORD}" ]; then
ROOT_LOGIN_UNLOCKED="true"
fi
# enable root login if keypair login is enabled
if [ "${ROOT_KEYPAIR_LOGIN_ENABLED}" = "true" ]; then
ROOT_LOGIN_UNLOCKED="true"
fi
# initiate default sshd-config if there is none available
if ! find "/etc/ssh" -mindepth 1 -print -quit 2>/dev/null | grep -q .; then
cp -a "${CACHED_SSH_DIRECTORY}"/* /etc/ssh/.
fi
rm -rf "${CACHED_SSH_DIRECTORY}"
# generate host keys if not present
ssh-keygen -A
ssh-keygen -A 1>/dev/null
log "Applying configuration for 'root' user ..."
if [ "${ROOT_LOGIN_UNLOCKED}" = "true" ] ; then
# generate random root password
if [ -z "${ROOT_PASSWORD}" ]; then
log " generating random password for user 'root'"
ROOT_PASSWORD="$(generate_passwd)"
fi
echo "root:${ROOT_PASSWORD}" | chpasswd >/dev/null 2>&1
log " password for user 'root' set"
log "warning" " user 'root' is now UNLOCKED"
# set root login mode by password or keypair
if [ "${ROOT_KEYPAIR_LOGIN_ENABLED}" = "true" ] && [ -f "${HOME}/.ssh/authorized_keys" ] ; then
sed -i "s/#PermitRootLogin.*/PermitRootLogin without-password/" /etc/ssh/sshd_config
sed -i "s/#PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config
ensure_mod "${HOME}/.ssh/authorized_keys" "0600" "root" "root"
log " enabled login by keypair and disabled password-login for user 'root'"
else
sed -i "s/#PermitRootLogin.*/PermitRootLogin\ yes/" /etc/ssh/sshd_config
log " enabled login by password for user 'root'"
fi
# set root login mode by password or keypair
if [ "${KEYPAIR_LOGIN}" = "true" ] && [ -f "${HOME}/.ssh/authorized_keys" ] ; then
sed -i "s/#PermitRootLogin.*/PermitRootLogin without-password/" /etc/ssh/sshd_config
sed -i "s/#PasswordAuthentication.*/PasswordAuthentication no/" /etc/ssh/sshd_config
chmod 600 "${HOME}/.ssh/authorized_keys"
chown root.root "${HOME}/.ssh/authorized_keys"
echo "Enabled root-login by keypair and disabled password-login"
else
sed -i s/#PermitRootLogin.*/PermitRootLogin\ yes/ /etc/ssh/sshd_config
echo "Enabled root-login by password"
sed -i "s/#PermitRootLogin.*/PermitRootLogin no/" /etc/ssh/sshd_config
log " disabled login for user 'root'"
log " user 'root' is now LOCKED"
fi
printf "\n" ""
log "Applying configuration for additional users ..."
if [ ! -x "${USER_LOGIN_SHELL}" ]; then
log "error" " can not allocate desired shell '${USER_LOGIN_SHELL}', falling back to '${USER_LOGIN_SHELL_FALLBACK}' ..."
USER_LOGIN_SHELL="${USER_LOGIN_SHELL_FALLBACK}"
fi
log " desired shell is ${USER_LOGIN_SHELL}"
if [ -n "${SSH_USERS}" ]; then
IFS=","
for USER in ${SSH_USERS}; do
log " '${USER}'"
USER_NAME="$(echo "${USER}" | cut -d ':' -f 1)"
USER_UID="$(echo "${USER}" | cut -d ':' -f 2)"
USER_GID="$(echo "${USER}" | cut -d ':' -f 3)"
if [ -z "${USER_NAME}" ] || [ -z "${USER_UID}" ] || [ -z "${USER_GID}" ]; then
log "error" " skipping invalid data '${USER_NAME}' - UID: '${USER_UID}' GID: '${USER_GID}'"
continue
fi
getent group "${USER_GID}" >/dev/null 2>&1 || addgroup -g "${USER_GID}" "${USER_NAME}"
getent passwd "${USER_NAME}" >/dev/null 2>&1 || adduser -s "${USER_LOGIN_SHELL}" -D -u "${USER_UID}" -G "${USER_NAME}" "${USER_NAME}"
passwd -u "${USER_NAME}" >/dev/null 2>&1
mkdir -p "/home/${USER_NAME}/.ssh"
log " user '${USER_NAME}' created - UID: '${USER_UID}' GID: '${USER_GID}'"
MOUNTED_AUTHORIZED_KEYS="${AUTHORIZED_KEYS_VOLUME}/${USER_NAME}"
LOCAL_AUTHORIZED_KEYS="/home/${USER_NAME}/.ssh/authorized_keys"
if [ ! -e "${MOUNTED_AUTHORIZED_KEYS}" ]; then
log "warning" " no SSH authorized_keys found for user '${USER_NAME}'"
else
cp "${MOUNTED_AUTHORIZED_KEYS}" "${LOCAL_AUTHORIZED_KEYS}"
log " copied ${MOUNTED_AUTHORIZED_KEYS} to ${LOCAL_AUTHORIZED_KEYS}"
ensure_mod "${LOCAL_AUTHORIZED_KEYS}" "0600" "${USER_NAME}" "${USER_GID}"
log " set mod 0600 on ${LOCAL_AUTHORIZED_KEYS}"
fi
printf "\n" ""
done
unset IFS
else
log " no additional SSH-users set"
fi
echo ""
# do not detach (-D), log to stderr (-e), passthrough other arguments
exec /usr/sbin/sshd -D -e "$@"
exec /usr/sbin/sshd -D -e "$@"