Initial commit
This commit is contained in:
		
						commit
						3442029733
					
				
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					**/*
 | 
				
			||||||
 | 
					!.gitignore
 | 
				
			||||||
 | 
					!README.md
 | 
				
			||||||
 | 
					!docker-compose.yaml
 | 
				
			||||||
 | 
					!build/
 | 
				
			||||||
 | 
					!build/*
 | 
				
			||||||
							
								
								
									
										27
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					# SSH entrypoint with yubikey OTP support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This docker container runs a sshd instance which is exposed through TCP 443. It can be a nice and secure way into your network.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since this is the only TCP service I expose, traefik handles this automagically while also routing SSL HTTPS traffic the normal way.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The image is a modified version of https://github.com/Hermsi1337/docker-sshd which has been made to work with yubikey OTP certification and allow for personalized `.ssh/config` files to be loaded.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## ENV variable `SSH_USERS`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`SSH_USERS` contain a comma separates lists of username:UID:GUI that will be allowed to login.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ex:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`SSH_USERS=myuser:1000:1000,anotheruser:1001:1001`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The directory mapped to `/conf.d/authorized_keys` contain files for authorized_keys, authorized yubikeys and ssh config.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- A file named `myuser` will be copied to `/home/myuser/.ssh/authorized_keys`
 | 
				
			||||||
 | 
					- A file named `myuser.config` will be copied to `/home/myuser/.ssh/config`
 | 
				
			||||||
 | 
					- A file name `myuser.yubi` will be copied to `/home/myuser/.yubico/authorized_yubikeys`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The format of the `.yubi` file is your username followed by a list of the first 12 characters from any OTP from all of your yubikeys, all separated by `:`s. E.g.:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					myuser:cccccccgklgc:ccccccclabca:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										10
									
								
								build/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								build/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					FROM hermsi/alpine-sshd:latest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN apk add --no-cache \
 | 
				
			||||||
 | 
					        openssh-server-pam \
 | 
				
			||||||
 | 
					        yubico-pam \
 | 
				
			||||||
 | 
					        google-authenticator \
 | 
				
			||||||
 | 
					    && \
 | 
				
			||||||
 | 
					    rm -rf /var/cache/apk/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY entrypoint.sh /
 | 
				
			||||||
							
								
								
									
										227
									
								
								build/entrypoint.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										227
									
								
								build/entrypoint.sh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,227 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#############################################
 | 
				
			||||||
 | 
					# This file is copied from https://github.com/Hermsi1337/docker-sshd
 | 
				
			||||||
 | 
					# Two additions have been made to allow for yubikey authentication
 | 
				
			||||||
 | 
					# and adding an .ssh/config file
 | 
				
			||||||
 | 
					#############################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# enable debug mode if desired
 | 
				
			||||||
 | 
					if [[ "${DEBUG}" == "true" ]]; then 
 | 
				
			||||||
 | 
					    set -x
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 [[ ! "$(ls -A /etc/ssh)" ]]; then
 | 
				
			||||||
 | 
					    cp -a "${CACHED_SSH_DIRECTORY}"/* /etc/ssh/.
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					rm -rf "${CACHED_SSH_DIRECTORY}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# generate host keys if not present
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        USER_GROUP="${USER_NAME}"
 | 
				
			||||||
 | 
					        if getent group "${USER_GID}" &>/dev/null ; then 
 | 
				
			||||||
 | 
					            USER_GROUP="$(getent group "${USER_GID}" | cut -d ':' -f 1)"
 | 
				
			||||||
 | 
					            log "warning" "        desired GID is already present in system. Using the present group-name - GID: '${USER_GID}' GNAME: '${USER_GROUP}'"
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            addgroup -g "${USER_GID}" "${USER_GROUP}"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if getent passwd "${USER_NAME}" &>/dev/null ; then
 | 
				
			||||||
 | 
					            log "warning" "        desired USER_NAME is already present in system. Skipping creation - USER_NAME: '${USER_NAME}'"
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            adduser -s "${USER_LOGIN_SHELL}" -D -u "${USER_UID}" -G "${USER_GROUP}" "${USER_NAME}"
 | 
				
			||||||
 | 
					            log "        user '${USER_NAME}' created - UID: '${USER_UID}' GID: '${USER_GID}' GNAME: '${USER_GROUP}'"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        passwd -u "${USER_NAME}" &>/dev/null || true
 | 
				
			||||||
 | 
					        mkdir -p "/home/${USER_NAME}/.ssh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #############################################
 | 
				
			||||||
 | 
					        # ADDED
 | 
				
			||||||
 | 
					        #############################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log "        set mod 0755 on /home/${USER_NAME}/.ssh"
 | 
				
			||||||
 | 
					        ensure_mod "/home/${USER_NAME}/.ssh" "0755" "${USER_NAME}" "${USER_GID}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mkdir -p "/home/${USER_NAME}/.yubico"
 | 
				
			||||||
 | 
					        log "        set mod 0755 on /home/${USER_NAME}/.yubico"
 | 
				
			||||||
 | 
					        ensure_mod "/home/${USER_NAME}/.yubico" "0755" "${USER_NAME}" "${USER_GID}"
 | 
				
			||||||
 | 
					        MOUNTED_AUTHORIZED_YUBI="${AUTHORIZED_KEYS_VOLUME}/${USER_NAME}.yubi"
 | 
				
			||||||
 | 
					        LOCAL_AUTHORIZED_YUBI="/home/${USER_NAME}/.yubico/authorized_yubikeys"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if [[ -e "${MOUNTED_AUTHORIZED_YUBI}" ]]; then
 | 
				
			||||||
 | 
					            cp "${MOUNTED_AUTHORIZED_YUBI}" "${LOCAL_AUTHORIZED_YUBI}"
 | 
				
			||||||
 | 
					            log "        copied ${MOUNTED_AUTHORIZED_YUBI} to ${LOCAL_AUTHORIZED_YUBI}"
 | 
				
			||||||
 | 
					            ensure_mod "${LOCAL_AUTHORIZED_YUBI}" "0600" "${USER_NAME}" "${USER_GID}"
 | 
				
			||||||
 | 
					            log "        set mod 0600 on ${LOCAL_AUTHORIZED_YUBI}"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        MOUNTED_CONFIG="${AUTHORIZED_KEYS_VOLUME}/${USER_NAME}.config"
 | 
				
			||||||
 | 
					        LOCAL_CONFIG="/home/${USER_NAME}/.ssh/config"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if [[ -e "${MOUNTED_CONFIG}" ]]; then
 | 
				
			||||||
 | 
					            cp "${MOUNTED_CONFIG}" "${LOCAL_CONFIG}"
 | 
				
			||||||
 | 
					            log "        copied ${MOUNTED_CONFIG} to ${LOCAL_CONFIG}"
 | 
				
			||||||
 | 
					            ensure_mod "${LOCAL_CONFIG}" "0644" "${USER_NAME}" "${USER_GID}"
 | 
				
			||||||
 | 
					            log "        set mod 0644 on ${LOCAL_CONFIG}"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #############################################
 | 
				
			||||||
 | 
					        # END OF ADDITION
 | 
				
			||||||
 | 
					        #############################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        printf "\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					    unset IFS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    log "    no additional SSH-users set"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#############################################
 | 
				
			||||||
 | 
					# ADDED
 | 
				
			||||||
 | 
					#############################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sed -i "s/#ChallengeResponseAuthentication.*/ChallengeResponseAuthentication yes/" /etc/ssh/sshd_config
 | 
				
			||||||
 | 
					sed -i "s/#UsePAM.*/UsePAM yes/" /etc/ssh/sshd_config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "auth sufficient pam_yubico.so id=16 debug" >> /etc/pam.d/sshd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#############################################
 | 
				
			||||||
 | 
					# END OF ADDITION
 | 
				
			||||||
 | 
					#############################################
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# do not detach (-D), log to stderr (-e), passthrough other arguments
 | 
				
			||||||
 | 
					exec /usr/sbin/sshd -D -e "$@"
 | 
				
			||||||
							
								
								
									
										22
									
								
								docker-compose.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								docker-compose.yaml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					version: "3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					networks:
 | 
				
			||||||
 | 
					  web:
 | 
				
			||||||
 | 
					    external: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  ssh:
 | 
				
			||||||
 | 
					    build: ./build
 | 
				
			||||||
 | 
					    restart: unless-stopped
 | 
				
			||||||
 | 
					    networks:
 | 
				
			||||||
 | 
					      web:
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      SSH_USERS:
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - ./authorized_keys:/conf.d/authorized_keys
 | 
				
			||||||
 | 
					      - ./ssh:/etc/ssh
 | 
				
			||||||
 | 
					    labels:
 | 
				
			||||||
 | 
					      traefik.enable: true
 | 
				
			||||||
 | 
					      traefik.tcp.services.ssh.loadbalancer.server.port: 22
 | 
				
			||||||
 | 
					      traefik.tcp.routers.ssh.rule: HostSNI(`*`)
 | 
				
			||||||
 | 
					      traefik.tcp.routers.ssh.entrypoints: websecure
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user