diff --git a/Makefile b/Makefile index 5d04d150295..6ac803847a4 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ else BUILD_STACK_TARGETS = build-in-docker endif -.PHONY: all apt-update install copyfiles man-db version plugins dependencies sshcommand pluginhook docker aufs stack count dokku-installer vagrant-acl-add vagrant-dokku +.PHONY: all apt-update install version copyfiles man-db plugins dependencies sshcommand pluginhook docker aufs stack count dokku-installer vagrant-acl-add vagrant-dokku include tests.mk include deb.mk @@ -28,7 +28,7 @@ include deb.mk all: # Type "make install" to install. -install: dependencies copyfiles plugin-dependencies plugins version +install: dependencies version copyfiles plugin-dependencies plugins release: deb-all package_cloud packer diff --git a/docs/deployment/ssl-configuration.md b/docs/deployment/ssl-configuration.md new file mode 100644 index 00000000000..327c296bc64 --- /dev/null +++ b/docs/deployment/ssl-configuration.md @@ -0,0 +1,91 @@ +# SSL Configuration + +> New as of 0.4.0 + +Dokku supports SSL/TLS certificate inspection and CSR/Self-signed certificate generation via the `certs` plugin. Note that whenever SSL/TLS support is enabled SPDY is also enabled. + +``` +certs:add CRT KEY Add an ssl endpoint to an app. Can also import from a tarball on stdin. +certs:generate DOMAIN Generate a key and certificate signing request (and self-signed certificate) +certs:info Show certificate information for an ssl endpoint. +certs:remove Remove an SSL Endpoint from an app. +certs:update CRT KEY Update an SSL Endpoint on an app. Can also import from a tarball on stdin +``` + +## Per-application certificate management + +Dokku provides built-in support for managing SSL certificates on a per-application basis. SSL is managed via nginx outside of application containers, and as such can be updated on-the-fly without rebuilding containers. At this time, applications only support a single SSL certificate at a time. To support multiple domains for a single application, wildcard certificate usage is encouraged. + +### Certificate setting + +The `certs:add` command can be used to push a `tar` containing a certificate `.crt` and `.key` file to a single application. The command should correctly handle cases where the `.crt` and `.key` are not named properly or are nested in a subdirectory of said `tar` file. You can import it as follows: + +```shell +tar cvf cert-key.tar server.crt server.key +# replace APP with your app name +dokku certs:add < cert-key.tar +``` + +### Certificate generation + +> Note: Using this method will create a self-signed certificate, which is only recommended for development or staging use, not production environments. + +The `certs:generate` command will walk you through the correct `openssl` commands to create a key, csr and a self-signed cert for a given app/domain. We automatically put the self-signed cert in place as well as add the specified domain to the application configuration. + +If you decide to obtain a CA signed certficate, you can import that certicate using the aformentioned `dokku certs:add` command. + +### Certificate information + +The `certs:info` command will simply inspect the install SSL cert and print out details. NOTE: The server-wide certificate will be inspect if installed and no app-specific certificate exists. + +``` +root@dokku:~/dokku# dokku certs:info node-js-app +-----> Fetching SSL Endpoint info for node-js-app... +-----> Certificate details: +=====> Common Name(s): +=====> test.dokku.me +=====> Expires At: Aug 24 23:32:59 2016 GMT +=====> Issuer: C=US, ST=California, L=San Francisco, O=dokku.me, CN=test.dokku.me +=====> Starts At: Aug 25 23:32:59 2015 GMT +=====> Subject: C=US; ST=California; L=San Francisco; O=dokku.me; CN=test.dokku.me +=====> SSL certificate is self signed. +``` + +### Certificate removal + +The `certs:remove` command only works on app-specific certificates. It will `rm` the app-specific tls directory, rebuild the nginx configuration, and reload nginx. + +## Global Certification + +Global certificate management is a manual process. To enable TLS connections for all your applications at once you will need a wildcard TLS certificate. + +To enable TLS across all apps, you can run the following commands: + +```shell +mkdir -p /home/dokku/tls +cp server.crt /home/dokku/tls/server.crt +cp server.key /home/dokku/tls/server.key +``` + +Next, you will want to enable the certificates by editing `/etc/nginx/conf.d/dokku.conf` and uncommenting these two lines (remove the `#`): + +``` +ssl_certificate /home/dokku/tls/server.crt; +ssl_certificate_key /home/dokku/tls/server.key; +``` + +The settings will take affect at the next deploy. If you would like to propagate the change to all apps immediately, you can also run the following command: + +```shell +dokku ps:restartall +``` + +Once TLS is enabled, the application will be accessible by `https://` (redirection from `http://` is applied as well). + +> Note: TLS will not be enabled unless the application's VHOST matches the certificate's name. (i.e. if you have a cert for `*.example.com` TLS won't be enabled for `something.example.org` or `example.net`) + +## HSTS Header + +The [HSTS header](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) is an HTTP header that can inform browsers that all requests to a given site should be made via HTTPS. dokku does not, by default, enable this header. It is thus left up to you, the user, to enable it for your site. + +Beware that if you enable the header and a subsequent deploy of your application results in an HTTP deploy (for whatever reason), the way the header works means that a browser will not attempt to request the HTTP version of your site if the HTTPS version fails. diff --git a/docs/development/plugin-creation.md b/docs/development/plugin-creation.md index 78f3e0f9ebf..13ee06120ec 100644 --- a/docs/development/plugin-creation.md +++ b/docs/development/plugin-creation.md @@ -19,7 +19,7 @@ source "$PLUGIN_PATH/common/functions" case "$1" in hello) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP); IMAGE=$(get_app_image_name $APP $IMAGE_TAG) verify_app_name "$APP" @@ -56,8 +56,7 @@ A few notes: ```shell IMAGE=$(docker images | grep "user/repo" | awk '{print $3}') if [[ -z $IMAGE ]]; then - echo "user/repo image not found... Did you run 'dokku plugins-install'?" - exit 1 + dokku_log_fail "user/repo image not found... Did you run 'dokku plugins-install'?" fi ``` @@ -80,4 +79,5 @@ A few notes: dokku config:set --no-restart APP KEY1=VALUE1 [KEY2=VALUE2 ...] dokku config:unset --no-restart APP KEY1 [KEY2 ...] ``` +- From time to time you may want to allow other plugins access to (some of) your plugin's functionality. You can expose this by including a `functions` file in your plugin for others to source. Consider all functions in that file to be publicly accessible by other plugins. Any functions not wished to be made "public" should reside within your pluginhook or commands files. - As of 0.4.0, we allow image tagging and deployment of said tagged images. Therefore, hard-coding of `$IMAGE` as `dokku/$APP` is no longer sufficient. Instead, for non `pre/post-build-*` plugins, use `get_running_image_tag()` & `get_app_image_name()` as sourced from common/functions. See [pluginhooks](http://progrium.viewdocs.io/dokku/development/pluginhooks) doc for examples. diff --git a/docs/nginx.md b/docs/nginx.md index 3491a9bd657..392070b6079 100644 --- a/docs/nginx.md +++ b/docs/nginx.md @@ -6,6 +6,10 @@ Dokku uses nginx as it's server for routing requests to specific applications. B Dokku provides easy TLS/SPDY support out of the box. This can be done app-by-app or for all subdomains at once. Note that whenever TLS support is enabled SPDY is also enabled. +### SSL Configuration + +In 0.4.0, SSL Configuration has been replaced by the [`certs` plugin](http://progrium.viewdocs.io/dokku/deployment/ssl-configuration)). For users of dokku 0.3.x, please refer to the following sections. + ### Per App To enable TLS connections to to one of your applications, do the following: diff --git a/docs/template.html b/docs/template.html index f837ae11783..fa646570e2f 100644 --- a/docs/template.html +++ b/docs/template.html @@ -64,6 +64,7 @@

