diff --git a/CHANGELOG.md b/CHANGELOG.md index de0c87c..e778cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ Summary of release changes for Version 2 - CentOS-7 +### 2.1.5 - 2016-12-15 + +- Adds updated `sudo`, `openssh`, `yum-plugin-versionlock` and `xz` packages. +- Adds functional tests using [shpec](https://github.com/rylnd/shpec). To run all tests, [install `shpec`](https://github.com/rylnd/shpec#installation) and run with `make test`. +- Adds support for running tests on Ubuntu. _Note: May require some additional setup prevent warnings about locale._ + + ``` + sudo locale-gen en_US.UTF-8; sudo dpkg-reconfigure locales + export LANG=en_US.UTF-8; unset LANGUAGE LC_ALL LC_CTYPE + ``` +- Adds correction to examples and test usage of the `sftp` command. +- Adds a "better practices" example of password hash generation in the `README.md`. +- Adds minor code style changes to the `Makefile`. + ### 2.1.4 - 2016-12-04 - Adds correct Makefile usage instructions for 'build' target. diff --git a/Dockerfile b/Dockerfile index 33badea..43a7a70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,13 +24,13 @@ RUN rpm --rebuilddb \ epel-release \ https://centos7.iuscommunity.org/ius-release.rpm \ vim-minimal-7.4.160-1.el7 \ - xz-5.1.2-12alpha.el7.x86_64 \ - sudo-1.8.6p7-17.el7_2 \ - openssh-6.6.1p1-25.el7_2 \ - openssh-server-6.6.1p1-25.el7_2 \ - openssh-clients-6.6.1p1-25.el7_2 \ + xz-5.2.2-1.el7 \ + sudo-1.8.6p7-21.el7_3 \ + openssh-6.6.1p1-31.el7 \ + openssh-server-6.6.1p1-31.el7 \ + openssh-clients-6.6.1p1-31.el7 \ python-setuptools-0.9.8-4.el7 \ - yum-plugin-versionlock-1.1.31-34.el7 \ + yum-plugin-versionlock-1.1.31-40.el7 \ && yum versionlock add \ vim-minimal \ xz \ @@ -151,7 +151,7 @@ ENV SSH_AUTHORIZED_KEYS="" \ # ----------------------------------------------------------------------------- # Set image metadata # ----------------------------------------------------------------------------- -ARG RELEASE_VERSION="2.1.4" +ARG RELEASE_VERSION="2.1.5" LABEL \ install="docker run \ --rm \ diff --git a/Makefile b/Makefile index c0489ad..250c823 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ Targets: start Start the container in the created state. stop Stop the container when in a running state. terminate Unpause, stop and remove the container. + test Run all test cases. unpause Unpause the container when in a paused state. Variables: @@ -108,10 +109,15 @@ PREFIX_SUB_STEP_POSITIVE := $(shell \ # Package prerequisites docker := $(shell \ - type -p docker \ + command -v docker \ ) xz := $(shell \ - type -p xz \ + command -v xz \ +) + +# Testing prerequisites +shpec := $(shell \ + command -v shpec \ ) # Used to test docker host is accessible @@ -131,6 +137,7 @@ get-docker-info := $(shell \ _require-docker-image-tag \ _require-docker-release-tag \ _require-package-path \ + _test-prerequisites \ _usage \ all \ build \ @@ -155,6 +162,7 @@ get-docker-info := $(shell \ start \ stop \ terminate \ + test \ unpause _prerequisites: @@ -172,78 +180,83 @@ endif _require-docker-container: @ if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) This operation requires the $(DOCKER_NAME) docker container."; \ - echo "$(PREFIX_SUB_STEP) Try installing it with: make install"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)This operation requires the $(DOCKER_NAME) docker container."; \ + echo "$(PREFIX_SUB_STEP)Try installing it with: make install"; \ + exit 1; \ + fi _require-docker-container-not: @ if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) This operation requires the $(DOCKER_NAME) docker container be removed (or renamed)."; \ - echo "$(PREFIX_SUB_STEP) Try removing it with: make rm"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)This operation requires the $(DOCKER_NAME) docker container be removed (or renamed)."; \ + echo "$(PREFIX_SUB_STEP)Try removing it with: make rm"; \ + exit 1; \ + fi _require-docker-container-not-status-paused: @ if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=paused") ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) This operation requires the $(DOCKER_NAME) docker container to be unpaused."; \ - echo "$(PREFIX_SUB_STEP) Try unpausing it with: make unpause"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)This operation requires the $(DOCKER_NAME) docker container to be unpaused."; \ + echo "$(PREFIX_SUB_STEP)Try unpausing it with: make unpause"; \ + exit 1; \ + fi _require-docker-container-status-created: @ if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=created") ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) This operation requires the $(DOCKER_NAME) docker container to be created."; \ - echo "$(PREFIX_SUB_STEP) Try installing it with: make install"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)This operation requires the $(DOCKER_NAME) docker container to be created."; \ + echo "$(PREFIX_SUB_STEP)Try installing it with: make install"; \ + exit 1; \ + fi _require-docker-container-status-exited: @ if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=exited") ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) This operation requires the $(DOCKER_NAME) docker container to be exited."; \ - echo "$(PREFIX_SUB_STEP) Try stopping it with: make stop"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)This operation requires the $(DOCKER_NAME) docker container to be exited."; \ + echo "$(PREFIX_SUB_STEP)Try stopping it with: make stop"; \ + exit 1; \ + fi _require-docker-container-status-paused: @ if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=paused") ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) This operation requires the $(DOCKER_NAME) docker container to be paused."; \ - echo "$(PREFIX_SUB_STEP) Try pausing it with: make pause"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)This operation requires the $(DOCKER_NAME) docker container to be paused."; \ + echo "$(PREFIX_SUB_STEP)Try pausing it with: make pause"; \ + exit 1; \ + fi _require-docker-container-status-running: @ if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) This operation requires the $(DOCKER_NAME) docker container to be running."; \ - echo "$(PREFIX_SUB_STEP) Try starting it with: make start"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)This operation requires the $(DOCKER_NAME) docker container to be running."; \ + echo "$(PREFIX_SUB_STEP)Try starting it with: make start"; \ + exit 1; \ + fi _require-docker-image-tag: @ if [[ -z $$(if [[ $(DOCKER_IMAGE_TAG) =~ $(DOCKER_IMAGE_TAG_PATTERN) ]]; then echo $(DOCKER_IMAGE_TAG); else echo ''; fi) ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) Invalid DOCKER_IMAGE_TAG value: $(DOCKER_IMAGE_TAG)"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)Invalid DOCKER_IMAGE_TAG value: $(DOCKER_IMAGE_TAG)"; \ + exit 1; \ + fi _require-docker-release-tag: @ if [[ -z $$(if [[ $(DOCKER_IMAGE_TAG) =~ $(DOCKER_IMAGE_RELEASE_TAG_PATTERN) ]]; then echo $(DOCKER_IMAGE_TAG); else echo ''; fi) ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) Invalid DOCKER_IMAGE_TAG value: $(DOCKER_IMAGE_TAG)"; \ - echo "$(PREFIX_SUB_STEP) A release tag is required for this operation."; \ - exit 1; \ - fi + echo "$(PREFIX_STEP_NEGATIVE)Invalid DOCKER_IMAGE_TAG value: $(DOCKER_IMAGE_TAG)"; \ + echo "$(PREFIX_SUB_STEP)A release tag is required for this operation."; \ + exit 1; \ + fi _require-package-path: @ if [[ -n $(DIST_PATH) ]] && [[ ! -d $(DIST_PATH) ]]; then \ - echo "$(PREFIX_STEP) Creating package directory"; \ - mkdir -p $(DIST_PATH); \ - fi; \ - if [[ ! $${?} -eq 0 ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) Failed to make package path: $(DIST_PATH)"; \ - exit 1; \ - elif [[ -z $(DIST_PATH) ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) Undefined DIST_PATH"; \ - exit 1; \ - fi + echo "$(PREFIX_STEP)Creating package directory"; \ + mkdir -p $(DIST_PATH); \ + fi; \ + if [[ ! $${?} -eq 0 ]]; then \ + echo "$(PREFIX_STEP_NEGATIVE)Failed to make package path: $(DIST_PATH)"; \ + exit 1; \ + elif [[ -z $(DIST_PATH) ]]; then \ + echo "$(PREFIX_STEP_NEGATIVE)Undefined DIST_PATH"; \ + exit 1; \ + fi + +_test-prerequisites: +ifeq ($(shpec),) + $(error "Please install shpec.") +endif _usage: @: $(info $(USAGE)) @@ -252,81 +265,81 @@ all: _prerequisites | build images install start ps # build NO_CACHE=[{false,true}] build: _prerequisites _require-docker-image-tag - @ echo "$(PREFIX_STEP) Building $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" + @ echo "$(PREFIX_STEP)Building $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" @ if [[ $(NO_CACHE) == true ]]; then \ - echo "$(PREFIX_SUB_STEP) Skipping cache"; \ - fi + echo "$(PREFIX_SUB_STEP)Skipping cache"; \ + fi @ $(docker) build \ - --no-cache=$(NO_CACHE) \ - -t $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) \ - .; \ - if [[ $${?} -eq 0 ]]; then \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Build complete"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Build error"; \ - exit 1; \ - fi + --no-cache=$(NO_CACHE) \ + -t $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) \ + .; \ + if [[ $${?} -eq 0 ]]; then \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Build complete"; \ + else \ + echo "$(PREFIX_SUB_STEP_NEGATIVE)Build error"; \ + exit 1; \ + fi clean: _prerequisites | terminate rmi create: _prerequisites _require-docker-container-not - @ echo "$(PREFIX_STEP) Creating container" + @ echo "$(PREFIX_STEP)Creating container" @ set -x; \ - $(docker) create \ - $(DOCKER_CONTAINER_PARAMETERS) \ - $(DOCKER_PUBLISH) \ - $(DOCKER_CONTAINER_OPTS) \ - $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 1> /dev/null; + $(docker) create \ + $(DOCKER_CONTAINER_PARAMETERS) \ + $(DOCKER_PUBLISH) \ + $(DOCKER_CONTAINER_OPTS) \ + $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 1> /dev/null; @ if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=created") ]]; then \ - echo "$(PREFIX_SUB_STEP) $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=created")"; \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Container created"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Container creation failed"; \ - exit 1; \ - fi + echo "$(PREFIX_SUB_STEP)$$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=created")"; \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Container created"; \ + else \ + echo "$(PREFIX_SUB_STEP_NEGATIVE)Container creation failed"; \ + exit 1; \ + fi dist: _prerequisites _require-docker-release-tag _require-package-path | pull $(eval $@_dist_path := $(realpath \ $(DIST_PATH) \ )) @ if [[ -s $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz ]]; then \ - echo "$(PREFIX_STEP) Saving package"; \ - echo "$(PREFIX_SUB_STEP) Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Package already exists"; \ + echo "$(PREFIX_STEP)Saving package"; \ + echo "$(PREFIX_SUB_STEP)Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Package already exists"; \ + else \ + echo "$(PREFIX_STEP)Saving package"; \ + $(docker) save \ + $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) | \ + $(xz) -9 > \ + $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz; \ + if [[ $${?} -eq 0 ]]; then \ + echo "$(PREFIX_SUB_STEP)Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Package saved"; \ else \ - echo "$(PREFIX_STEP) Saving package"; \ - $(docker) save \ - $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) | \ - $(xz) -9 > \ - $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz; \ - if [[ $${?} -eq 0 ]]; then \ - echo "$(PREFIX_SUB_STEP) Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Package saved"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Package save error"; \ - exit 1; \ - fi; \ - fi + echo "$(PREFIX_SUB_STEP_NEGATIVE)Package save error"; \ + exit 1; \ + fi; \ + fi distclean: _prerequisites _require-docker-release-tag _require-package-path | clean $(eval $@_dist_path := $(realpath \ $(DIST_PATH) \ )) @ if [[ -e $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz ]]; then \ - echo "$(PREFIX_STEP) Deleting package"; \ - echo "$(PREFIX_SUB_STEP) Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ - find $($@_dist_path) \ - -name $(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz \ - -delete; \ - if [[ ! -e $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz ]]; then \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Package cleanup complete"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Package cleanup failed"; \ - exit 1; \ - fi; \ + echo "$(PREFIX_STEP)Deleting package"; \ + echo "$(PREFIX_SUB_STEP)Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ + find $($@_dist_path) \ + -name $(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz \ + -delete; \ + if [[ ! -e $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz ]]; then \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Package cleanup complete"; \ else \ - echo "$(PREFIX_STEP) Package cleanup skipped"; \ - fi + echo "$(PREFIX_SUB_STEP_NEGATIVE)Package cleanup failed"; \ + exit 1; \ + fi; \ + else \ + echo "$(PREFIX_STEP)Package cleanup skipped"; \ + fi exec: _prerequisites @ $(docker) exec -it $(DOCKER_NAME) $(filter-out $@, $(MAKECMDGOALS)) @@ -334,7 +347,7 @@ exec: _prerequisites images: _prerequisites @ $(docker) images \ - $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG); + $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG); help: _usage @@ -351,142 +364,149 @@ load: _prerequisites _require-docker-release-tag _require-package-path $(eval $@_dist_path := $(realpath \ $(DIST_PATH) \ )) - @ echo "$(PREFIX_STEP) Loading image from package"; \ - echo "$(PREFIX_SUB_STEP) Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ - if [[ ! -s $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz ]]; then \ - echo "$(PREFIX_STEP_NEGATIVE) Package not found"; \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) To create a package try: DOCKER_IMAGE_TAG=\"$(DOCKER_IMAGE_TAG)\" make dist"; \ - exit 1; \ - else \ - $(xz) -dc $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz | \ - $(docker) load; \ - echo "$(PREFIX_SUB_STEP) $$( if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi; )"; \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Image loaded"; \ - fi + @ echo "$(PREFIX_STEP)Loading image from package"; \ + echo "$(PREFIX_SUB_STEP)Package path: $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz"; \ + if [[ ! -s $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz ]]; then \ + echo "$(PREFIX_STEP_NEGATIVE)Package not found"; \ + echo "$(PREFIX_SUB_STEP_NEGATIVE)To create a package try: DOCKER_IMAGE_TAG=\"$(DOCKER_IMAGE_TAG)\" make dist"; \ + exit 1; \ + else \ + $(xz) -dc $($@_dist_path)/$(DOCKER_IMAGE_NAME).$(DOCKER_IMAGE_TAG).tar.xz | \ + $(docker) load; \ + echo "$(PREFIX_SUB_STEP)$$(if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi;)"; \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Image loaded"; \ + fi pause: _prerequisites _require-docker-container-status-running - @ echo "$(PREFIX_STEP) Pausing container" + @ echo "$(PREFIX_STEP)Pausing container" @ $(docker) pause $(DOCKER_NAME) 1> /dev/null - @ echo "$(PREFIX_SUB_STEP_POSITIVE) Container paused" + @ echo "$(PREFIX_SUB_STEP_POSITIVE)Container paused" pull: _prerequisites _require-docker-image-tag - @ echo "$(PREFIX_STEP) Pulling image from registry" + @ echo "$(PREFIX_STEP)Pulling image from registry" @ $(docker) pull \ - $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG); \ - if [[ $${?} -eq 0 ]]; then \ - echo "$(PREFIX_SUB_STEP) $$( if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi; )"; \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Image pulled"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Error pulling image"; \ - exit 1; \ - fi + $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG); \ + if [[ $${?} -eq 0 ]]; then \ + echo "$(PREFIX_SUB_STEP)$$(if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi;)"; \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Image pulled"; \ + else \ + echo "$(PREFIX_SUB_STEP_NEGATIVE)Error pulling image"; \ + exit 1; \ + fi ps: _prerequisites _require-docker-container @ $(docker) ps -as --filter "name=$(DOCKER_NAME)"; restart: _prerequisites _require-docker-container _require-docker-container-not-status-paused - @ echo "$(PREFIX_STEP) Restarting container" + @ echo "$(PREFIX_STEP)Restarting container" @ $(docker) restart $(DOCKER_NAME) 1> /dev/null - @ echo "$(PREFIX_SUB_STEP_POSITIVE) Container restarted" + @ echo "$(PREFIX_SUB_STEP_POSITIVE)Container restarted" rm: _prerequisites _require-docker-container-not-status-paused @ if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ - echo "$(PREFIX_STEP) Container removal skipped"; \ + echo "$(PREFIX_STEP)Container removal skipped"; \ + else \ + echo "$(PREFIX_STEP)Removing container"; \ + $(docker) rm -f $(DOCKER_NAME); \ + if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Container removed"; \ else \ - echo "$(PREFIX_STEP) Removing container"; \ - $(docker) rm -f $(DOCKER_NAME); \ - if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Container removed"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Container removal failed"; \ - exit 1; \ - fi; \ - fi + echo "$(PREFIX_SUB_STEP_NEGATIVE)Container removal failed"; \ + exit 1; \ + fi; \ + fi rmi: _prerequisites _require-docker-image-tag _require-docker-container-not - @ if [[ -n $$( if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi; ) ]]; then \ - echo "$(PREFIX_STEP) Untagging image"; \ - echo "$(PREFIX_SUB_STEP) $$( if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi; ) : $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"; \ - $(docker) rmi \ - $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 1> /dev/null; \ - if [[ $${?} -eq 0 ]]; then \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Image untagged"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Error untagging image"; \ - exit 1; \ - fi; \ + @ if [[ -n $$(if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi;) ]]; then \ + echo "$(PREFIX_STEP)Untagging image"; \ + echo "$(PREFIX_SUB_STEP)$$(if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)); fi;) : $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"; \ + $(docker) rmi \ + $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 1> /dev/null; \ + if [[ $${?} -eq 0 ]]; then \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Image untagged"; \ else \ - echo "$(PREFIX_STEP) Untagging image skipped"; \ - fi + echo "$(PREFIX_SUB_STEP_NEGATIVE)Error untagging image"; \ + exit 1; \ + fi; \ + else \ + echo "$(PREFIX_STEP)Untagging image skipped"; \ + fi run: _prerequisites _require-docker-image-tag - @ echo "$(PREFIX_STEP) Running container" + @ echo "$(PREFIX_STEP)Running container" @ set -x; \ - $(docker) run \ - --detach \ - $(DOCKER_CONTAINER_PARAMETERS) \ - $(DOCKER_PUBLISH) \ - $(DOCKER_CONTAINER_OPTS) \ - $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 1> /dev/null; + $(docker) run \ + --detach \ + $(DOCKER_CONTAINER_PARAMETERS) \ + $(DOCKER_PUBLISH) \ + $(DOCKER_CONTAINER_OPTS) \ + $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) 1> /dev/null; @ if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ - echo "$(PREFIX_SUB_STEP) $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running")"; \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Container running"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Container run failed"; \ - exit 1; \ - fi + echo "$(PREFIX_SUB_STEP)$$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running")"; \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Container running"; \ + else \ + echo "$(PREFIX_SUB_STEP_NEGATIVE)Container run failed"; \ + exit 1; \ + fi start: _prerequisites _require-docker-container _require-docker-container-not-status-paused - @ echo "$(PREFIX_STEP) Starting container" + @ echo "$(PREFIX_STEP)Starting container" @ if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]] \ - && [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ - $(docker) start $(DOCKER_NAME) 1> /dev/null; \ - fi + && [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ + $(docker) start $(DOCKER_NAME) 1> /dev/null; \ + fi @ if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Container started"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Container start failed"; \ - exit 1; \ - fi + echo "$(PREFIX_SUB_STEP_POSITIVE)Container started"; \ + else \ + echo "$(PREFIX_SUB_STEP_NEGATIVE)Container start failed"; \ + exit 1; \ + fi stop: _prerequisites _require-docker-container-not-status-paused _require-docker-container-status-running - @ echo "$(PREFIX_STEP) Stopping container" + @ echo "$(PREFIX_STEP)Stopping container" @ if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ - $(docker) stop $(DOCKER_NAME) 1> /dev/null; \ - if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=exited") ]]; then \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Container stopped"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Error stopping container"; \ - exit 1; \ - fi; \ - fi + $(docker) stop $(DOCKER_NAME) 1> /dev/null; \ + if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=exited") ]]; then \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Container stopped"; \ + else \ + echo "$(PREFIX_SUB_STEP_NEGATIVE)Error stopping container"; \ + exit 1; \ + fi; \ + fi terminate: _prerequisites @ if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ - echo "$(PREFIX_STEP) Container termination skipped"; \ + echo "$(PREFIX_STEP)Container termination skipped"; \ + else \ + echo "$(PREFIX_STEP)Terminating container"; \ + if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=paused") ]]; then \ + echo "$(PREFIX_SUB_STEP)Unpausing container"; \ + $(docker) unpause $(DOCKER_NAME) 1> /dev/null; \ + fi; \ + if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ + echo "$(PREFIX_SUB_STEP)Stopping container"; \ + $(docker) stop $(DOCKER_NAME) 1> /dev/null; \ + fi; \ + if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ + echo "$(PREFIX_SUB_STEP)Removing container"; \ + $(docker) rm -f $(DOCKER_NAME) 1> /dev/null; \ + fi; \ + if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ + echo "$(PREFIX_SUB_STEP_POSITIVE)Container terminated"; \ else \ - echo "$(PREFIX_STEP) Terminating container"; \ - if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=paused") ]]; then \ - echo "$(PREFIX_SUB_STEP) Unpausing container"; \ - $(docker) unpause $(DOCKER_NAME) 1> /dev/null; \ - fi; \ - if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)" --filter "status=running") ]]; then \ - echo "$(PREFIX_SUB_STEP) Stopping container"; \ - $(docker) stop $(DOCKER_NAME) 1> /dev/null; \ - fi; \ - if [[ -n $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ - echo "$(PREFIX_SUB_STEP) Removing container"; \ - $(docker) rm -f $(DOCKER_NAME) 1> /dev/null; \ - fi; \ - if [[ -z $$($(docker) ps -aq --filter "name=$(DOCKER_NAME)") ]]; then \ - echo "$(PREFIX_SUB_STEP_POSITIVE) Container terminated"; \ - else \ - echo "$(PREFIX_SUB_STEP_NEGATIVE) Container termination failed"; \ - exit 1; \ - fi; \ - fi + echo "$(PREFIX_SUB_STEP_NEGATIVE)Container termination failed"; \ + exit 1; \ + fi; \ + fi + +test: _test-prerequisites + @ if [[ -z $$(if [[ -n $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):latest) ]]; then echo $$($(docker) images -q $(DOCKER_USER)/$(DOCKER_IMAGE_NAME):latest); else echo $$($(docker) images -q docker.io/$(DOCKER_USER)/$(DOCKER_IMAGE_NAME):latest); fi;) ]]; then \ + $(MAKE) build; \ + fi; + @ echo "$(PREFIX_STEP)Functional test"; + @ SHPEC_ROOT=$(SHPEC_ROOT) $(shpec); unpause: _prerequisites _require-docker-container-status-paused - @ echo "$(PREFIX_STEP) Unpausing container" + @ echo "$(PREFIX_STEP)Unpausing container" @ $(docker) unpause $(DOCKER_NAME) 1> /dev/null - @ echo "$(PREFIX_SUB_STEP_POSITIVE) Container unpaused" + @ echo "$(PREFIX_SUB_STEP_POSITIVE)Container unpaused" diff --git a/README.md b/README.md index 19c5e08..41dfe0e 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.7.4` OR `1.7.4` for the [1.7.4](https://github.com/jdeathe/centos-ssh/tree/1.7.4) release tag and `centos-7-2.1.4` OR `2.1.4` for the [2.1.4](https://github.com/jdeathe/centos-ssh/tree/2.1.4) 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.7.5` OR `1.7.5` for the [1.7.5](https://github.com/jdeathe/centos-ssh/tree/1.7.5) release tag and `centos-7-2.1.5` OR `2.1.5` for the [2.1.5](https://github.com/jdeathe/centos-ssh/tree/2.1.5) release tag. ### Tags and respective `Dockerfile` links -- `centos-7`,`centos-7-2.1.4`,`2.1.4` [(centos-7/Dockerfile)](https://github.com/jdeathe/centos-ssh/blob/centos-7/Dockerfile) -- `centos-6`,`centos-6-1.7.4`,`1.7.4` [(centos-6/Dockerfile)](https://github.com/jdeathe/centos-ssh/blob/centos-6/Dockerfile) +- `centos-7`,`centos-7-2.1.5`,`2.1.5` [(centos-7/Dockerfile)](https://github.com/jdeathe/centos-ssh/blob/centos-7/Dockerfile) +- `centos-6`,`centos-6-1.7.5`,`1.7.5` [(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. @@ -82,7 +82,9 @@ $ docker run -d \ Connect using the `sftp` command line client with the [insecure private key](https://github.com/mitchellh/vagrant/blob/master/keys/vagrant). ``` -$ sftp -p 2021 -i id_rsa_insecure \ +$ sftp -i id_rsa_insecure \ + -o Port=2021 \ + -o StrictHostKeyChecking=no \ app-admin@{docker-host-ip} ``` @@ -103,10 +105,10 @@ $ docker run \ --rm \ --privileged \ --volume /:/media/root \ - jdeathe/centos-ssh:2.1.4 \ + jdeathe/centos-ssh:2.1.5 \ /usr/sbin/scmi install \ --chroot=/media/root \ - --tag=2.1.4 \ + --tag=2.1.5 \ --name=ssh.pool-1.1.1 \ --setopt="--volume {{NAME}}.config-ssh:/etc/ssh" ``` @@ -120,10 +122,10 @@ $ docker run \ --rm \ --privileged \ --volume /:/media/root \ - jdeathe/centos-ssh:2.1.4 \ + jdeathe/centos-ssh:2.1.5 \ /usr/sbin/scmi uninstall \ --chroot=/media/root \ - --tag=2.1.4 \ + --tag=2.1.5 \ --name=ssh.pool-1.1.1 \ --setopt="--volume {{NAME}}.config-ssh:/etc/ssh" ``` @@ -137,10 +139,10 @@ $ docker run \ --rm \ --privileged \ --volume /:/media/root \ - jdeathe/centos-ssh:2.1.4 \ + jdeathe/centos-ssh:2.1.5 \ /usr/sbin/scmi install \ --chroot=/media/root \ - --tag=2.1.4 \ + --tag=2.1.5 \ --name=ssh.pool-1.1.1 \ --manager=systemd \ --register \ @@ -163,7 +165,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:2.1.4 + jdeathe/centos-ssh:2.1.5 ) --info" ``` @@ -173,7 +175,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:2.1.4 + jdeathe/centos-ssh:2.1.5 ) --name=ssh.pool-1.2.1" ``` @@ -183,7 +185,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:2.1.4 + jdeathe/centos-ssh:2.1.5 ) --name=ssh.pool-1.2.1" ``` @@ -196,7 +198,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:2.1.4 \ + jdeathe/centos-ssh:2.1.5 \ --info ``` @@ -205,14 +207,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:2.1.4 + jdeathe/centos-ssh:2.1.5 ``` Alternatively, you could use the `scmi` options `--name` or `-n` for naming the container. ``` $ sudo -E atomic install \ - jdeathe/centos-ssh:2.1.4 \ + jdeathe/centos-ssh:2.1.5 \ --name ssh.pool-1.3.1 ``` @@ -221,7 +223,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:2.1.4 + jdeathe/centos-ssh:2.1.5 ``` #### Using environment variables @@ -323,7 +325,7 @@ It may be desirable to prevent the startup of the sshd daemon and/or sshd-bootst ##### SSH_CHROOT_DIRECTORY -This option is only applicable when `SSH_USER_FORCE_SFTP` is set to `true`. When using the using the SFTP option the user is jailed into the ChrootDirectory. The value can contain the placeholders `%h` and `%u` which will be replaced with the values of `SSH_USER_HOME` and `SSH_USER` respectively. The default value of `%h` is the best choice in most cases but the user requires a sub-directory in their HOME directory which they have write access to. If no volume is mounted into the path of the SSH user's HOME directory the a directory named `_data` is created automatically. If you need the user to be able to write to their HOME directory they use an alternative value such as `/chroot/%u` so that the user's HOME path, (relative to the ChrootDirectory), becomes `/chroot/app-admin/home/app-admin` by default. +This option is only applicable when `SSH_USER_FORCE_SFTP` is set to `true`. When using the SFTP option the user is jailed into the ChrootDirectory. The value can contain the placeholders `%h` and `%u` which will be replaced with the values of `SSH_USER_HOME` and `SSH_USER` respectively. The default value of `%h` is the best choice in most cases but the user requires a sub-directory in their HOME directory which they have write access to. If no volume is mounted into the path of the SSH user's HOME directory then a directory named `_data` is created automatically. If you need the user to be able to write to their HOME directory then use an alternative value such as `/chroot/%u` so that the user's HOME path, (relative to the ChrootDirectory), becomes `/chroot/app-admin/home/app-admin` by default. ``` ... @@ -404,18 +406,14 @@ If setting a password for the SSH user you might not want to store the plain tex ###### Generating a crypt SHA-512 password hash -To generate a new hashed password string you can use the following method - given a password of "Passw0rd!" and a salt of "pepper". +To generate a hashed password string for the password `Passw0rd!`, use the following method. ``` -$ docker exec -it ssh.pool-1.1.1 \ - env \ - PASSWORD=Passw0rd! \ - PASSWORD_SALT=pepper \ - python -c "import crypt,os; print crypt.crypt(os.environ.get('PASSWORD'), '\$6\$' + os.environ.get('PASSWORD_SALT') + '\$')" +$ docker run --rm jdeathe/centos-ssh \ + env PASSWORD=Passw0rd! \ + python -c "import crypt,os; print crypt.crypt(os.environ.get('PASSWORD'))" ``` -The result should be the string: ```$6$pepper$g5/OhofGtHVo3wqRgVHFQrJDyK0mV9bDpF5HP964wuIkQ7MXuYq1KRTmShaUmTQW3ZRsjw2MjC1LNPh5HMcrY0``` - ##### SSH_USER_SHELL On first run the SSH user is created with a default shell of "/bin/bash". If you require a specific shell `SSH_USER_SHELL` can be used when running the container. You could use "/sbin/nologin" to prevent login with the user account. diff --git a/environment.mk b/environment.mk index 265a74c..18dd2e3 100644 --- a/environment.mk +++ b/environment.mk @@ -3,6 +3,7 @@ # ----------------------------------------------------------------------------- DOCKER_USER := jdeathe DOCKER_IMAGE_NAME := centos-ssh +SHPEC_ROOT := test/shpec # Tag validation patterns DOCKER_IMAGE_TAG_PATTERN := ^(latest|centos-[6-7]|((1|2|centos-(6-1|7-2))\.[0-9]+\.[0-9]+))$ diff --git a/etc/systemd/system/centos-ssh@.service b/etc/systemd/system/centos-ssh@.service index 05841a3..1b7bfab 100644 --- a/etc/systemd/system/centos-ssh@.service +++ b/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=2.1.4" +Environment="DOCKER_IMAGE_TAG=2.1.5" Environment="DOCKER_PORT_MAP_TCP_22=2020" Environment="SSH_AUTHORIZED_KEYS=" Environment="SSH_AUTOSTART_SSHD=true" diff --git a/test/fixture/id_rsa_insecure b/test/fixture/id_rsa_insecure new file mode 100644 index 0000000..7cf1a37 --- /dev/null +++ b/test/fixture/id_rsa_insecure @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI +w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP +kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2 +hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO +Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW +yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd +ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1 +Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf +TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK +iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A +sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf +4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP +cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk +EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN +CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX +3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG +YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj +3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+ +dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz +6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC +P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF +llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ +kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH ++vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ +NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s= +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/test/fixture/id_rsa_test_1 b/test/fixture/id_rsa_test_1 new file mode 100644 index 0000000..44f7cda --- /dev/null +++ b/test/fixture/id_rsa_test_1 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAx83fpmF0hi0EmcFCVsGCjgSOiO5oR4dzsNA200L0FB4ZA3EF +a6o6QSmavErJxCuBMciXkUOU7U+VaqJX1Rui5Wk98sV5icce+WPKMqRQCs+6AjkA +iboKrmwJMdmKU+iju3elDj6jri6ZhYt7fkcn+PU2zZY0nrNbG0gsVA5U3pMRviLf +v3mVKl1rnaK52CQn5jHtVAGLUcDCosg1qlNSzdSm9s1Ue+mKYzMSvSIhCY4aWUv0 +e9M8RhIoxay/kb+AcfMv1IwFVbU5L6WhGo8e6TJ3dH7H51MIWC6FR4BuWXRwGEkc +vI/9PpqXlRLE0GSQJ3oFG1TbgRHN8dm9cYJEtwIDAQABAoIBAAtqF3zJ1H5V1A10 +nPWX2H2EhSANfqeXdWSEgJ1RFeCQV516t3SPJQTT+6MGIsCyO488hmwmEDv+b+rH +m3zxNz0j5gYYgfj9Byf4K3T5LhlwBJBb8Uvs9O9B/MR8SQrCH5h2HVfS/EbXIq4e +ac+tAPuYBql4C0mFtY65cM7cgry0UFamRQJQBNXwbcWOilF0mlae6TNpKK9HIhIr +helHy/j2ioeSWLudOwXiKJlTYxHjGO7/rmbHt4RxhQ84reVcDN8j2GPAf8QxvnCn +2Du+ZqHi+cBU7cs0tteMFmrMABKWDL230WgOpBxwzyBQPnSmZmki/nvrXddokX6d +IEeHowECgYEA5pgfZDaZwof/ImnwW9HbRdC4Ad4rRobz44KrfVCPKJOuBI5lvkiz +qo158BEZvWkVNEWerxZPdPwEvz0P8lUgS3UuAaZLlpfsrm3hmU7J3niglRsxXfYS +w++bCLLv8IjP15tHBjkNGvvuW87BX8YBHrYJWRVIA5E9h608yepyR50CgYEA3dFS +jHO+g3UnVNOs5975TrfaNJnHOC5eSJvmj4snXH//As90sbCdWibfh6Fd+A1zyGUj +mkm++MhXJ9eJTncEzEplfG4w4DK9a/XZTZFVf9+ArZwgr/NEgNXHC20DtszxOQ0b +T2lAaSksiV+F/d5LtmXbXVQlVqcvkCuLhc6572MCgYAUUrPxkR5cFsBVuCCG9yfs +L0kIYRxPSvWTx6B1mvQDD4D2xdYRvzaWgYGNtvQdzXUg9kHWDJFULiH8+NS+9uGx +xNIZMx7WVu11Mhn/+QGxqc/9VDg1n8pnmmZ/jcW33Vb0GatY0Q5mom2PidlhJ4JR +wplwUH/YUKSroIkLAq6vwQKBgQCYYdqRvlncuTjB34Di8ZzXZRo0FIh1ofUHcIJd +jj0GILAxYA9MmoYejqH07Pg/Fg66VjC1J4BYLFkjd8BN9RmItnstlg1hl7nlVsln +rjXMWOBvY9hYx4gB8dqBkOyCQhxdExH15dpn4+9CmCrWkkX1Edg3Lz1PYB8erat1 +yuSU/QKBgA0gWAdcG6bNULfP/rabGNzj9AcKdPR2mzwvUHap33yFRWqDzDTqYV0F +FBhWx7Npe7yWMKg3XniG27snSI+e/zqvwsL//0pMwTAO/spHmfJB0sqLuXTYabFY +k1DUG1R+AY3nl71VOG+wqJVyzUvZ1hfJI3gDxhajksAgwWkJSPht +-----END RSA PRIVATE KEY----- diff --git a/test/fixture/id_rsa_test_1.pub b/test/fixture/id_rsa_test_1.pub new file mode 100644 index 0000000..ad725e4 --- /dev/null +++ b/test/fixture/id_rsa_test_1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHzd+mYXSGLQSZwUJWwYKOBI6I7mhHh3Ow0DbTQvQUHhkDcQVrqjpBKZq8SsnEK4ExyJeRQ5TtT5VqolfVG6LlaT3yxXmJxx75Y8oypFAKz7oCOQCJugqubAkx2YpT6KO7d6UOPqOuLpmFi3t+Ryf49TbNljSes1sbSCxUDlTekxG+It+/eZUqXWudornYJCfmMe1UAYtRwMKiyDWqU1LN1Kb2zVR76YpjMxK9IiEJjhpZS/R70zxGEijFrL+Rv4Bx8y/UjAVVtTkvpaEajx7pMnd0fsfnUwhYLoVHgG5ZdHAYSRy8j/0+mpeVEsTQZJAnegUbVNuBEc3x2b1xgkS3 test_key_1 diff --git a/test/fixture/id_rsa_test_2 b/test/fixture/id_rsa_test_2 new file mode 100644 index 0000000..5a0419d --- /dev/null +++ b/test/fixture/id_rsa_test_2 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAw/7DIvyZK2LV1YuvqWLsL7FMSw16MKd6JLytvtqqpzwOp1hg +bvy0kkGZ2Jh2WMoXG0Sq3bHJ7GzNI35lNHCn/5RokUStWH1HqaJrUVlQccwN5rwb +vqpm7lPzwx3yScQdfRx7nOmvPzTzmSDTTBiGYPdVu/W1tIFyBId/j4jVb7npTnRS +XbyklrRJSZTJP5Ze3Utb6cCfYmfBD22pUsxGd9Es6hvF4OFTITp/cm573DVhiVQo +OMroIZnVQN9h9vq4+qorOACXUGFDaIfZASv11KIIEVeyxGnnsozFj5+hsTOMjOyG +JLLaYpWyojyEI8PRA0zeBXSCfy3HrHH8A0+ymQIDAQABAoIBADojJ98I7qZf+u4N +MV4dadH71ZWtucEU7LZZAXDLjDNvLSmWnYROkYGQ1AHbt78+svvzsntwcWUgIPmW +VI7pm+vEKgBtTr4g61D2TL8krygST01SHyi/gyjO3k/SYjxfYB/sgv5YkxM8scWK +ZyL7Y2D//P50NCIHA7s0NjJtdGJfTketoNldTJjVhFYt3TSn7WVa/n5oPn+DkYlO +rXN9rZ+jkYJiscyo6dB3yxuutFp2dMDVPfTBacOiIGr159xzSnajvnmYvDdS9T0S +SOXaQrNiVgo9rQy/sRNcBDbsGfI4owtyj5kAefm5QorXcAZbKkBbecYYpZYI4Fpn +0JntacECgYEA6PP1oOIkKbKpm3ajIHABz0LJQAMsfUR75PhnvDdSShB3DLCZ5Xkq +p++bFazJu2CNlpZKxKTw4lFI4w8c/xbeFZzvIR0rH30AXzMWwjZiHP1JXeBotTcQ +N1bP9f6cRbTVt/vQShC/qbpsRaKe+HnwA+Bs+qZ5HgpvAVxWWRMm3oMCgYEA12LE +HKMFVU402y5bzrQfLJxLRsJ11VHv2WRY2lOWNHwyE+z30DCUczwi8q5WXwTjpEuc +DUUO7YROySV0mdZF2B73O3GMEWzHVjJklEMObCa2uTImGQQ6UsR+WA+jsLWJliVe +dahIEBMnlCJ1ynsdicV1j+nNGGktTs2TzQ4a37MCgYEAl1AHh1X8mV0kkAeyk9pN +Qpwl7r9hAYkfVK4Ja5cfi15AheycOhLHI0gemyid7Gkd7ud2b16X3Nlhl6L0ebkX +zkAej4rLvOtL94pOyR3cAup+4kxmlvbKQLfR4v5b8Wcxvw0A0Wspo4yk0WJ9Efba +cRowj2rclye9zMt2hutKvxMCgYApMH0akN5zOtqG0+3AsNeJW3ZV2NhZ7Cp3I2aO +kg+Wh+23u8iEatXo16ZDTK29zByQjdkQ/JTnPTcn5l7PEJgYxYs4NfW4W1oflNGB +PPWkHx8weXOpsxJIQ8V5p7wBDiZ6rTjMPVtSn6chABoQl8zFTRq3Y9nSAkMk39GV +FE51WwKBgGt6iiatH0/Nhfk0whDcZb6eedvvSfUtuGtPj551UjmsyN+hUqrLFys8 +4l+/VydbCBlwOutdy8dT9P0N/xq/5F3feBc4l5nsIN0eDm0KfGnbNUSWdDWP30aM +n968ZtFu4G+gSgHznVUV76YkgzezvWNacH3Zz+ERab6Ou/d1tJsI +-----END RSA PRIVATE KEY----- diff --git a/test/fixture/id_rsa_test_2.pub b/test/fixture/id_rsa_test_2.pub new file mode 100644 index 0000000..82c7f86 --- /dev/null +++ b/test/fixture/id_rsa_test_2.pub @@ -0,0 +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/id_rsa_test_combined.pub b/test/fixture/id_rsa_test_combined.pub new file mode 100644 index 0000000..0ff42fc --- /dev/null +++ b/test/fixture/id_rsa_test_combined.pub @@ -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/id_rsa_test_combined.pub.base64 b/test/fixture/id_rsa_test_combined.pub.base64 new file mode 100644 index 0000000..f93c110 --- /dev/null +++ b/test/fixture/id_rsa_test_combined.pub.base64 @@ -0,0 +1 @@ +c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFESHpkK21ZWFNHTFFTWndVSld3WUtPQkk2STdtaEhoM093MERiVFF2UVVIaGtEY1FWcnFqcEJLWnE4U3NuRUs0RXh5SmVSUTVUdFQ1VnFvbGZWRzZMbGFUM3l4WG1KeHg3NVk4b3lwRkFLejdvQ09RQ0p1Z3F1YkFreDJZcFQ2S083ZDZVT1BxT3VMcG1GaTN0K1J5ZjQ5VGJObGpTZXMxc2JTQ3hVRGxUZWt4RytJdCsvZVpVcVhXdWRvcm5ZSkNmbU1lMVVBWXRSd01LaXlEV3FVMUxOMUtiMnpWUjc2WXBqTXhLOUlpRUpqaHBaUy9SNzB6eEdFaWpGckwrUnY0Qng4eS9VakFWVnRUa3ZwYUVhang3cE1uZDBmc2ZuVXdoWUxvVkhnRzVaZEhBWVNSeThqLzArbXBlVkVzVFFaSkFuZWdVYlZOdUJFYzN4MmIxeGdrUzMgdGVzdF9rZXlfMQpzc2gtcnNhIEFBQUFCM056YUMxeWMyRUFBQUFEQVFBQkFBQUJBUUREL3NNaS9Ka3JZdFhWaTYrcFl1d3ZzVXhMRFhvd3Azb2t2SzIrMnFxblBBNm5XR0J1L0xTU1FablltSFpZeWhjYlJLcmRzY25zYk0wamZtVTBjS2YvbEdpUlJLMVlmVWVwb210UldWQnh6QTNtdkJ1K3FtYnVVL1BESGZKSnhCMTlISHVjNmE4L05QT1pJTk5NR0laZzkxVzc5YlcwZ1hJRWgzK1BpTlZ2dWVsT2RGSmR2S1NXdEVsSmxNay9sbDdkUzF2cHdKOWlaOEVQYmFsU3pFWjMwU3pxRzhYZzRWTWhPbjl5Ym52Y05XR0pWQ2c0eXVnaG1kVkEzMkgyK3JqNnFpczRBSmRRWVVOb2g5a0JLL1hVb2dnUlY3TEVhZWV5ak1XUG42R3hNNHlNN0lZa3N0cGlsYktpUElRanc5RURUTjRGZElKL0xjZXNjZndEVDdLWiB0ZXN0X2tleV8yCg== diff --git a/test/fixture/test_directory/var/www/test/public_html/index.html b/test/fixture/test_directory/var/www/test/public_html/index.html new file mode 100644 index 0000000..b1272b6 --- /dev/null +++ b/test/fixture/test_directory/var/www/test/public_html/index.html @@ -0,0 +1 @@ +

