diff --git a/CHANGELOG.md b/CHANGELOG.md index d88c46e..e5f4db2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ Summary of release changes for Version 1 - CentOS-6 +### 1.8.4 - 2018-04-24 + +- Updates supervisor to 3.3.4. +- Adds feature to set `SSH_USER_PASSWORD` via a file path. e.g. Docker Swarm secrets. +- Adds feature to set `SSH_AUTHORIZED_KEYS` via a file path. e.g. Docker Swarm config. +- Updates python-setuptools to 0.6.10-4el6_9. Removes workaround for easy_install failure. + ### 1.8.3 - 2018-01-12 - Fixes image build failure caused by error installing supervisor via easy_install. diff --git a/Dockerfile b/Dockerfile index 064ad55..b3ea511 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ RUN rpm --rebuilddb \ openssh-5.3p1-123.el6_9 \ openssh-clients-5.3p1-123.el6_9 \ openssh-server-5.3p1-123.el6_9 \ - python-setuptools-0.6.10-3.el6 \ + python-setuptools-0.6.10-4el6_9 \ sudo-1.8.6p3-29.el6_9 \ vim-minimal-7.4.629-5.el6_8.1 \ yum-plugin-versionlock-1.1.30-40.el6 \ @@ -65,8 +65,7 @@ RUN rpm --rebuilddb \ # supervisord to be easily inspected with "docker logs". # ----------------------------------------------------------------------------- RUN easy_install \ - --index-url 'https://pypi.python.org/pypi' \ - 'supervisor == 3.3.3' \ + 'supervisor == 3.3.4' \ 'supervisor-stdout == 0.1.1' \ && mkdir -p \ /var/log/supervisor/ @@ -165,7 +164,7 @@ ENV SSH_AUTHORIZED_KEYS="" \ # ----------------------------------------------------------------------------- # Set image metadata # ----------------------------------------------------------------------------- -ARG RELEASE_VERSION="1.8.3" +ARG RELEASE_VERSION="1.8.4" LABEL \ maintainer="James Deathe " \ install="docker run \ diff --git a/README.md b/README.md index 08e6029..d17d6f4 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,12 @@ Includes public key authentication, Automated password generation and supports c ## Overview & links -The latest CentOS-6 / CentOS-7 based releases can be pulled from the `centos-6` / `centos-7` Docker tags respectively. For production use it is recommended to select a specific release tag - the convention is `centos-6-1.8.3` OR `1.8.3` for the [1.8.3](https://github.com/jdeathe/centos-ssh/tree/1.8.3) release tag and `centos-7-2.3.1` OR `2.3.1` for the [2.3.1](https://github.com/jdeathe/centos-ssh/tree/2.3.1) release tag. +The latest CentOS-6 / CentOS-7 based releases can be pulled from the `centos-6` / `centos-7` Docker tags respectively. For production use it is recommended to select a specific release tag - the convention is `centos-6-1.8.4` OR `1.8.4` for the [1.8.4](https://github.com/jdeathe/centos-ssh/tree/1.8.4) release tag and `centos-7-2.3.2` OR `2.3.2` for the [2.3.2](https://github.com/jdeathe/centos-ssh/tree/2.3.2) release tag. ### Tags and respective `Dockerfile` links -- `centos-7`,`centos-7-2.3.1`,`2.3.1` [(centos-7/Dockerfile)](https://github.com/jdeathe/centos-ssh/blob/centos-7/Dockerfile) -- `centos-6`,`centos-6-1.8.3`,`1.8.3` [(centos-6/Dockerfile)](https://github.com/jdeathe/centos-ssh/blob/centos-6/Dockerfile) +- `centos-7`,`centos-7-2.3.2`,`2.3.2` [(centos-7/Dockerfile)](https://github.com/jdeathe/centos-ssh/blob/centos-7/Dockerfile) +- `centos-6`,`centos-6-1.8.4`,`1.8.4` [(centos-6/Dockerfile)](https://github.com/jdeathe/centos-ssh/blob/centos-6/Dockerfile) The Dockerfile can be used to build a base image that is the bases for several other docker images. @@ -105,10 +105,10 @@ $ docker run \ --rm \ --privileged \ --volume /:/media/root \ - jdeathe/centos-ssh:1.8.3 \ + jdeathe/centos-ssh:1.8.4 \ /usr/sbin/scmi install \ --chroot=/media/root \ - --tag=1.8.3 \ + --tag=1.8.4 \ --name=ssh.pool-1.1.1 \ --setopt="--volume {{NAME}}.config-ssh:/etc/ssh" ``` @@ -122,10 +122,10 @@ $ docker run \ --rm \ --privileged \ --volume /:/media/root \ - jdeathe/centos-ssh:1.8.3 \ + jdeathe/centos-ssh:1.8.4 \ /usr/sbin/scmi uninstall \ --chroot=/media/root \ - --tag=1.8.3 \ + --tag=1.8.4 \ --name=ssh.pool-1.1.1 \ --setopt="--volume {{NAME}}.config-ssh:/etc/ssh" ``` @@ -139,10 +139,10 @@ $ docker run \ --rm \ --privileged \ --volume /:/media/root \ - jdeathe/centos-ssh:1.8.3 \ + jdeathe/centos-ssh:1.8.4 \ /usr/sbin/scmi install \ --chroot=/media/root \ - --tag=1.8.3 \ + --tag=1.8.4 \ --name=ssh.pool-1.1.1 \ --manager=systemd \ --register \ @@ -164,7 +164,7 @@ Since release tags `1.7.2` / `2.1.2` the install template has been added to the _NOTE:_ A prerequisite of the following examples is that the image has been pulled (or loaded from the release package). ``` -$ docker pull jdeathe/centos-ssh:1.8.3 +$ docker pull jdeathe/centos-ssh:1.8.4 ``` To see detailed information about the image run `scmi` with the `--info` option. To see all available `scmi` options run with the `--help` option. @@ -173,7 +173,7 @@ To see detailed information about the image run `scmi` with the `--info` option. $ eval "sudo -E $( docker inspect \ -f "{{.ContainerConfig.Labels.install}}" \ - jdeathe/centos-ssh:1.8.3 + jdeathe/centos-ssh:1.8.4 ) --info" ``` @@ -183,7 +183,7 @@ To perform an installation using the docker name `ssh.pool-1.2.1` simply use the $ eval "sudo -E $( docker inspect \ -f "{{.ContainerConfig.Labels.install}}" \ - jdeathe/centos-ssh:1.8.3 + jdeathe/centos-ssh:1.8.4 ) --name=ssh.pool-1.2.1" ``` @@ -193,7 +193,7 @@ To uninstall use the *same command* that was used to install but with the `unins $ eval "sudo -E $( docker inspect \ -f "{{.ContainerConfig.Labels.uninstall}}" \ - jdeathe/centos-ssh:1.8.3 + jdeathe/centos-ssh:1.8.4 ) --name=ssh.pool-1.2.1" ``` @@ -206,7 +206,7 @@ To see detailed information about the image run `scmi` with the `--info` option. ``` $ sudo -E atomic install \ -n ssh.pool-1.3.1 \ - jdeathe/centos-ssh:1.8.3 \ + jdeathe/centos-ssh:1.8.4 \ --info ``` @@ -215,14 +215,14 @@ To perform an installation using the docker name `ssh.pool-1.3.1` simply use the ``` $ sudo -E atomic install \ -n ssh.pool-1.3.1 \ - jdeathe/centos-ssh:1.8.3 + jdeathe/centos-ssh:1.8.4 ``` Alternatively, you could use the `scmi` options `--name` or `-n` for naming the container. ``` $ sudo -E atomic install \ - jdeathe/centos-ssh:1.8.3 \ + jdeathe/centos-ssh:1.8.4 \ --name ssh.pool-1.3.1 ``` @@ -231,7 +231,7 @@ To uninstall use the *same command* that was used to install but with the `unins ``` $ sudo -E atomic uninstall \ -n ssh.pool-1.3.1 \ - jdeathe/centos-ssh:1.8.3 + jdeathe/centos-ssh:1.8.4 ``` #### Using environment variables @@ -299,7 +299,7 @@ There are several environmental variables defined at runtime these allow the ope ##### SSH_AUTHORIZED_KEYS -As detailed below the public key added for the SSH user is insecure by default. This is intentional and allows for access using a known private key. Using `SSH_AUTHORIZED_KEYS` you can replace the insecure public key with another one (or several). Further details on how to create your own private + public key pair are provided below. If adding more than one key it is recommended to base64 encode the value. +As detailed below the public key added for the SSH user is insecure by default. This is intentional and allows for access using a known private key. Using `SSH_AUTHORIZED_KEYS` you can replace the insecure public key with another one (or several). Further details on how to create your own private + public key pair are provided below. If adding more than one key it is recommended to either base64 encode the value or use a container file path in combination with a bind mounted file or Docker Swarm config etc. ``` ... @@ -320,6 +320,14 @@ ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAqmLedI2mEJimvIm1OzT1EYJCMwegL/jfsXARLnYkZvJl ... ``` +Using `SSH_AUTHORIZED_KEYS` with a container file path allows for the authorized_keys to be populated from the file path. + +``` +... + --env "SSH_AUTHORIZED_KEYS=/var/run/config/authorized_keys" +... +``` + ##### SSH_AUTOSTART_SSHD & SSH_AUTOSTART_SSHD_BOOTSTRAP It may be desirable to prevent the startup of the sshd daemon and/or sshd-bootstrap script. For example, when using an image built from this Dockerfile as the source for another Dockerfile you could disable both sshd and sshd-booststrap from startup by setting `SSH_AUTOSTART_SSHD` and `SSH_AUTOSTART_SSHD_BOOTSTRAP` to `false`. The benefit of this is to reduce the number of running processes in the final container. @@ -401,6 +409,14 @@ On first run the SSH user is created with a generated password. If you require a ... ``` +If set to a valid container file path the value will be read from the file - this allows for setting the value securely when combined with an orchestration feature such as Docker Swarm secrets. + +``` +... + --env "SSH_USER_PASSWORD=/var/run/secrets/ssh_user_password" \ +... +``` + ##### SSH_USER_PASSWORD_HASHED If setting a password for the SSH user you might not want to store the plain text password value in the `SSH_USER_PASSWORD` environment variable. Setting `SSH_USER_PASSWORD_HASHED` to `true` indicates that the value stored in `SSH_USER_PASSWORD` should be treated as a crypt SHA-512 salted password hash. diff --git a/src/etc/systemd/system/centos-ssh@.service b/src/etc/systemd/system/centos-ssh@.service index 00c75e9..c75389f 100644 --- a/src/etc/systemd/system/centos-ssh@.service +++ b/src/etc/systemd/system/centos-ssh@.service @@ -51,7 +51,7 @@ Environment="DOCKER_USER=jdeathe" Environment="DOCKER_IMAGE_NAME=centos-ssh" Environment="DOCKER_CONTAINER_OPTS=" Environment="DOCKER_IMAGE_PACKAGE_PATH=/var/opt/scmi/packages" -Environment="DOCKER_IMAGE_TAG=1.8.3" +Environment="DOCKER_IMAGE_TAG=1.8.4" Environment="DOCKER_PORT_MAP_TCP_22=2020" Environment="SSH_AUTHORIZED_KEYS=" Environment="SSH_AUTOSTART_SSHD=true" diff --git a/src/usr/sbin/sshd-bootstrap b/src/usr/sbin/sshd-bootstrap index 3704bae..72bc829 100644 --- a/src/usr/sbin/sshd-bootstrap +++ b/src/usr/sbin/sshd-bootstrap @@ -168,13 +168,20 @@ function get_ssh_authorized_keys () local VALUE="${SSH_AUTHORIZED_KEYS:-}" local PATTERN_BASE64='^[A-Za-z0-9/+=]*$' - if [[ -z ${VALUE} ]] && [[ -s ${DEFAULT_PATH} ]]; then + if [[ -z ${VALUE} ]] \ + && [[ -s ${DEFAULT_PATH} ]] + then VALUE="$(< "${DEFAULT_PATH}")" elif [[ -n ${VALUE} ]] \ - && [[ ${VALUE} =~ ${PATTERN_BASE64} ]]; then + && [[ ${VALUE} =~ ${PATTERN_BASE64} ]] + then VALUE="$( base64 -d -i <<< "${VALUE}" )" + elif [[ -n ${VALUE} ]] \ + && [[ -f ${VALUE} ]] + then + VALUE="$(< "${VALUE}")" fi printf -- "%s" "${VALUE}" @@ -481,6 +488,66 @@ function generate_ssh_host_keys () esac } +function set_ssh_user_password () +{ + local PASSWORD="${2:-${SSH_USER_PASSWORD}}" + local PASSWORD_HASHED="${3:-${SSH_USER_PASSWORD_HASHED:-false}}" + local PASSWORD_LENGTH="${4:-16}" + local USER="${1:-${SSH_USER}}" + + # Get password from file + if [[ -n ${PASSWORD} ]] \ + && [[ -s ${PASSWORD} ]] + then + PASSWORD="$(< "${PASSWORD}")" + fi + + if ! is_valid_ssh_user "${USER}"; then + printf -- \ + 'ERROR: Invalid user\n' \ + >&2 + sleep 0.1 + + return 1 + else + if [[ ${PASSWORD_HASHED} == true ]]; then + if ! is_valid_ssh_user_password_hash "${PASSWORD}"; then + printf -- \ + 'ERROR: Invalid password - requires SHA-512 hash\n' \ + >&2 + sleep 0.1 + + return 1 + else + printf -- \ + '%s:%s\n' \ + "${USER}" \ + "${PASSWORD}" \ + | chpasswd -e + + return "${?}" + fi + else + if [[ -z ${PASSWORD} ]]; then + printf -- \ + 'ERROR: Invalid password - empty\n' \ + >&2 + sleep 0.1 + + return 1 + else + printf -- \ + '%s:%s\n' \ + "${USER}" \ + "${PASSWORD}" \ + | chpasswd + + return "${?}" + fi + fi + fi +} + OPTS_SSH_INHERIT_ENVIRONMENT="${SSH_INHERIT_ENVIRONMENT:-false}" OPTS_SSH_USER="$( get_ssh_user app-admin @@ -592,23 +659,35 @@ if [[ ! -d ${OPTS_SSH_USER_HOME}/.ssh ]]; then ) & PIDS[1]=${!} - # Set SSH user password + # Get password from file + if [[ -n ${SSH_USER_PASSWORD} ]] \ + && [[ -s ${SSH_USER_PASSWORD} ]] + then + SSH_USER_PASSWORD="$(< "${SSH_USER_PASSWORD}")" + OPTS_SSH_USER_PASSWORD="${SSH_USER_PASSWORD}" + fi + + # Hashed password must use SHA-512 hashing algorithm if [[ ${OPTS_SSH_USER_PASSWORD_HASHED} == true ]] \ - && [[ -n ${SSH_USER_PASSWORD} ]]; then - # Hashed password must use SHA-512 hashing algorithm - if ! is_valid_ssh_user_password_hash "${SSH_USER_PASSWORD}"; then - echo "ERROR: Password hash not SHA-512 - setting new password." - - SSH_USER_PASSWORD= - OPTS_SSH_USER_PASSWORD=$(get_password ${PASSWORD_LENGTH}) - echo "${OPTS_SSH_USER}:${OPTS_SSH_USER_PASSWORD}" | chpasswd - else - echo "${OPTS_SSH_USER}:${SSH_USER_PASSWORD}" | chpasswd -e - fi - else - echo "${OPTS_SSH_USER}:${OPTS_SSH_USER_PASSWORD}" | chpasswd + && ! is_valid_ssh_user_password_hash "${OPTS_SSH_USER_PASSWORD}" + then + printf -- \ + "ERROR: Password hash not SHA-512 - setting new password.\n" + sleep 0.1 + + SSH_USER_PASSWORD= + OPTS_SSH_USER_PASSWORD="$( + get_password ${PASSWORD_LENGTH} + )" + OPTS_SSH_USER_PASSWORD_HASHED=false fi + set_ssh_user_password \ + "${OPTS_SSH_USER}" \ + "${OPTS_SSH_USER_PASSWORD}" \ + "${OPTS_SSH_USER_PASSWORD_HASHED}" \ + "${PASSWORD_LENGTH}" + # SFTP users if [[ ${OPTS_SSH_USER_FORCE_SFTP} == true ]]; then SSHD_COMMAND=SFTP diff --git a/test/fixture/config/authorized_keys b/test/fixture/config/authorized_keys new file mode 100644 index 0000000..0ff42fc --- /dev/null +++ b/test/fixture/config/authorized_keys @@ -0,0 +1,2 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHzd+mYXSGLQSZwUJWwYKOBI6I7mhHh3Ow0DbTQvQUHhkDcQVrqjpBKZq8SsnEK4ExyJeRQ5TtT5VqolfVG6LlaT3yxXmJxx75Y8oypFAKz7oCOQCJugqubAkx2YpT6KO7d6UOPqOuLpmFi3t+Ryf49TbNljSes1sbSCxUDlTekxG+It+/eZUqXWudornYJCfmMe1UAYtRwMKiyDWqU1LN1Kb2zVR76YpjMxK9IiEJjhpZS/R70zxGEijFrL+Rv4Bx8y/UjAVVtTkvpaEajx7pMnd0fsfnUwhYLoVHgG5ZdHAYSRy8j/0+mpeVEsTQZJAnegUbVNuBEc3x2b1xgkS3 test_key_1 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD/sMi/JkrYtXVi6+pYuwvsUxLDXowp3okvK2+2qqnPA6nWGBu/LSSQZnYmHZYyhcbRKrdscnsbM0jfmU0cKf/lGiRRK1YfUepomtRWVBxzA3mvBu+qmbuU/PDHfJJxB19HHuc6a8/NPOZINNMGIZg91W79bW0gXIEh3+PiNVvuelOdFJdvKSWtElJlMk/ll7dS1vpwJ9iZ8EPbalSzEZ30SzqG8Xg4VMhOn9ybnvcNWGJVCg4yughmdVA32H2+rj6qis4AJdQYUNoh9kBK/XUoggRV7LEaeeyjMWPn6GxM4yM7IYkstpilbKiPIQjw9EDTN4FdIJ/LcescfwDT7KZ test_key_2 diff --git a/test/fixture/secrets/ssh_user_password b/test/fixture/secrets/ssh_user_password new file mode 100644 index 0000000..2744a17 --- /dev/null +++ b/test/fixture/secrets/ssh_user_password @@ -0,0 +1 @@ +Insecure_Passw0rd£ \ No newline at end of file diff --git a/test/fixture/secrets/ssh_user_password_hashed b/test/fixture/secrets/ssh_user_password_hashed new file mode 100644 index 0000000..e5f8c75 --- /dev/null +++ b/test/fixture/secrets/ssh_user_password_hashed @@ -0,0 +1 @@ +$6$pepper$g5/OhofGtHVo3wqRgVHFQrJDyK0mV9bDpF5HP964wuIkQ7MXuYq1KRTmShaUmTQW3ZRsjw2MjC1LNPh5HMcrY0 \ No newline at end of file diff --git a/test/shpec/operation_shpec.sh b/test/shpec/operation_shpec.sh index 174d338..46cb2b5 100644 --- a/test/shpec/operation_shpec.sh +++ b/test/shpec/operation_shpec.sh @@ -595,84 +595,169 @@ function test_custom_ssh_configuration () end describe "Configure multiple public keys" - __terminate_container \ - ssh.pool-1.1.1 \ - &> /dev/null + describe "Base64 method" + __terminate_container \ + ssh.pool-1.1.1 \ + &> /dev/null - docker run \ - --detach \ - --name ssh.pool-1.1.1 \ - --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ - --env "SSH_AUTHORIZED_KEYS=${PUBLIC_KEY_ID_RSA_TEST_COMBINED_BASE64}" \ - --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ - jdeathe/centos-ssh:latest \ - &> /dev/null + docker run \ + --detach \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_AUTHORIZED_KEYS=${PUBLIC_KEY_ID_RSA_TEST_COMBINED_BASE64}" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest \ + &> /dev/null - container_port_22="$( - __get_container_port \ + container_port_22="$( + __get_container_port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + + if ! __is_container_ready \ ssh.pool-1.1.1 \ - 22/tcp - )" + ${STARTUP_TIME} \ + "/usr/sbin/sshd " \ + "grep \ + '^Server listening on 0\.0\.0\.0 port 22\.' \ + /var/log/secure" + then + exit 1 + fi - if ! __is_container_ready \ - ssh.pool-1.1.1 \ - ${STARTUP_TIME} \ - "/usr/sbin/sshd " \ - "grep \ - '^Server listening on 0\.0\.0\.0 port 22\.' \ - /var/log/secure" - then - exit 1 - fi + it "Can set multiple keys." + user_home="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_test_1 \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\${HOME}" + )" - it "Can set multiple keys." - user_home="$( - ssh -q \ - -p ${container_port_22} \ - -i ${TEST_DIRECTORY}/fixture/id_rsa_test_1 \ - -o StrictHostKeyChecking=no \ - -o LogLevel=error \ - app-admin@${DOCKER_HOSTNAME} \ - -- printf \ - '%s\\n' \ - "\${HOME}" - )" + user_home+=":" - user_home+=":" + user_home+="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_test_2 \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\${HOME}" + )" - user_home+="$( - ssh -q \ - -p ${container_port_22} \ - -i ${TEST_DIRECTORY}/fixture/id_rsa_test_2 \ - -o StrictHostKeyChecking=no \ - -o LogLevel=error \ - app-admin@${DOCKER_HOSTNAME} \ - -- printf \ - '%s\\n' \ - "\${HOME}" - )" + assert equal \ + "${user_home}" \ + "/home/app-admin:/home/app-admin" + end - assert equal \ - "${user_home}" \ - "/home/app-admin:/home/app-admin" + it "Logs the key signatures." + user_key_signature="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^45:46:b0:ef:a5:e3:c9:6f:1e:66:94:ba:e1:fd:df:65$/ { print $1; }' + )" + + user_key_signature+=" " + + user_key_signature+="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^b3:2e:5d:8c:76:d3:c7:24:13:a3:4f:6f:4d:a2:31:9c$/ { print $1; }' + )" + + assert equal \ + "${user_key_signature}" \ + "${PUBLIC_KEY_ID_RSA_TEST_1_SIGNATURE} ${PUBLIC_KEY_ID_RSA_TEST_2_SIGNATURE}" + end end - it "Logs the key signatures." - user_key_signature="$( - docker logs ssh.pool-1.1.1 \ - | awk '/^45:46:b0:ef:a5:e3:c9:6f:1e:66:94:ba:e1:fd:df:65$/ { print $1; }' + describe "File path method" + __terminate_container \ + ssh.pool-1.1.1 \ + &> /dev/null + + docker run \ + --detach \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_AUTHORIZED_KEYS=/var/run/config/authorized_keys" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + --volume ${PWD}/${TEST_DIRECTORY}/fixture/config:/var/run/config:ro \ + jdeathe/centos-ssh:latest \ + &> /dev/null + + container_port_22="$( + __get_container_port \ + ssh.pool-1.1.1 \ + 22/tcp )" - user_key_signature+=" " + if ! __is_container_ready \ + ssh.pool-1.1.1 \ + ${STARTUP_TIME} \ + "/usr/sbin/sshd " \ + "grep \ + '^Server listening on 0\.0\.0\.0 port 22\.' \ + /var/log/secure" + then + exit 1 + fi - user_key_signature+="$( - docker logs ssh.pool-1.1.1 \ - | awk '/^b3:2e:5d:8c:76:d3:c7:24:13:a3:4f:6f:4d:a2:31:9c$/ { print $1; }' - )" + it "Can set multiple keys." + user_home="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_test_1 \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\${HOME}" + )" - assert equal \ - "${user_key_signature}" \ - "${PUBLIC_KEY_ID_RSA_TEST_1_SIGNATURE} ${PUBLIC_KEY_ID_RSA_TEST_2_SIGNATURE}" + user_home+=":" + + user_home+="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_test_2 \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\${HOME}" + )" + + assert equal \ + "${user_home}" \ + "/home/app-admin:/home/app-admin" + end + + it "Logs the key signatures." + user_key_signature="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^45:46:b0:ef:a5:e3:c9:6f:1e:66:94:ba:e1:fd:df:65$/ { print $1; }' + )" + + user_key_signature+=" " + + user_key_signature+="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^b3:2e:5d:8c:76:d3:c7:24:13:a3:4f:6f:4d:a2:31:9c$/ { print $1; }' + )" + + assert equal \ + "${user_key_signature}" \ + "${PUBLIC_KEY_ID_RSA_TEST_1_SIGNATURE} ${PUBLIC_KEY_ID_RSA_TEST_2_SIGNATURE}" + end end end @@ -967,17 +1052,76 @@ function test_custom_ssh_configuration () "/home/app-admin" end - # TODO - # it "Will redact the value of SSH_USER_PASSWORD after bootstrap." - # user_password="$( - # docker exec ssh.pool-1.1.1 env \ - # | grep '^SSH_USER_PASSWORD=' - # )" - # - # assert equal \ - # "${user_password/SSH_USER_PASSWORD=/}" \ - # "${REDACTED_VALUE}" - # end + it "Logs a redacted value." + user_password="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^password :.*$/ { print $0; }' + )" + + assert equal \ + "${user_password/password : /}" \ + "${REDACTED_VALUE}" + end + + # Reset sudo configuration + docker exec ssh.pool-1.1.1 \ + bash -c 'rm -f /etc/sudoers.d/no_lecture' + end + + describe "Configure secret password" + __terminate_container \ + ssh.pool-1.1.1 \ + &> /dev/null + + docker run \ + --detach \ + --name ssh.pool-1.1.1 \ + --env "SSH_USER_PASSWORD=/var/run/secrets/ssh_user_password" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + --volume ${PWD}/${TEST_DIRECTORY}/fixture/secrets:/var/run/secrets:ro \ + jdeathe/centos-ssh:latest \ + &> /dev/null + + # Prevent sudo lecture output when testing the sudo password + docker exec ssh.pool-1.1.1 \ + bash -c 'echo "Defaults lecture_file = /dev/null" > /etc/sudoers.d/no_lecture' + + container_port_22="$( + __get_container_port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + + if ! __is_container_ready \ + ssh.pool-1.1.1 \ + ${STARTUP_TIME} \ + "/usr/sbin/sshd " \ + "grep \ + '^Server listening on 0\.0\.0\.0 port 22\.' \ + /var/log/secure" + then + exit 1 + fi + + it "Can set a plain text password." + user_home="$( + echo 'Insecure_Passw0rd£' \ + | ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- sudo -p "[password_test]" -S \ + printf \ + '%s\\n' \ + "\${HOME}" + )" + + assert equal \ + "${user_home}" \ + "/home/app-admin" + end it "Logs a redacted value." user_password="$( @@ -1050,17 +1194,73 @@ function test_custom_ssh_configuration () "/home/app-admin" end - # TODO - # it "Will redact the SSH_USER_PASSWORD environment variable after bootstrap." - # user_password="$( - # docker exec ssh.pool-1.1.1 env \ - # | grep '^SSH_USER_PASSWORD=' - # )" - # - # assert equal \ - # "${user_password/SSH_USER_PASSWORD=/}" \ - # "${REDACTED_VALUE}" - # end + it "Logs a redacted value." + user_password="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^password :.*$/ { print $0; }' + )" + + assert equal \ + "${user_password/password : /}" \ + "${REDACTED_VALUE}" + end + end + + describe "Configure secret hashed password" + __terminate_container \ + ssh.pool-1.1.1 \ + &> /dev/null + + docker run \ + --detach \ + --name ssh.pool-1.1.1 \ + --env "SSH_USER_PASSWORD=/var/run/secrets/ssh_user_password_hashed" \ + --env "SSH_USER_PASSWORD_HASHED=true" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + --volume ${PWD}/${TEST_DIRECTORY}/fixture/secrets:/var/run/secrets:ro \ + jdeathe/centos-ssh:latest \ + &> /dev/null + + # Prevent sudo lecture output when testing the sudo password + docker exec ssh.pool-1.1.1 \ + bash -c 'echo "Defaults lecture_file = /dev/null" > /etc/sudoers.d/no_lecture' + + container_port_22="$( + __get_container_port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + + if ! __is_container_ready \ + ssh.pool-1.1.1 \ + ${STARTUP_TIME} \ + "/usr/sbin/sshd " \ + "grep \ + '^Server listening on 0\.0\.0\.0 port 22\.' \ + /var/log/secure" + then + exit 1 + fi + + it "Can set a hashed password." + user_home="$( + echo 'Passw0rd!' \ + | ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- sudo -p "[password_test]" -S \ + printf \ + '%s\\n' \ + "\${HOME}" + )" + + assert equal \ + "${user_home}" \ + "/home/app-admin" + end it "Logs a redacted value." user_password="$(