Dokku

Process management DNS Configuration Nginx Configuration + SSL Configuration Running Remote commands Container Options Dokku Event Logs diff --git a/plugins/00_dokku-standard/commands b/plugins/00_dokku-standard/commands index bd986485109..3d183876097 100755 --- a/plugins/00_dokku-standard/commands +++ b/plugins/00_dokku-standard/commands @@ -101,8 +101,7 @@ case "$1" in trace) [[ -d $DOKKU_ROOT/.dokkurc ]] || mkdir -p $DOKKU_ROOT/.dokkurc [[ "$2" == "on" ]] || [[ "$2" == "off" ]] || { - echo "Valid trace options are [on/off]" - exit 1 + dokku_log_fail "Valid trace options are [on/off]" } if [[ "$2" == "on" ]]; then @@ -121,7 +120,7 @@ case "$1" in ;; ls) - dokku_apps=$(ls -d $DOKKU_ROOT/*/ 2>/dev/null) || (echo "You haven't deployed any applications yet" && exit 1) + dokku_apps=$(ls -d $DOKKU_ROOT/*/ 2>/dev/null) || (dokku_log_fail "You haven't deployed any applications yet") dokku_col_log_info1_quiet "App Name" "Container Type" "Container Id" "Status" @@ -143,7 +142,7 @@ case "$1" in ;; logs) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; verify_app_name "$2" if (is_deployed $APP); then @@ -169,7 +168,7 @@ case "$1" in ;; run) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP); IMAGE=$(get_app_image_name $APP $IMAGE_TAG) verify_app_name "$APP" @@ -185,7 +184,7 @@ case "$1" in ;; url | urls) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; verify_app_name "$2" eval "$(config_export app $APP)" @@ -217,10 +216,7 @@ case "$1" in ;; version) - cat "$DOKKU_ROOT/VERSION" || { - echo "Unable to determine dokku's version" 2>&1 - exit 1 - } + cat "$DOKKU_ROOT/VERSION" || dokku_log_fail "Unable to determine dokku's version" ;; help) diff --git a/plugins/apps/commands b/plugins/apps/commands index f2e29666ddc..47a1b304f34 100755 --- a/plugins/apps/commands +++ b/plugins/apps/commands @@ -9,8 +9,8 @@ case "$1" in ;; apps:create) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - [[ -d "$DOKKU_ROOT/$2" ]] && dokku_log_warn "Name is already taken" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + [[ -d "$DOKKU_ROOT/$2" ]] && dokku_log_fail "Name is already taken" APP="$2" mkdir -p "$DOKKU_ROOT/$APP" @@ -18,8 +18,8 @@ case "$1" in ;; apps:destroy) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - [[ "$2" == "tls" ]] && echo "Unable to destroy tls directory" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + [[ "$2" == "tls" ]] && dokku_log_fail "Unable to destroy tls directory" [[ "$3" == "force" ]] && DOKKU_APPS_FORCE_DELETE=1 APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP) verify_app_name "$APP" @@ -32,8 +32,7 @@ case "$1" in read -p "> " app_name if [[ "$app_name" != "$APP" ]]; then - dokku_log_warn "Confirmation did not match $APP. Aborted." - exit 1 + dokku_log_fail "Confirmation did not match $APP. Aborted." fi fi diff --git a/plugins/backup/commands b/plugins/backup/commands index d964cde6eda..1d1cf065537 100755 --- a/plugins/backup/commands +++ b/plugins/backup/commands @@ -50,14 +50,12 @@ case "$1" in tar xf $INPUT_FILE --directory=$BACKUP_TMP_DIR if [[ ! -f $BACKUP_TMP_DIR/.dokku_backup_version ]]; then - echo "Unable to determine backup version" - exit 1 + dokku_log_fail "Unable to determine backup version" fi VERSION=$(< $BACKUP_TMP_DIR/.dokku_backup_version) if [[ $VERSION -ne 1 ]]; then - echo "Unknown format version $VERSION" - exit 1 + dokku_log_fail "Unknown format version $VERSION" fi echo "Importing a version $VERSION backup..." @@ -69,8 +67,7 @@ case "$1" in if $force; then echo "-f used. Ignoring warnings." else - echo "Archive did not pass sanity checks. Use -f to import anyway" >&2 - exit 1 + dokku_log_fail "Archive did not pass sanity checks. Use -f to import anyway" fi fi diff --git a/plugins/certs/commands b/plugins/certs/commands new file mode 100755 index 00000000000..a46e9865b91 --- /dev/null +++ b/plugins/certs/commands @@ -0,0 +1,174 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$(dirname $0)/../common/functions" +source "$(dirname $0)/functions" + +is_tar_import() { + [[ -t 0 ]] && return 1 + return 0 +} + +is_file_import() { + local CRT_FILE="$3" + local KEY_FILE="$4" + + if [[ -f $CRT_FILE ]] && [[ -f $KEY_FILE ]]; then + return 0 + fi + + return 1 +} + +certs_set() { + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + verify_app_name "$2" + APP="$2"; CRT_FILE="$3"; KEY_FILE="$4" + + is_file_import $CRT_FILE $KEY_FILE || is_tar_import || dokku_log_fail "Tar archive containing server.crt and server.key expected on stdin" + + if is_tar_import; then + TEMP_DIR=$(mktemp -d) + cd $TEMP_DIR + tar xvf - <&0 + + CRT_FILE_SEARCH=$(find . -type f -name "*.crt") + CRT_FILE_COUNT=$(printf "%s" "$CRT_FILE_SEARCH" | grep -c '^') + if [[ $CRT_FILE_COUNT -lt 1 ]]; then + dokku_log_fail "Tar archive is missing .crt file" + elif [[ $CRT_FILE_COUNT -gt 1 ]]; then + dokku_log_fail "Tar archive contains more than one .crt file" + else + CRT_FILE=$CRT_FILE_SEARCH + fi + + KEY_FILE_SEARCH=$(find . -type f -name "*.key") + KEY_FILE_COUNT=$(printf "%s" "$KEY_FILE_SEARCH" | grep -c '^') + if [[ $KEY_FILE_COUNT -lt 1 ]]; then + dokku_log_fail "Tar archive is missing .key file" + elif [[ $KEY_FILE_COUNT -gt 1 ]]; then + dokku_log_fail "Tar archive contains more than one .key file" + else + KEY_FILE=$KEY_FILE_SEARCH + fi + fi + + mkdir -p "$DOKKU_ROOT/$APP/tls" + mv "$CRT_FILE" "$DOKKU_ROOT/$APP/tls/server.crt" + mv "$KEY_FILE" "$DOKKU_ROOT/$APP/tls/server.key" + cd $DOKKU_ROOT + rm -rf $TEMP_DIR + dokku nginx:build-config $APP +} + +case "$1" in + certs:add) + certs_set "$@" + ;; + + certs:generate) + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + verify_app_name "$2" + APP="$2"; DOMAIN="$3"; SSL_PATH="$DOKKU_ROOT/$APP/tls" + + if [[ ! -f "$SSL_PATH/server.key" ]] && [[ ! -f "$SSL_PATH/server.crt" ]]; then + TMP_WORK_DIR=$(mktemp -d -t "dokku_certs.XXXXXXXXX") + trap 'rm -rf "$TMP_WORK_DIR" > /dev/null' INT TERM EXIT + + pushd $TMP_WORK_DIR > /dev/null + + openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 + openssl rsa -passin pass:x -in server.pass.key -out server.key + openssl req -new -key server.key -out server.csr + openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt + + mkdir -p "$DOKKU_ROOT/$APP/tls" + dokku_log_info1 "Installing certificate and key..." + mv -f $TMP_WORK_DIR/server.key $TMP_WORK_DIR/server.crt $SSL_PATH + [[ -n "$DOMAIN" ]] && (dokku domains:add $APP $DOMAIN || dokku nginx:build-config $APP) + dokku_log_info1 "The following is a certificate signing request that can be used" + dokku_log_info1 "to generate an 'officially' signed SSL certificate for $APP at $DOMAIN" + dokku_log_info1 "by a CA of your choosing." + cat server.csr + else + dokku_log_info1 "$APP has an SSL endpoint already defined" + fi + ;; + + certs:info) + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + verify_app_name "$2" + APP="$2"; SSL_TYPE=$(is_ssl_enabled $APP) + case "$SSL_TYPE" in + app) + SSL_PATH="$DOKKU_ROOT/$APP/tls" + ;; + + global) + SSL_PATH="$DOKKU_ROOT/tls" + ;; + + *) + ;; + esac + + if [[ -n "$SSL_PATH" ]]; then + dokku_log_info1 "Fetching SSL Endpoint info for $APP..." + dokku_log_info1 "Certificate details:" + dokku_log_info2 "Common Name(s): " + + for domain in $(get_ssl_hostnames $APP | xargs); do + dokku_log_info2 " $domain" + done + + dokku_log_info2 "Expires At: $(openssl x509 -in $SSL_PATH/server.crt -noout -text | grep "Not After :" | awk -F " : " '{ print $2 }')" + dokku_log_info2 "Issuer: $(openssl x509 -in $SSL_PATH/server.crt -noout -text | grep "Issuer:" | xargs | sed -e "s/Issuer: //g")" + dokku_log_info2 "Starts At: $(openssl x509 -in $SSL_PATH/server.crt -noout -text | grep "Not Before:" | awk -F ": " '{ print $2 }')" + dokku_log_info2 "Subject: $(openssl x509 -in $SSL_PATH/server.crt -noout -subject | sed -e "s:subject= ::g"| sed -e "s:^/::g" | sed -e "s:/:; :g")" + SSL_VERIFY_OUTPUT="$(openssl verify -verbose -purpose sslserver $SSL_PATH/server.crt | awk -F ':' '{ print $2 }' | tail -1 | xargs || true)" + if [[ "$SSL_VERIFY_OUTPUT" == "OK" ]]; then + SSL_SELF_SIGNED="verified by a certificate authority." + else + SSL_SELF_SIGNED="self signed." + fi + dokku_log_info2 "SSL certificate is $SSL_SELF_SIGNED" + else + dokku_log_info1 "$APP does not have an SSL endpoint" + fi + ;; + + certs:remove) + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" + verify_app_name "$2" + APP="$2"; APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" + + if [[ -d "$APP_SSL_PATH" ]]; then + dokku_log_info1 "Removing SSL endpoint from $APP" + rm -rf $APP_SSL_PATH + pluginhook post-domains-update $APP + else + dokku_log_info1 "An app-specific SSL endpoint is not defined" + fi + ;; + + certs:update) + certs_set "$@" + ;; + + help | certs:help) + cat && cat< CRT KEY, Add an ssl endpoint to an app. Can also import from a tarball on stdin + certs:chain CRT [CRT ...], [NOT IMPLEMENTED] Print the ordered and complete chain for the given certificate. + certs:generate DOMAIN, Generate a key and certificate signing request (and self-signed certificate) + certs:info , Show certificate information for an ssl endpoint. + certs:key CRT KEY [KEY ...], [NOT IMPLEMENTED] Print the correct key for the given certificate. + certs:remove , Remove an SSL Endpoint from an app. + certs:rollback , [NOT IMPLEMENTED] Rollback an SSL Endpoint for an app. + certs:update CRT KEY, Update an SSL Endpoint on an app. Can also import from a tarball on stdin +EOF + ;; + + *) + exit $DOKKU_NOT_IMPLEMENTED_EXIT + ;; + +esac diff --git a/plugins/certs/functions b/plugins/certs/functions new file mode 100755 index 00000000000..e125c72edd0 --- /dev/null +++ b/plugins/certs/functions @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_PATH/common/functions" + +# returns 'global', 'app', 'false' +# if both are configured, app trumps global +is_ssl_enabled() { + local APP=$1; verify_app_name $APP + APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" + WILDCARD_SSL_PATH="$DOKKU_ROOT/tls" + + if [[ -e "$APP_SSL_PATH/server.crt" ]] && [[ -e "$APP_SSL_PATH/server.key" ]]; then + echo app + return 0 + elif [[ -e "$WILDCARD_SSL_PATH/server.crt" ]] && [[ -e "$WILDCARD_SSL_PATH/server.key" ]]; then + echo global + return 0 + else + return 1 + fi +} + +get_ssl_hostnames() { + local APP=$1; verify_app_name $APP + case "$(is_ssl_enabled $APP)" in + app) + SSL_PATH="$DOKKU_ROOT/$APP/tls" + ;; + + global) + SSL_PATH="$DOKKU_ROOT/tls" + ;; + esac + + SSL_HOSTNAME=$(openssl x509 -in $SSL_PATH/server.crt -noout -subject | tr '/' '\n' | grep CN= | cut -c4-) + SSL_HOSTNAME_ALT=$(openssl x509 -in $SSL_PATH/server.crt -noout -text | grep --after-context=1 '509v3 Subject Alternative Name:' | tail -n 1 | sed -e "s/[[:space:]]*DNS://g" | tr ',' '\n' || true) + if [[ -n "$SSL_HOSTNAME_ALT" ]]; then + SSL_HOSTNAMES="${SSL_HOSTNAME}\n${SSL_HOSTNAME_ALT}" + else + SSL_HOSTNAMES=$SSL_HOSTNAME + fi + echo -e $SSL_HOSTNAMES + return 0 +} diff --git a/plugins/config/functions b/plugins/config/functions index bfdd610b2a7..a07d23fbcc2 100644 --- a/plugins/config/functions +++ b/plugins/config/functions @@ -38,7 +38,7 @@ config_parse_args() { if [[ "$DOKKU_CONFIG_TYPE" = "app" ]]; then if [[ -z $APP ]]; then - echo "Please specify an app to run the command on" >&2 && exit 1 + dokku_log_fail "Please specify an app to run the command on" else verify_app_name "$2" fi @@ -105,7 +105,7 @@ config_all() { is_config_export "$@" && config_export "$DOKKU_CONFIG_TYPE" "$APP" && exit 0 [[ "$APP" ]] && DOKKU_CONFIG_TYPE=$APP - [[ ! -s $ENV_FILE ]] && echo "no config vars for $DOKKU_CONFIG_TYPE" && exit 1 + [[ ! -s $ENV_FILE ]] && dokku_log_fail "no config vars for $DOKKU_CONFIG_TYPE" VARS=$(grep -Eo "export ([a-zA-Z_][a-zA-Z0-9_]*=.*)" $ENV_FILE | cut -d" " -f2-) diff --git a/plugins/domains/commands b/plugins/domains/commands index 73acd648e10..9f4f78a11d6 100755 --- a/plugins/domains/commands +++ b/plugins/domains/commands @@ -2,6 +2,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_PATH/common/functions" source "$PLUGIN_PATH/config/functions" +source "$PLUGIN_PATH/domains/functions" RE_IPV4="([0-9]{1,3}[\.]){3}[0-9]{1,3}" @@ -25,21 +26,21 @@ RE_IPV6="${RE_IPV6}\$" case "$1" in domains) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" verify_app_name "$2" APP="$2" dokku domains:setup $APP if [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then dokku_log_info2_quiet "$APP Domain Names" - cat "$DOKKU_ROOT/$APP/VHOST" + get_app_domains "$APP" else dokku_log_fail "No domain names set for $APP" fi ;; domains:setup) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" verify_app_name "$2" APP="$2"; VHOST_PATH="$DOKKU_ROOT/$APP/VHOST" @@ -72,7 +73,7 @@ case "$1" in ;; domains:add) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" verify_app_name "$2" APP="$2" @@ -102,7 +103,7 @@ case "$1" in ;; domains:clear) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" verify_app_name "$2" APP="$2" @@ -114,7 +115,7 @@ case "$1" in ;; domains:remove) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" verify_app_name "$2" APP="$2" diff --git a/plugins/domains/functions b/plugins/domains/functions new file mode 100755 index 00000000000..f79e889ee52 --- /dev/null +++ b/plugins/domains/functions @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$(dirname $0)/../common/functions" + +get_app_domains() { + local APP=$1; verify_app_name $APP + cat "$DOKKU_ROOT/$APP/VHOST" +} diff --git a/plugins/nginx-vhosts/commands b/plugins/nginx-vhosts/commands index d2f40040240..e3dd21c4d12 100755 --- a/plugins/nginx-vhosts/commands +++ b/plugins/nginx-vhosts/commands @@ -1,7 +1,9 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_PATH/common/functions" +source "$PLUGIN_PATH/certs/functions" source "$PLUGIN_PATH/config/functions" +source "$PLUGIN_PATH/domains/functions" validate_nginx () { set +e @@ -32,8 +34,8 @@ case "$1" in verify_app_name "$APP" VHOST_PATH="$DOKKU_ROOT/$APP/VHOST" URLS_PATH="$DOKKU_ROOT/$APP/URLS" - WILDCARD_SSL="$DOKKU_ROOT/tls" - SSL="$DOKKU_ROOT/$APP/tls" + WILDCARD_SSL_PATH="$DOKKU_ROOT/tls" + APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" APP_NGINX_TEMPLATE="$DOKKU_ROOT/$APP/nginx.conf.template" if [[ -z "$DOKKU_APP_LISTEN_PORT" ]] && [[ -z "$DOKKU_APP_LISTEN_IP" ]]; then @@ -58,17 +60,34 @@ case "$1" in [[ -f "$APP_NGINX_TEMPLATE" ]] && NGINX_TEMPLATE="$APP_NGINX_TEMPLATE" && NGINX_CUSTOM_TEMPLATE="true" && dokku_log_info1 'Overriding default nginx.conf with detected nginx.conf.template' if [[ ! -n "$NO_VHOST" ]] && [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then - NONSSL_VHOSTS=$(cat $VHOST_PATH) - if [[ -e "$SSL/server.crt" ]] && [[ -e "$SSL/server.key" ]]; then - SSL_INUSE="$SSL" - SSL_DIRECTIVES=$(cat <> $VHOST_PATH - fi - - SSL_HOSTNAME_ALT=$(openssl x509 -in $SSL_INUSE/server.crt -noout -text | grep --after-context=1 '509v3 Subject Alternative Name:' | tail -n 1 | sed -e "s/[[:space:]]*DNS://g" | tr ',' '\n' || true) - if [[ -n "$SSL_HOSTNAME_ALT" ]]; then - SSL_HOSTNAME_ALT_REGEX=$(echo "$SSL_HOSTNAME_ALT" | sed 's|\.|\\.|g' | sed 's/\*/\[^\.\]\*/g') - [[ -z "$(egrep "^${SSL_HOSTNAME_ALT_REGEX}$" $VHOST_PATH)" ]] && [[ ! "$SSL_HOSTNAME_ALT" =~ ^\*.* ]] && echo "$SSL_HOSTNAME_ALT" >> $VHOST_PATH - fi - - SSL_VHOSTS=$(egrep "^${SSL_HOSTNAME_REGEX}$|^${SSL_HOSTNAME_ALT_REGEX}$" $VHOST_PATH || exit 0) - NONSSL_VHOSTS=$(egrep -v "^${SSL_HOSTNAME_REGEX}$|^${SSL_HOSTNAME_ALT_REGEX}$" $VHOST_PATH || exit 0) + SSL_VHOSTS=$(egrep "^${SSL_HOSTNAME_REGEX}$" $VHOST_PATH || true) + NONSSL_VHOSTS=$(egrep -v "^${SSL_HOSTNAME_REGEX}$" $VHOST_PATH || true) while read line; do [[ -z "$line" ]] && continue @@ -152,49 +159,8 @@ EOF fi fi ;; - - nginx:import-ssl) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 - verify_app_name "$2" - [[ -t 0 ]] && echo "Tar archive containing server.crt and server.key expected on stdin" && exit 1 - APP="$2" - - TEMP_DIR=$(mktemp -d) - cd $TEMP_DIR - tar xvf - <&0 - - CRT_FILE_SEARCH=$(find . -type f -name "*.crt") - CRT_FILE_COUNT=$(printf "%s" "$CRT_FILE_SEARCH" | grep -c '^') - if [[ $CRT_FILE_COUNT -lt 1 ]]; then - echo "Tar archive is missing .crt file" && exit 1 - elif [[ $CRT_FILE_COUNT -gt 1 ]]; then - echo "Tar archive contains more than one .crt file" && exit 1 - else - CRT_FILE=$CRT_FILE_SEARCH - fi - - KEY_FILE_SEARCH=$(find . -type f -name "*.key") - KEY_FILE_COUNT=$(printf "%s" "$KEY_FILE_SEARCH" | grep -c '^') - if [[ $KEY_FILE_COUNT -lt 1 ]]; then - echo "Tar archive is missing .key file" && exit 1 - elif [[ $KEY_FILE_COUNT -gt 1 ]]; then - echo "Tar archive contains more than one .key file" && exit 1 - else - KEY_FILE=$KEY_FILE_SEARCH - fi - - - mkdir -p "$DOKKU_ROOT/$APP/tls" - mv "$CRT_FILE" "$DOKKU_ROOT/$APP/tls/server.crt" - mv "$KEY_FILE" "$DOKKU_ROOT/$APP/tls/server.key" - cd $DOKKU_ROOT - rm -rf $TEMP_DIR - dokku nginx:build-config $APP - ;; - help | nginx:help) cat && cat<, Imports a tarball from stdin; should contain server.crt and server.key nginx:build-config , (Re)builds nginx config for given app EOF ;; diff --git a/plugins/ps/commands b/plugins/ps/commands index 0acf81ac537..a24b10a588e 100755 --- a/plugins/ps/commands +++ b/plugins/ps/commands @@ -5,7 +5,7 @@ source "$(dirname $0)/functions" case "$1" in ps) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; verify_app_name "$APP"; DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids $APP) ! (is_deployed $APP) && echo "App $APP has not been deployed" && exit 0 @@ -17,7 +17,7 @@ case "$1" in ;; ps:start) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP); verify_app_name "$APP" @@ -31,7 +31,7 @@ case "$1" in ;; ps:stop) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; DOKKU_APP_RUNNING_CONTAINER_IDS=$(get_app_running_container_ids $APP) verify_app_name "$APP" @@ -46,7 +46,7 @@ case "$1" in ;; ps:rebuild) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; verify_app_name "$APP" pluginhook -p receive-app $APP @@ -63,7 +63,7 @@ case "$1" in ;; ps:restart) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP) verify_app_name "$APP" @@ -83,7 +83,7 @@ case "$1" in ;; ps:scale) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG=$(get_running_image_tag $APP) verify_app_name "$APP" diff --git a/plugins/ps/functions b/plugins/ps/functions index f0e5aef7840..c21498f0534 100755 --- a/plugins/ps/functions +++ b/plugins/ps/functions @@ -2,7 +2,7 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_PATH/common/functions" -print_dokku_scale_file(){ +print_dokku_scale_file() { local APP="$1"; local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" while read line || [ -n "$line" ] do diff --git a/plugins/tags/commands b/plugins/tags/commands index a77f8bc93dc..1d8fb3cefaa 100755 --- a/plugins/tags/commands +++ b/plugins/tags/commands @@ -5,7 +5,7 @@ source "$(dirname $0)/functions" case "$1" in tags) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_REPO=$(get_app_image_repo $APP) verify_app_name "$APP" @@ -14,7 +14,7 @@ case "$1" in ;; tags:create) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG="$3"; IMAGE_REPO=$(get_app_image_repo $APP) verify_app_name "$APP" @@ -24,7 +24,7 @@ case "$1" in ;; tags:deploy) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG="$3" verify_app_name "$APP" @@ -32,7 +32,7 @@ case "$1" in ;; tags:destroy) - [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" APP="$2"; IMAGE_TAG="$3"; IMAGE_REPO=$(get_app_image_repo $APP) verify_app_name "$2" diff --git a/tests/test_deploy b/tests/test_deploy index f1b46a0a650..07b0a27e4fd 100755 --- a/tests/test_deploy +++ b/tests/test_deploy @@ -5,17 +5,17 @@ SELF=$(which $0); APP="$1"; TARGET="$2"; FORWARDED_PORT="$3"; SHOULD_FAIL="$4" REMOTE="dokku@$TARGET" REPO="test-$(basename $APP)-$RANDOM" -destroy_app(){ +destroy_app() { echo $REPO | ssh $REMOTE apps:destroy $REPO } -failed(){ +failed() { echo "************ $1 failed ************" destroy_app exit 1 } -succeeded(){ +succeeded() { echo "************ $1 succeeded but should have failed ************" destroy_app exit 1 diff --git a/tests/unit/core.bats b/tests/unit/core.bats index 99c545a99d4..0d2641f27b9 100644 --- a/tests/unit/core.bats +++ b/tests/unit/core.bats @@ -99,9 +99,9 @@ build_nginx_config() { setup_test_tls assert_urls "https://dokku.me" build_nginx_config - assert_urls "https://node-js-app.dokku.me" "http://${TEST_APP}.dokku.me" + assert_urls "http://${TEST_APP}.dokku.me" add_domain "test.dokku.me" - assert_urls "https://node-js-app.dokku.me" "http://${TEST_APP}.dokku.me" "http://test.dokku.me" + assert_urls "http://${TEST_APP}.dokku.me" "http://test.dokku.me" } @test "(core) urls (wildcard ssl)" { diff --git a/tests/unit/nginx-vhosts.bats b/tests/unit/nginx-vhosts.bats index d08e7701c35..fe2cf114a73 100644 --- a/tests/unit/nginx-vhosts.bats +++ b/tests/unit/nginx-vhosts.bats @@ -74,9 +74,9 @@ assert_error_log() { } @test "(nginx-vhosts) logging" { - deploy_app - assert_access_log ${TEST_APP} - assert_error_log ${TEST_APP} + deploy_app + assert_access_log ${TEST_APP} + assert_error_log ${TEST_APP} } @test "(nginx-vhosts) nginx:build-config (wildcard SSL)" { @@ -109,21 +109,11 @@ assert_error_log() { assert_output "0" } -@test "(nginx-vhosts) nginx:build-config (with SSL CN mismatch)" { - setup_test_tls - deploy_app - assert_ssl_domain "node-js-app.dokku.me" -} - -@test "(nginx-vhosts) nginx:build-config (with SSL CN mismatch & custom nginx template)" { - setup_test_tls - custom_ssl_nginx_template - deploy_app - assert_ssl_domain "node-js-app.dokku.me" -} - @test "(nginx-vhosts) nginx:build-config (with SSL and Multiple SANs)" { setup_test_tls_with_sans + add_domain "test.dokku.me" + add_domain "www.test.dokku.me" + add_domain "www.test.app.dokku.me" deploy_app assert_ssl_domain "test.dokku.me" assert_ssl_domain "www.test.dokku.me"