Merge pull request #1414 from progrium/1368_mh-certs-plugin

certs plugin
This commit is contained in:
Jose Diaz-Gonzalez
2015-09-04 13:26:43 -04:00
20 changed files with 404 additions and 133 deletions

View File

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

View File

@@ -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 <app> CRT KEY Add an ssl endpoint to an app. Can also import from a tarball on stdin.
certs:generate <app> DOMAIN Generate a key and certificate signing request (and self-signed certificate)
certs:info <app> Show certificate information for an ssl endpoint.
certs:remove <app> Remove an SSL Endpoint from an app.
certs:update <app> 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.

View File

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

View File

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

View File

@@ -64,6 +64,7 @@
<a href="http://progrium.viewdocs.io/dokku/process-management" class="list-group-item">Process management</a>
<a href="http://progrium.viewdocs.io/dokku/dns" class="list-group-item">DNS Configuration</a>
<a href="http://progrium.viewdocs.io/dokku/nginx" class="list-group-item">Nginx Configuration</a>
<a href="http://progrium.viewdocs.io/dokku/deployment/ssl-configuration" class="list-group-item">SSL Configuration</a>
<a href="http://progrium.viewdocs.io/dokku/remote-commands" class="list-group-item">Running Remote commands</a>
<a href="http://progrium.viewdocs.io/dokku/docker-options" class="list-group-item">Container Options</a>
<a href="http://progrium.viewdocs.io/dokku/dokku-events-logs" class="list-group-item">Dokku Event Logs</a>

View File

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

View File

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

View File

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

174
plugins/certs/commands Executable file
View File

@@ -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<<EOF
certs:add <app> 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 <app> DOMAIN, Generate a key and certificate signing request (and self-signed certificate)
certs:info <app>, Show certificate information for an ssl endpoint.
certs:key <app> CRT KEY [KEY ...], [NOT IMPLEMENTED] Print the correct key for the given certificate.
certs:remove <app>, Remove an SSL Endpoint from an app.
certs:rollback <app>, [NOT IMPLEMENTED] Rollback an SSL Endpoint for an app.
certs:update <app> CRT KEY, Update an SSL Endpoint on an app. Can also import from a tarball on stdin
EOF
;;
*)
exit $DOKKU_NOT_IMPLEMENTED_EXIT
;;
esac

44
plugins/certs/functions Executable file
View File

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

View File

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

View File

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

8
plugins/domains/functions Executable file
View File

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

View File

@@ -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 <<EOF
ssl_certificate $SSL_INUSE/server.crt;
ssl_certificate_key $SSL_INUSE/server.key;
NONSSL_VHOSTS=$(get_app_domains $APP)
if [[ -n "$(is_ssl_enabled $APP)" ]];then
SSL_HOSTNAME=$(get_ssl_hostnames $APP)
[[ -n "$SSL_HOSTNAME" ]] && SSL_HOSTNAME_REGEX=$(echo "$SSL_HOSTNAME" | xargs | sed 's|\.|\\.|g' | sed 's/\*/\[^\.\]\*/g' | sed 's/ /|/g')
if (egrep -q "^${SSL_HOSTNAME_REGEX}$" $VHOST_PATH); then
SSL_INUSE=true
else
dokku_log_info1 "No matching configured domains for $APP found in SSL certificate. Your app will not be configured with an SSL endpoint"
dokku_log_info1 "Please add appropriate domains via the dokku domains command"
dokku_log_info1 "Configured domains for app:"
for domain in $(echo $NONSSL_VHOSTS| xargs); do
dokku_log_info2 "$domain"
done
dokku_log_info1 "Domains found in SSL certificate:"
for domain in $(echo $SSL_HOSTNAME | xargs); do
dokku_log_info2 "$domain"
done
fi
if [[ "$(is_ssl_enabled $APP)" == "app" ]]; then
SSL_DIRECTIVES=$(cat <<EOF
ssl_certificate $APP_SSL_PATH/server.crt;
ssl_certificate_key $APP_SSL_PATH/server.key;
EOF
)
elif [[ -e "$WILDCARD_SSL/server.crt" ]] && [[ -e "$WILDCARD_SSL/server.key" ]]; then
SSL_INUSE="$WILDCARD_SSL"
SSL_DIRECTIVES=""
elif [[ "$(is_ssl_enabled $APP)" == "global" ]]; then
SSL_DIRECTIVES=""
fi
fi
NGINX_CONF=$(mktemp -t "nginx.conf.XXXXXX")
@@ -77,20 +96,8 @@ EOF
SCHEME="https"
[[ -z "$NGINX_TEMPLATE" ]] && NGINX_TEMPLATE="$PLUGIN_PATH/nginx-vhosts/templates/nginx.ssl.conf.template"
SSL_HOSTNAME=$(openssl x509 -in $SSL_INUSE/server.crt -noout -subject | tr '/' '\n' | grep CN= | cut -c4-)
if [[ -n "$SSL_HOSTNAME" ]]; then
SSL_HOSTNAME_REGEX=$(echo "$SSL_HOSTNAME" | sed 's|\.|\\.|g' | sed 's/\*/\[^\.\]\*/g')
[[ -z "$(egrep "^${SSL_HOSTNAME_REGEX}$" $VHOST_PATH)" ]] && [[ ! "$SSL_HOSTNAME" =~ ^\*.* ]] && echo "$SSL_HOSTNAME" >> $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<<EOF
nginx:import-ssl <app>, Imports a tarball from stdin; should contain server.crt and server.key
nginx:build-config <app>, (Re)builds nginx config for given app
EOF
;;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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)" {

View File

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