#!/usr/bin/env bash [[ " certs:add certs:generate certs:info certs:remove certs:update help certs:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" source "$PLUGIN_AVAILABLE_PATH/certs/functions" source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" is_tar_import() { [[ -t 0 ]] && return 1 return 0 } is_file_import() { local CRT_FILE="$1" local KEY_FILE="$2" if [[ $CRT_FILE ]] && [[ $KEY_FILE ]]; then if [[ ! -f $CRT_FILE ]]; then dokku_log_fail "CRT file specified not found, please check file paths" elif [[ ! -f $KEY_FILE ]]; then dokku_log_fail "KEY file specified not found, please check file paths" else return 0 fi fi return 1 } certs_set() { [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on" verify_app_name "$2" local APP="$2"; local CRT_FILE="$3"; local KEY_FILE="$4"; local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" 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 -t "dokku_certs_set.XXXX") pushd "$TEMP_DIR" &> /dev/null trap 'popd &> /dev/null || true; rm -rf $TEMP_DIR > /dev/null' RETURN tar xvf - <&0 CRT_FILE_SEARCH=$(find . -not -path '*/\.*' -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 . -not -path '*/\.*' -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 "$APP_SSL_PATH" cp "$CRT_FILE" "$APP_SSL_PATH/server.crt" cp "$KEY_FILE" "$APP_SSL_PATH/server.key" chmod 750 "$APP_SSL_PATH" chmod 640 "$APP_SSL_PATH/server.crt" "$APP_SSL_PATH/server.key" cd "$DOKKU_ROOT" 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"; APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" if [[ ! -f "$APP_SSL_PATH/server.key" ]] && [[ ! -f "$APP_SSL_PATH/server.crt" ]]; then TMP_WORK_DIR=$(mktemp -d -t "dokku_certs.XXXXXXXXX") pushd "$TMP_WORK_DIR" > /dev/null trap 'popd &> /dev/null || true; rm -rf "$TMP_WORK_DIR" > /dev/null' INT TERM EXIT 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 "$APP_SSL_PATH" dokku_log_info1 "Installing certificate and key..." mv -f "$TMP_WORK_DIR/server.key" "$TMP_WORK_DIR/server.crt" "$APP_SSL_PATH" chmod 750 "$APP_SSL_PATH" chmod 640 "$APP_SSL_PATH/server.key" "$APP_SSL_PATH/server.crt" [[ -n "$DOMAIN" ]] && (dokku domains:add "$APP" "$DOMAIN" || 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" plugn trigger post-domains-update "$APP" else dokku_log_fail "An app-specific SSL endpoint is not defined" fi ;; certs:update) certs_set "$@" ;; help | certs:help) 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