diff --git a/Makefile b/Makefile index 25081480b..b4db46895 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ DOKKU_VERSION = master -SSHCOMMAND_URL ?= https://raw.githubusercontent.com/dokku/sshcommand/v0.4.0/sshcommand +SSHCOMMAND_URL ?= https://raw.githubusercontent.com/dokku/sshcommand/v0.5.0/sshcommand PLUGN_URL ?= https://github.com/dokku/plugn/releases/download/v0.2.1/plugn_0.2.1_linux_x86_64.tgz SIGIL_URL ?= https://github.com/gliderlabs/sigil/releases/download/v0.4.0/sigil_0.4.0_Linux_x86_64.tgz STACK_URL ?= https://github.com/gliderlabs/herokuish.git diff --git a/plugins/ssh-keys/commands b/plugins/ssh-keys/commands new file mode 100755 index 000000000..bfd36a0ce --- /dev/null +++ b/plugins/ssh-keys/commands @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +[[ " help ssh-keys:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x + +case "$1" in + help | ssh-keys:help) + help_content_func () { + declare desc="return ssh-keys plugin help content" + cat< [/path/to/key], Add a new public key by pipe or path + ssh-keys:remove , Remove SSH public key by name +help_content + } + + if [[ $1 = "ssh-keys:help" ]] ; then + echo -e 'Usage: dokku ssh-keys[:COMMAND]' + echo '' + echo 'Manage public ssh keys that are allowed to connect to Dokku' + echo '' + echo 'Additional commands:' + help_content_func | sort | column -c2 -t -s, + else + help_content_func + fi + ;; + + *) + exit "$DOKKU_NOT_IMPLEMENTED_EXIT" + ;; + +esac diff --git a/plugins/ssh-keys/functions b/plugins/ssh-keys/functions new file mode 100755 index 000000000..1a2f4f19b --- /dev/null +++ b/plugins/ssh-keys/functions @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" + +verify_ssh_key_file() { + declare desc="Test that public key is valid" + [[ -s ${DOKKU_ROOT}/.ssh/authorized_keys ]] || dokku_log_fail "No public keys found." + ssh-keygen -l -f "${DOKKU_ROOT}/.ssh/authorized_keys" &> /dev/null || dokku_log_fail "${DOKKU_ROOT}/.ssh/authorized_keys failed ssh-kegen check." +} + +verify_ssh_key_exists() { + declare desc="Test that public key exists" + [[ -e ${DOKKU_ROOT}/.ssh/authorized_keys ]] || dokku_log_fail "No public keys found." +} diff --git a/plugins/ssh-keys/plugin.toml b/plugins/ssh-keys/plugin.toml new file mode 100644 index 000000000..7d17ef091 --- /dev/null +++ b/plugins/ssh-keys/plugin.toml @@ -0,0 +1,4 @@ +[plugin] +description = "dokku core ssh-keys plugin" +version = "0.6.4" +[plugin.config] diff --git a/plugins/ssh-keys/subcommands/add b/plugins/ssh-keys/subcommands/add new file mode 100755 index 000000000..b9e3b2ddf --- /dev/null +++ b/plugins/ssh-keys/subcommands/add @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/ssh-keys/functions" + +add_keys(){ + declare desc="add a new key via sshcommand" + shift + local name="$1" key_file="$2" key_contents key_from_pipe + [[ -p /dev/stdin ]] && read -r key_from_pipe + if [[ -n "$key_from_pipe" ]]; then + ssh-keygen -lf /dev/stdin <<< "$key_from_pipe" &> /dev/null || dokku_log_fail "Key piped in is not a valid ssh public key" + key_contents="$key_from_pipe" + elif [[ -n "$key_file" ]]; then + key_contents="$(cat "$key_file")" + fi + [[ -n "$name" && -n "$key_contents" ]] || dokku_log_fail "Two arguments are required if not piping, ie: dokku ssh-keys:add " + verify_ssh_key_exists + echo "$key_contents" | sshcommand acl-add dokku "$name" || dokku_log_fail "sshcommand returned an error: $?" +} + +add_keys "$@" diff --git a/plugins/ssh-keys/subcommands/list b/plugins/ssh-keys/subcommands/list new file mode 100755 index 000000000..5eb165e4f --- /dev/null +++ b/plugins/ssh-keys/subcommands/list @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/ssh-keys/functions" + +list_ssh_keys() { + declare desc="List ssh key hashes" + verify_ssh_key_file + sshcommand list dokku +} + +list_ssh_keys "$@" diff --git a/plugins/ssh-keys/subcommands/remove b/plugins/ssh-keys/subcommands/remove new file mode 100755 index 000000000..415d3a528 --- /dev/null +++ b/plugins/ssh-keys/subcommands/remove @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_CORE_AVAILABLE_PATH/ssh-keys/functions" + +remove_key() { + declare desc="Removes key from authorized_keys" + shift + local name="$1" + verify_ssh_key_file + [[ -z $1 ]] && dokku_log_fail "A name is required to remove a key, ie: dokku ssh-keys:remove " + sshcommand acl-remove dokku "$name" || dokku_log_fail "sshcommand returned an error $?" +} + +remove_key "$@" diff --git a/tests/unit/30_ssh_keys.bats b/tests/unit/30_ssh_keys.bats new file mode 100644 index 000000000..7bd43907e --- /dev/null +++ b/tests/unit/30_ssh_keys.bats @@ -0,0 +1,38 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + create_key +} + +teardown() { + destroy_key +} + +@test "(ssh-keys) ssh-keys:add, ssh-keys:list, ssh-keys:remove" { + run /bin/bash -c "dokku ssh-keys:add name1 /tmp/testkey.pub" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "cat /tmp/testkey.pub | dokku ssh-keys:add name2" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "dokku ssh-keys:list | grep name1 && dokku ssh-keys:list | grep name2" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "dokku ssh-keys:remove name1" + echo "output: "$output + echo "status: "$status + assert_success + run /bin/bash -c "dokku ssh-keys:list | grep name1" + echo "output: "$output + echo "status: "$status + assert_failure + run /bin/bash -c "dokku ssh-keys:list | grep name2" + echo "output: "$output + echo "status: "$status + assert_success +} diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 8f518750c..206b6a04f 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -117,6 +117,12 @@ create_app() { dokku apps:create "$TEST_APP" } + +create_key() { + touch "$DOKKU_ROOT/.ssh/authorized_keys" && chown dokku:dokku "$DOKKU_ROOT/.ssh/authorized_keys" + ssh-keygen -P "" -f /tmp/testkey &> /dev/null +} + destroy_app() { local RC="$1"; local RC=${RC:=0} local APP="$2"; local TEST_APP=${APP:=$TEST_APP} @@ -124,6 +130,10 @@ destroy_app() { return "$RC" } +destroy_key() { + rm -f "$DOKKU_ROOT/.ssh/authorized_keys" /tmp/testkey* &> /dev/null || true +} + add_domain() { dokku domains:add "$TEST_APP" "$1" }