Hello, world!

\ No newline at end of file diff --git a/test/fixture/test_file b/test/fixture/test_file new file mode 100644 index 0000000..dacce06 --- /dev/null +++ b/test/fixture/test_file @@ -0,0 +1 @@ +[::TEST::] \ No newline at end of file diff --git a/test/shpec/makefile.sh b/test/shpec/makefile.sh new file mode 100755 index 0000000..e13d526 --- /dev/null +++ b/test/shpec/makefile.sh @@ -0,0 +1,114 @@ +describe "Makefile" + readonly DOCKER_NAME="makefile_test" + + it "builds the image" + local status_make_build + + make build &> /dev/null + status_make_build=${?} + + assert equal ${status_make_build} 0 + end + + it "creates the container" + local status_make_install + + make install &> /dev/null + status_make_install=${?} + + assert equal ${status_make_install} 0 + end + + it "starts the container" + local status_make_start + + make start &> /dev/null + status_make_start=${?} + + assert equal ${status_make_start} 0 + end + + it "pauses the container" + local status_make_pause + + make pause &> /dev/null + status_make_pause=${?} + + assert equal ${status_make_pause} 0 + end + + it "unpauses the container" + local status_make_unpause + + make unpause &> /dev/null + status_make_unpause=${?} + + assert equal ${status_make_unpause} 0 + end + + it "restarts the container" + local status_make_restart + + make restart &> /dev/null + status_make_restart=${?} + + assert equal ${status_make_restart} 0 + end + + it "outputs the container logs" + local status_make_logs + local content_make_logs + + content_make_logs=$( + make logs + ) + status_make_logs=${?} + + assert equal ${status_make_logs} 0 + end + + it "terminates the container" + local status_make_terminate + + make terminate &> /dev/null + status_make_terminate=${?} + + assert equal ${status_make_terminate} 0 + end + + it "runs the container" + local status_make_run + + make run &> /dev/null + status_make_run=${?} + + assert equal ${status_make_run} 0 + end + + it "stops the container" + local status_make_stop + + make stop &> /dev/null + status_make_stop=${?} + + assert equal ${status_make_stop} 0 + end + + it "deletes the container" + local status_make_rm + + make rm &> /dev/null + status_make_rm=${?} + + assert equal ${status_make_rm} 0 + end + + it "untags the image" + local status_make_clean + + make rmi &> /dev/null + status_make_clean=${?} + + assert equal ${status_make_clean} 0 + end +end \ No newline at end of file diff --git a/test/shpec/operation_shpec.sh b/test/shpec/operation_shpec.sh new file mode 100644 index 0000000..6a43102 --- /dev/null +++ b/test/shpec/operation_shpec.sh @@ -0,0 +1,979 @@ +readonly BOOTSTRAP_BACKOFF_TIME=2 +readonly DOCKER_HOSTNAME="localhost" +readonly REDACTED_VALUE="********" +readonly TEST_DIRECTORY="test" +readonly PUBLIC_KEY_ID_RSA_TEST_1="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHzd+mYXSGLQSZwUJWwYKOBI6I7mhHh3Ow0DbTQvQUHhkDcQVrqjpBKZq8SsnEK4ExyJeRQ5TtT5VqolfVG6LlaT3yxXmJxx75Y8oypFAKz7oCOQCJugqubAkx2YpT6KO7d6UOPqOuLpmFi3t+Ryf49TbNljSes1sbSCxUDlTekxG+It+/eZUqXWudornYJCfmMe1UAYtRwMKiyDWqU1LN1Kb2zVR76YpjMxK9IiEJjhpZS/R70zxGEijFrL+Rv4Bx8y/UjAVVtTkvpaEajx7pMnd0fsfnUwhYLoVHgG5ZdHAYSRy8j/0+mpeVEsTQZJAnegUbVNuBEc3x2b1xgkS3 test_key_1" +readonly PUBLIC_KEY_ID_RSA_TEST_1_SIGNATURE="45:46:b0:ef:a5:e3:c9:6f:1e:66:94:ba:e1:fd:df:65" +readonly PUBLIC_KEY_ID_RSA_TEST_2="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDD/sMi/JkrYtXVi6+pYuwvsUxLDXowp3okvK2+2qqnPA6nWGBu/LSSQZnYmHZYyhcbRKrdscnsbM0jfmU0cKf/lGiRRK1YfUepomtRWVBxzA3mvBu+qmbuU/PDHfJJxB19HHuc6a8/NPOZINNMGIZg91W79bW0gXIEh3+PiNVvuelOdFJdvKSWtElJlMk/ll7dS1vpwJ9iZ8EPbalSzEZ30SzqG8Xg4VMhOn9ybnvcNWGJVCg4yughmdVA32H2+rj6qis4AJdQYUNoh9kBK/XUoggRV7LEaeeyjMWPn6GxM4yM7IYkstpilbKiPIQjw9EDTN4FdIJ/LcescfwDT7KZ test_key_2" +readonly PUBLIC_KEY_ID_RSA_TEST_2_SIGNATURE="b3:2e:5d:8c:76:d3:c7:24:13:a3:4f:6f:4d:a2:31:9c" +readonly PUBLIC_KEY_ID_RSA_TEST_COMBINED_BASE64="c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFCQVFESHpkK21ZWFNHTFFTWndVSld3WUtPQkk2STdtaEhoM093MERiVFF2UVVIaGtEY1FWcnFqcEJLWnE4U3NuRUs0RXh5SmVSUTVUdFQ1VnFvbGZWRzZMbGFUM3l4WG1KeHg3NVk4b3lwRkFLejdvQ09RQ0p1Z3F1YkFreDJZcFQ2S083ZDZVT1BxT3VMcG1GaTN0K1J5ZjQ5VGJObGpTZXMxc2JTQ3hVRGxUZWt4RytJdCsvZVpVcVhXdWRvcm5ZSkNmbU1lMVVBWXRSd01LaXlEV3FVMUxOMUtiMnpWUjc2WXBqTXhLOUlpRUpqaHBaUy9SNzB6eEdFaWpGckwrUnY0Qng4eS9VakFWVnRUa3ZwYUVhang3cE1uZDBmc2ZuVXdoWUxvVkhnRzVaZEhBWVNSeThqLzArbXBlVkVzVFFaSkFuZWdVYlZOdUJFYzN4MmIxeGdrUzMgdGVzdF9rZXlfMQpzc2gtcnNhIEFBQUFCM056YUMxeWMyRUFBQUFEQVFBQkFBQUJBUUREL3NNaS9Ka3JZdFhWaTYrcFl1d3ZzVXhMRFhvd3Azb2t2SzIrMnFxblBBNm5XR0J1L0xTU1FablltSFpZeWhjYlJLcmRzY25zYk0wamZtVTBjS2YvbEdpUlJLMVlmVWVwb210UldWQnh6QTNtdkJ1K3FtYnVVL1BESGZKSnhCMTlISHVjNmE4L05QT1pJTk5NR0laZzkxVzc5YlcwZ1hJRWgzK1BpTlZ2dWVsT2RGSmR2S1NXdEVsSmxNay9sbDdkUzF2cHdKOWlaOEVQYmFsU3pFWjMwU3pxRzhYZzRWTWhPbjl5Ym52Y05XR0pWQ2c0eXVnaG1kVkEzMkgyK3JqNnFpczRBSmRRWVVOb2g5a0JLL1hVb2dnUlY3TEVhZWV5ak1XUG42R3hNNHlNN0lZa3N0cGlsYktpUElRanc5RURUTjRGZElKL0xjZXNjZndEVDdLWiB0ZXN0X2tleV8yCg==" + +# This should ideally be a static value but hosts might be using this port so +# need to allow for an alternative. +DOCKER_PORT_MAP_TCP_22="${DOCKER_PORT_MAP_TCP_22:-2020}" + +function docker_terminate_container () +{ + local CONTAINER="${1}" + + if docker ps -aq \ + --filter "name=${CONTAINER}" \ + --filter "status=paused" &> /dev/null; then + docker unpause ${CONTAINER} &> /dev/null + fi + + if docker ps -aq \ + --filter "name=${CONTAINER}" \ + --filter "status=running" &> /dev/null; then + docker stop ${CONTAINER} &> /dev/null + fi + + if docker ps -aq \ + --filter "name=${CONTAINER}" &> /dev/null; then + docker rm -vf ${CONTAINER} &> /dev/null + fi +} + +function test_setup () +{ + chmod 600 ${TEST_DIRECTORY}/fixture/{id_rsa_insecure,id_rsa_test_1,id_rsa_test_2} +} + +if [[ ! -d ${TEST_DIRECTORY} ]]; then + printf -- \ + "ERROR: Please run from the project root.\n" \ + >&2 + exit 1 +fi + +describe "jdeathe/centos-ssh:latest" + test_setup + + describe "Basic SSH operations" + trap "docker_terminate_container ssh.pool-1.1.1 &> /dev/null" \ + INT TERM EXIT + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + it "Runs an SSH container named ssh.pool-1.1.1 on port ${DOCKER_PORT_MAP_TCP_22}." + local container_port_22="" + + docker run -d \ + --name ssh.pool-1.1.1 \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + if [[ ${DOCKER_PORT_MAP_TCP_22} == 0 ]] \ + || [[ -z ${DOCKER_PORT_MAP_TCP_22} ]]; then + assert gt "${container_port_22}" "30000" + else + assert equal "${container_port_22}" "${DOCKER_PORT_MAP_TCP_22}" + fi + end + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + it "Generates a password that can be retrieved from the log." + local password="" + + password="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^password :.*$/ { print $3 }' + )" + + assert unequal "${password}" "" + + it "Displays the password in plain text." + assert unequal "${password}" "${REDACTED_VALUE}" + end + end + + it "Allows the user to connect using SSH + private key authentication." + local status_ssh_connection="" + local user_home="" + + # 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' + + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\${HOME}" \ + &> /dev/null + + status_ssh_connection=${?} + + assert equal "${status_ssh_connection}" 0 + + it "Requires a password for sudo commands." + user_home="$( + echo ${password} \ + | 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 + + # Reset sudo configuration + docker exec ssh.pool-1.1.1 \ + bash -c 'rm -f /etc/sudoers.d/no_lecture' + end + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + trap - \ + INT TERM EXIT + end + + describe "Basic SFTP operations" + trap "docker_terminate_container sftp.pool-1.1.1 &> /dev/null" \ + INT TERM EXIT + + docker_terminate_container sftp.pool-1.1.1 &> /dev/null + + it "Runs an SFTP container named sftp.pool-1.1.1 on port ${DOCKER_PORT_MAP_TCP_22}." + local container_port_22="" + + docker run -d \ + --name sftp.pool-1.1.1 \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + --env SSH_USER_FORCE_SFTP=true \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + sftp.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + if [[ ${DOCKER_PORT_MAP_TCP_22} == 0 ]] \ + || [[ -z ${DOCKER_PORT_MAP_TCP_22} ]]; then + assert gt "${container_port_22}" "30000" + else + assert equal "${container_port_22}" "${DOCKER_PORT_MAP_TCP_22}" + fi + end + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + it "Allows the user to connect using SFTP + private key authentication." + local status_sftp_connection="" + + sftp -q \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + -o Port=${container_port_22} \ + app-admin@${DOCKER_HOSTNAME} \ + <<< "version" \ + &> /dev/null + + status_sftp_connection=${?} + + assert equal "${status_sftp_connection}" 0 + + it "Allows the user to upload a file to their _data directory." + local status_sftp_connection="" + + sftp -q \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + -o Port=${container_port_22} \ + app-admin@${DOCKER_HOSTNAME}:_data \ + <<< "put ${TEST_DIRECTORY}/fixture/test_file" \ + &> /dev/null + + status_sftp_connection=${?} + + assert equal "${status_sftp_connection}" 0 + end + + it "Jails the user into a chroot directory." + local status_sftp_connection="" + + docker exec sftp.pool-1.1.1 \ + touch /home/app-admin/root_test + + sftp -q \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + -o Port=${container_port_22} \ + app-admin@${DOCKER_HOSTNAME} \ + <<< "ls /root_test" \ + | grep -q "^/root_test" + + status_sftp_connection=${?} + + assert equal "${status_sftp_connection}" 0 + end + end + + docker_terminate_container sftp.pool-1.1.1 &> /dev/null + trap - \ + INT TERM EXIT + end + + describe "Customised SSH configuration" + trap "docker_terminate_container ssh.pool-1.1.1 &> /dev/null" \ + INT TERM EXIT + + it "Allows configuration of passwordless sudo." + local container_port_22="" + local user_home="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + user_home="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- sudo \ + printf \ + '%s\\n' \ + "\${HOME}" + )" + + assert equal "${user_home}" "/home/app-admin" + + it "Displays the sudo settings in the logs output summary." + local user_sudo="" + + user_sudo="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^sudo :.*$/ { print $0; }' + )" + + assert equal "${user_sudo/sudo : /}" "ALL=(ALL) NOPASSWD:ALL" + end + end + + it "Allows configuration of the username." + local container_port_22="" + local user_home="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_USER=centos" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + user_home="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + centos@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\${HOME}" + )" + + assert equal "${user_home}" "/home/centos" + + it "Displays the user in the logs output summary." + local user="" + + user="$(docker logs ssh.pool-1.1.1 \ + | awk '/^user :.*$/ { print $0; }' + )" + + assert equal "${user/user : /}" "centos" + end + end + + it "Allows configuration of an alternative public key." + local container_port_22="" + local user_home="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_AUTHORIZED_KEYS=${PUBLIC_KEY_ID_RSA_TEST_1}" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + 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_home}" "/home/app-admin" + + it "Displays the key's signature in the logs output summary." + local user_key_signature="" + + 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; }' + )" + + assert equal "${user_key_signature}" "${PUBLIC_KEY_ID_RSA_TEST_1_SIGNATURE}" + end + + it "Allows multiple keys to be added as a base64 encoded string." + local container_port_22="" + local user_key="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --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="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + 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+="$( + 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" + + it "Displays the key's signatures in the logs output summary." + local user_key_signature="" + + 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 + + it "Allows configuration of the user's home directory where %u is replaced with the username in the path." + local container_port_22="" + local user_home="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_USER=app-1" \ + --env "SSH_USER_HOME=/var/www/%u" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + user_home="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-1@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\${HOME}" + )" + + assert equal "${user_home}" "/var/www/app-1" + + it "Displays the user's home directory in the logs output summary." + local home="" + + home="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^home :.*$/ { print $0; }' + )" + + assert equal "${home/home : /}" "/var/www/app-1" + end + end + + it "Allows configuration of the user's uid:gid." + local container_port_22="" + local user_id="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_USER_ID=1000:502" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + user_id="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s:%s\\n' \ + "\$(id --user app-admin)" \ + "\$(id --group app-admin)" + )" + + assert equal "${user_id}" "1000:502" + + it "Displays the user's uid:gid in the logs output summary." + local user_id="" + + user_id="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^id :.*$/ { print $0; }' + )" + + assert equal "${user_id/id : /}" "1000:502" + end + end + + it "Allows configuration of the user's shell." + local container_port_22="" + local user_shell="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_USER_SHELL=/bin/sh" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + user_shell="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\$(getent passwd app-admin \ + | cut -d: -f7 + )" + )" + + assert equal "${user_shell}" "/bin/sh" + + it "Displays the user's shell in the logs output summary." + local user_shell="" + + user_shell="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^shell :.*$/ { print $0; }' + )" + + assert equal "${user_shell/shell : /}" "/bin/sh" + end + end + + it "Allows configuration to enable the environment to be inherited." + local container_port_22="" + local user_env_value="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_SUDO=ALL=(ALL) NOPASSWD:ALL" \ + --env "SSH_INHERIT_ENVIRONMENT=true" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + user_env_value="$( + ssh -q \ + -p ${container_port_22} \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + app-admin@${DOCKER_HOSTNAME} \ + -- printf \ + '%s\\n' \ + "\$(env | grep SSH_INHERIT_ENVIRONMENT=true)" + )" + + assert equal "${user_env_value}" "SSH_INHERIT_ENVIRONMENT=true" + end + + it "Allows configuration of a plain text password." + local container_port_22="" + local user_home="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_USER_PASSWORD=Insecure_Passw0rd£" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + # 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' + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + 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" + + it "Will redact the SSH_USER_PASSWORD environment variable after bootstrap." + # TODO + # 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 "Will redact the user's password in the logs output summary." + local password="" + + password="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^password :.*$/ { print $0; }' + )" + + assert equal "${password/password : /}" "${REDACTED_VALUE}" + end + end + + it "Allows configuration of a hashed password." + local container_port_22="" + local user_home="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env 'SSH_USER_PASSWORD=$6$pepper$g5/OhofGtHVo3wqRgVHFQrJDyK0mV9bDpF5HP964wuIkQ7MXuYq1KRTmShaUmTQW3ZRsjw2MjC1LNPh5HMcrY0' \ + --env "SSH_USER_PASSWORD_HASHED=true" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + container_port_22="$( + docker port \ + ssh.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + # 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' + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + 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" + + # 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 "Will redact the user's password in the logs output summary." + local password="" + + password="$( + docker logs ssh.pool-1.1.1 \ + | awk '/^password :.*$/ { print $0; }' + )" + + assert equal "${password/password : /}" "${REDACTED_VALUE}" + end + end + + it "Allows preventing the startup of the sshd bootstrap." + local container_port_22="" + local sshd_bootstrap_info="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_AUTOSTART_SSHD_BOOTSTRAP=false" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + sshd_bootstrap_info="$( + docker logs ssh.pool-1.1.1 \ + | awk '/INFO success: sshd-bootstrap entered RUNNING state/ { print $0; }' + )" + + assert equal "${sshd_bootstrap_info}" "" + end + + it "Allows preventing the startup of the sshd daemon." + local container_port_22="" + local docker_top="" + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + + docker run -d \ + --name ssh.pool-1.1.1 \ + --env "SSH_AUTOSTART_SSHD=false" \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + jdeathe/centos-ssh:latest &> /dev/null + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + docker_top="$( + docker top ssh.pool-1.1.1 \ + | awk '/\/usr\/sbin\/sshd -/ { print $0; }' + )" + + assert equal "${docker_top}" "" + end + + docker_terminate_container ssh.pool-1.1.1 &> /dev/null + trap - \ + INT TERM EXIT + end + + describe "Customised SFTP configuration" + trap "docker_terminate_container sftp.pool-1.1.1 &> /dev/null; docker_terminate_container www-data.pool-1.1.1 &> /dev/null; docker volume rm www-data.pool-1.1.1 &> /dev/null" \ + INT TERM EXIT + + docker_terminate_container sftp.pool-1.1.1 &> /dev/null + + it "Allows configuration of the user's ChrootDirectory where %u is replaced with the username in the path." + local container_port_22="" + local status_sftp_connection="" + + docker run -d \ + --name sftp.pool-1.1.1 \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + --env SSH_CHROOT_DIRECTORY="/chroot/%u" \ + --env SSH_USER_FORCE_SFTP=true \ + jdeathe/centos-ssh:latest \ + &> /dev/null + + container_port_22="$( + docker port \ + sftp.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + docker exec sftp.pool-1.1.1 \ + touch /chroot/app-admin/home/app-admin/root_test + docker exec sftp.pool-1.1.1 \ + chown app-admin:app-admin /chroot/app-admin/home/app-admin/root_test + + sftp -q \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + -o Port=${container_port_22} \ + app-admin@${DOCKER_HOSTNAME} \ + <<< "ls /home/app-admin/root_test" \ + | grep -q "^/home/app-admin/root_test" + + status_sftp_connection=${?} + + assert equal "${status_sftp_connection}" 0 + + it "Displays the chroot path in the logs output summary." + local chroot_path="" + + chroot_path="$( + docker logs sftp.pool-1.1.1 \ + | awk '/^chroot path :.*$/ { print $0; }' + )" + + assert equal "${chroot_path/chroot path : /}" "/chroot/app-admin" + end + + it "Configures the user with the /sbin/nologin shell." + local user_shell="" + + user_shell="$( + docker exec sftp.pool-1.1.1 \ + getent passwd app-admin \ + | cut -d: -f7 + )" + + assert equal "${user_shell}" "/sbin/nologin" + + it "Displays the user's shell in the logs output summary." + local user_shell="" + + user_shell="$( + docker logs sftp.pool-1.1.1 \ + | awk '/^shell :.*$/ { print $0; }' + )" + + assert equal "${user_shell/shell : /}" "/sbin/nologin" + end + end + + it "Allows the user to write to their HOME directory." + local status_sftp_connection="" + + sftp -q \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + -o Port=${container_port_22} \ + app-admin@${DOCKER_HOSTNAME} \ + <<< "put ${TEST_DIRECTORY}/fixture/test_file" \ + &> /dev/null + + status_sftp_connection=${?} + + assert equal "${status_sftp_connection}" 0 + end + end + + it "Allows configuration of SFTP access to a volume mounted from another container." + local container_port_22="" + local status_sftp_connection="" + + docker_terminate_container sftp.pool-1.1.1 &> /dev/null + docker_terminate_container www-data.pool-1.1.1 &> /dev/null + docker volume rm www-data.pool-1.1.1 &> /dev/null + + docker run -d \ + --name www-data.pool-1.1.1 \ + --volume www-data.pool-1.1.1:/var/www \ + jdeathe/centos-ssh:latest \ + &> /dev/null + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + docker cp \ + test/fixture/test_directory/var/www/. \ + www-data.pool-1.1.1:/var/www + docker exec www-data.pool-1.1.1 \ + chown -R app-admin:app-admin /var/www/test + + docker run -d \ + --name sftp.pool-1.1.1 \ + --publish ${DOCKER_PORT_MAP_TCP_22}:22 \ + --env SSH_CHROOT_DIRECTORY="/var/www" \ + --env SSH_USER_FORCE_SFTP=true \ + --env SSH_USER_HOME="/var/www" \ + --volumes-from www-data.pool-1.1.1 \ + jdeathe/centos-ssh:latest \ + &> /dev/null + + container_port_22="$( + docker port \ + sftp.pool-1.1.1 \ + 22/tcp + )" + container_port_22=${container_port_22##*:} + + sleep ${BOOTSTRAP_BACKOFF_TIME} + + sftp -q \ + -i ${TEST_DIRECTORY}/fixture/id_rsa_insecure \ + -o StrictHostKeyChecking=no \ + -o LogLevel=error \ + -o Port=${container_port_22} \ + app-admin@${DOCKER_HOSTNAME} \ + <<< "ls test/public_html/index.html" \ + | grep -q "^test/public_html/index.html" + + status_sftp_connection=${?} + + assert equal "${status_sftp_connection}" 0 + end + + docker_terminate_container sftp.pool-1.1.1 &> /dev/null + docker_terminate_container www-data.pool-1.1.1 &> /dev/null + docker volume rm www-data.pool-1.1.1 &> /dev/null + trap - \ + INT TERM EXIT + end +end diff --git a/testing.md b/testing.md new file mode 100644 index 0000000..9152846 --- /dev/null +++ b/testing.md @@ -0,0 +1,21 @@ +# Testing + +## Functional + +### Installation + +The functional test cases are written in [shpec](https://github.com/rylnd/shpec). + +To run the tests install shpec with the installer. + +``` +$ bash -c "$(curl -L https://raw.github.com/rylnd/shpec/master/install.sh)" +``` + +### Usage + +To manually run the test cases, from the project root: + +``` +$ env SHPEC_ROOT=test/shpec shpec +```