Merge pull request #2287 from dokku/u2mejc-ssh-keys

Add ssh-keys core plugin
This commit is contained in:
Jose Diaz-Gonzalez
2016-07-30 15:11:01 -04:00
committed by GitHub
12 changed files with 186 additions and 22 deletions

View File

@@ -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

2
deb.mk
View File

@@ -26,7 +26,7 @@ Simplifies running a single command over SSH, and
manages authorized keys (ACL) and users in order to do so.
endef
SSHCOMMAND_REPO_NAME ?= dokku/sshcommand
SSHCOMMAND_VERSION ?= 0.4.0
SSHCOMMAND_VERSION ?= 0.5.0
SSHCOMMAND_ARCHITECTURE = amd64
SSHCOMMAND_PACKAGE_NAME = sshcommand_$(SSHCOMMAND_VERSION)_$(SSHCOMMAND_ARCHITECTURE).deb
SSHCOMMAND_URL ?= https://raw.githubusercontent.com/dokku/sshcommand/v$(SSHCOMMAND_VERSION)/sshcommand

View File

@@ -1,40 +1,41 @@
# User Management
While it is possible to use password-based authorization to push to Dokku, it is preferable to use key-based authentication for security.
When pushing to Dokku, ssh key based authorization is the preferred authentication method, for ease of use and increased security.
Users in dokku are managed via the `~/dokku/.ssh/authorized_keys` file. While you *can* manually edit this file, it is **highly** recommended that you follow the below steps to manage users on a dokku server.
Users in Dokku are managed via the `~/dokku/.ssh/authorized_keys` file. It is **highly** recommended that you follow the steps below to manage users on a Dokku server.
## SSHCommand
## Dokku ssh-keys command
Dokku uses the [`sshcommand`](https://github.com/dokku/sshcommand) utility to manage ssh keys for the dokku user. The following is the usage output for sshcommand.
The `dokku ssh-keys` command(s) allow you to manage ssh keys used to push to the Dokku server. The following is the usage output for `dokku ssh-keys`:
```
sshcommand create <user> <command> # creates a user forced to run command when SSH connects
sshcommand acl-add <user> <ssh-key-name> # adds named SSH key to user from STDIN
sshcommand acl-remove <user> <ssh-key-name> # removes SSH key by name
sshcommand help # displays the usage help message
$ dokku ssh-keys:help
Usage: dokku ssh-keys[:COMMAND]
Manage public ssh keys that are allowed to connect to Dokku
Additional commands:
ssh-keys:add <name> [/path/to/key] Add a new public key by pipe or path
ssh-keys:list List of all authorized Dokku public ssh keys
ssh-keys Manage public ssh keys that are allowed to connect to Dokku
ssh-keys:remove <name> Remove SSH public key by name
```
In dokku's case, the `<user>` section is *always* `dokku`, as this is the system user that the dokku binary performs all it's actions. Keys are given unique names, which can be used in conjunction with the [user-auth](/dokku/development/plugin-triggers/#user-auth) plugin trigger to handle command authorization.
Keys are given unique names, which can be used in conjunction with the [user-auth](/dokku/development/plugin-triggers/#user-auth) plugin trigger to handle command authorization. In Dokku's case, the unique _name_ is just for ease of identifying the keys, the ssh (git) user is *always* `dokku`, as this is the system user that the `dokku` binary uses to perform all it's actions.
## Adding deploy users
You can add your public key to the dokku user's `~/dokku/.ssh/authorized_keys` file with the following command:
You can add your public key to Dokku with the following command:
`NAME` is the username prefer to use to refer to this particular key. Including the word `admin` in the name will grant the user privileges to add additional keys remotely.
```shell
# from your local machine
# replace dokku.me with your domain name or the host's IP
# replace root with your server's root user
# USER is the username you use to refer to this particular key
cat ~/.ssh/id_rsa.pub | ssh root@dokku.me "sudo sshcommand acl-add dokku USER"
$ dokku ssh-keys:add <NAME> <PATH/TO/KEY>
```
At it's base, the `sshcommand` *must* be run under a user with sudo access, as it sets keys for the dokku user.
For instance, if you stored your public key at `~/.ssh/id_rsa.pub-open` and are deploying to EC2 where the default root-enabled user is `ubuntu`, you can run the following command to add your key under the `superuser` username:
Admin users and root can also add keys remotely:
```shell
cat ~/.ssh/id_rsa.pub-open | ssh ubuntu@dokku.me "sudo sshcommand acl-add dokku superuser"
cat <PATH/TO/KEY> | ssh dokku@dokku.me ssh-keys:add <NAME>
```
If you are using the vagrant installation, you can also use the `make vagrant-acl-add` target to add your public key to dokku (it will use your host username as the `USER`):

33
plugins/ssh-keys/commands Executable file
View File

@@ -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<<help_content
ssh-keys, Manage public ssh keys that are allowed to connect to Dokku
ssh-keys:list, List of all authorized dokku public ssh keys
ssh-keys:add <name> [/path/to/key], Add a new public key by pipe or path
ssh-keys:remove <name>, 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

14
plugins/ssh-keys/functions Executable file
View File

@@ -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-keygen 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."
}

View File

@@ -0,0 +1,4 @@
[plugin]
description = "dokku core ssh-keys plugin"
version = "0.6.4"
[plugin.config]

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/ssh-keys/functions"
add_keys() {
declare desc="add a new key via sshcommand"
local cmd="ssh-keys:add"
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 <NAME> <KEY_FILE>"
verify_ssh_key_exists
echo "$key_contents" | sshcommand acl-add dokku "$name" || dokku_log_fail "sshcommand returned an error: $?"
}
add_keys "$@"

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/ssh-keys/functions"
list_ssh_keys() {
declare desc="List ssh key hashes"
local cmd="ssh-keys:list"
verify_ssh_key_file
sshcommand list dokku
}
list_ssh_keys "$@"

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/ssh-keys/functions"
remove_key() {
declare desc="Removes key from authorized_keys"
local cmd="ssh-keys:remove"
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 <name>"
sshcommand acl-remove dokku "$name" || dokku_log_fail "sshcommand returned an error $?"
}
remove_key "$@"

13
plugins/ssh-keys/user-auth Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
check_ssh_keys_user() {
declare desc="check user running ssh-keys"
local SSH_USER=$1 SSH_NAME=$2
[[ "$SSH_USER" == "root" || "$SSH_NAME" == *admin* ]] || dokku_log_fail "You must be root, or a dokku admin, to execute this command"
}
if [[ "$3" == ssh-keys* ]]; then
check_ssh_keys_user "$@"
fi

View File

@@ -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
}

View File

@@ -117,6 +117,11 @@ create_app() {
dokku apps:create "$TEST_APP"
}
create_key() {
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 +129,10 @@ destroy_app() {
return "$RC"
}
destroy_key() {
rm -f /tmp/testkey* &> /dev/null || true
}
add_domain() {
dokku domains:add "$TEST_APP" "$1"
}