diff --git a/docs/nginx.md b/docs/nginx.md index 1490a7c3b..a27347ab1 100644 --- a/docs/nginx.md +++ b/docs/nginx.md @@ -2,6 +2,14 @@ Dokku uses nginx as it's server for routing requests to specific applications. By default, access and error logs are written for each app to `/var/log/nginx/${APP}-access.log` and `/var/log/nginx/${APP}-error.log` respectively +``` +nginx:access-logs [-t] Show the nginx access logs for an application (-t follows) +nginx:build-config (Re)builds nginx config for given app +nginx:disable disable nginx for an application (forces container binding to external interface) +nginx:enable enable nginx for an application +nginx:error-logs [-t] Show the nginx error logs for an application (-t follows) +``` + ## TLS/SPDY support 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. @@ -166,13 +174,13 @@ You can also use the built-in `domains` plugin to handle: ### Disabling VHOSTS -If desired, it is possible to disable vhosts by setting the `NO_VHOST` environment variable: +If desired, it is possible to disable vhosts by setting the environment variable `NO_VHOST=1`: ```shell dokku config:set myapp NO_VHOST=1 ``` -On subsequent deploys, the nginx virtualhost will be discarded. This is useful when deploying internal-facing services that should not be publicly routeable. +On subsequent deploys, the nginx virtualhost will be discarded. This is useful when deploying internal-facing services that should not be publicly routeable. As of 0.4.0, nginx will still be configured to proxy your app on some random high port. This allows internal services to maintain the same port between deployments. You may change this port by setting `DOKKU_NGINX_PORT` and/or `DOKKU_NGINX_SSL_PORT` (for services configured to use SSL.) ### Domains plugin diff --git a/dokku b/dokku index c3dd36337..b15993024 100755 --- a/dokku +++ b/dokku @@ -80,6 +80,16 @@ case "$1" in DOKKU_RESTART_LIMIT=$(dokku config:get $APP DOKKU_RESTART_LIMIT || echo 10) DOKKU_RESTART_LIMIT=${DOKKU_RESTART_LIMIT:=10} + DOKKU_APP_SKIP_ALL_CHECKS=$(dokku config:get $APP DOKKU_SKIP_ALL_CHECKS || true) + DOKKU_APP_SKIP_DEFAULT_CHECKS=$(dokku config:get $APP DOKKU_SKIP_DEFAULT_CHECKS || true) + DOKKU_GLOBAL_SKIP_ALL_CHECKS=$(dokku config:get --global DOKKU_SKIP_ALL_CHECKS || true) + DOKKU_GLOBAL_SKIP_DEFAULT_CHECKS=$(dokku config:get --global DOKKU_SKIP_DEFAULT_CHECKS || true) + + DOKKU_SKIP_ALL_CHECKS=${DOKKU_APP_SKIP_ALL_CHECKS:="$DOKKU_GLOBAL_SKIP_ALL_CHECKS"} + DOKKU_SKIP_DEFAULT_CHECKS=${DOKKU_APP_SKIP_DEFAULT_CHECKS:="$DOKKU_GLOBAL_SKIP_DEFAULT_CHECKS"} + + DOKKU_DEFAULT_DOCKER_ARGS=$(: | plugn trigger docker-args-deploy $APP $IMAGE_TAG) + while read line || [[ -n "$line" ]]; do TRIM=${line%#*} PROC_TYPE=${TRIM%%=*} @@ -93,9 +103,9 @@ case "$1" in DOKKU_PORT_FILE="$DOKKU_ROOT/$APP/PORT.$PROC_TYPE.$CONTAINER_INDEX" # start the app - DOCKER_ARGS=" -e DYNO='$PROC_TYPE.$CONTAINER_INDEX' " + DOCKER_ARGS="$DOKKU_DEFAULT_DOCKER_ARGS" + DOCKER_ARGS+=" -e DYNO='$PROC_TYPE.$CONTAINER_INDEX' " DOCKER_ARGS+=" --restart=on-failure:$DOKKU_RESTART_LIMIT" - DOCKER_ARGS+=$(: | plugn trigger docker-args-deploy $APP $IMAGE_TAG) [[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true " BIND_EXTERNAL=$(plugn trigger bind-external-ip $APP) @@ -127,14 +137,6 @@ case "$1" in kill -9 $$ } - DOKKU_APP_SKIP_ALL_CHECKS=$(dokku config:get $APP DOKKU_SKIP_ALL_CHECKS || true) - DOKKU_APP_SKIP_DEFAULT_CHECKS=$(dokku config:get $APP DOKKU_SKIP_DEFAULT_CHECKS || true) - DOKKU_GLOBAL_SKIP_ALL_CHECKS=$(dokku config:get --global DOKKU_SKIP_ALL_CHECKS || true) - DOKKU_GLOBAL_SKIP_DEFAULT_CHECKS=$(dokku config:get --global DOKKU_SKIP_DEFAULT_CHECKS || true) - - DOKKU_SKIP_ALL_CHECKS=${DOKKU_APP_SKIP_ALL_CHECKS:="$DOKKU_GLOBAL_SKIP_ALL_CHECKS"} - DOKKU_SKIP_DEFAULT_CHECKS=${DOKKU_APP_SKIP_DEFAULT_CHECKS:="$DOKKU_GLOBAL_SKIP_DEFAULT_CHECKS"} - # run checks first, then post-deploy hooks, which switches Nginx traffic if [[ "$DOKKU_SKIP_ALL_CHECKS" = "true" ]]; then dokku_log_info1 "Skipping pre-flight checks" diff --git a/plugins/00_dokku-standard/commands b/plugins/00_dokku-standard/commands index af5e43770..a3dc43779 100755 --- a/plugins/00_dokku-standard/commands +++ b/plugins/00_dokku-standard/commands @@ -1,7 +1,9 @@ #!/usr/bin/env bash +[[ " build release trace delete ls logs run url urls version 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/config/functions" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" case "$1" in build) @@ -189,12 +191,22 @@ case "$1" in SCHEME="https" fi - if [[ -f "$DOKKU_ROOT/VHOST" ]] && [[ "$NO_VHOST" != "1" ]]; then - echo "$SCHEME://$(< "$DOKKU_ROOT/VHOST")" - else + if [[ "$(is_app_vhost_enabled $APP)" == "false" ]]; then for PORT_FILE in $DOKKU_ROOT/$APP/PORT.*; do - echo "$SCHEME://$(< "$DOKKU_ROOT/HOSTNAME"):$(< "$PORT_FILE")" + echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$(< "$PORT_FILE") (container)" done + + DOKKU_NGINX_PORT=$(config_get $APP DOKKU_NGINX_PORT || true) + DOKKU_NGINX_SSL_PORT=$(config_get $APP DOKKU_NGINX_SSL_PORT || true) + + if [[ -n "$DOKKU_NGINX_PORT" ]]; then + echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$DOKKU_NGINX_PORT (nginx)" + fi + if [[ -n "$DOKKU_NGINX_SSL_PORT" ]]; then + echo "https://$(< "$DOKKU_ROOT/HOSTNAME"):$DOKKU_NGINX_SSL_PORT (nginx-ssl)" + fi + else + echo "$SCHEME://$(< "$DOKKU_ROOT/VHOST")" fi ;; diff --git a/plugins/20_events/commands b/plugins/20_events/commands index 11f3a2b15..582b5c38f 100755 --- a/plugins/20_events/commands +++ b/plugins/20_events/commands @@ -1,9 +1,8 @@ #!/usr/bin/env bash +[[ " events events:on events:off events:list help events:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -PLUGIN_DIR="$(dirname $0)" - case "$1" in events) if [[ -f $DOKKU_EVENTS_LOGFILE ]]; then @@ -27,6 +26,7 @@ case "$1" in ;; events:list) + PLUGIN_DIR="$(dirname $0)" if [[ "$DOKKU_EVENTS" ]]; then logged="$(find $PLUGIN_DIR -type l -printf '%f ' | sort)" dokku_col_log_info2_quiet "Events currently logged" diff --git a/plugins/apps/commands b/plugins/apps/commands index 93052e0d0..a73e97975 100755 --- a/plugins/apps/commands +++ b/plugins/apps/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " apps apps:create apps:destroy help apps:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" diff --git a/plugins/backup/commands b/plugins/backup/commands index 2fa160979..90f28a4ad 100755 --- a/plugins/backup/commands +++ b/plugins/backup/commands @@ -1,10 +1,10 @@ #!/usr/bin/env bash +[[ " backup:export backup:import help backup:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -CURRENT_BACKUP_VERSION=1 - case "$1" in backup:export) + CURRENT_BACKUP_VERSION=1 OUTPUT_FILE="$2" BACKUP_DIR="$DOKKU_ROOT" diff --git a/plugins/certs/commands b/plugins/certs/commands index ba6e62bc0..870cd48cf 100755 --- a/plugins/certs/commands +++ b/plugins/certs/commands @@ -1,7 +1,9 @@ #!/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 @@ -57,7 +59,7 @@ certs_set() { mv "$KEY_FILE" "$DOKKU_ROOT/$APP/tls/server.key" cd $DOKKU_ROOT rm -rf $TEMP_DIR - dokku nginx:build-config $APP + nginx_build_config $APP } case "$1" in @@ -84,7 +86,7 @@ case "$1" in 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) + [[ -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." diff --git a/plugins/common/functions b/plugins/common/functions index fecfce1ef..69e4c6315 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -330,3 +330,15 @@ docker_cleanup() { # shellcheck disable=SC2046 docker rmi $(docker images -f 'dangling=true' -q) &> /dev/null & } + +get_available_port() { + while true; do + local port=$(shuf -i 1025-65535 -n 1) + if ! nc -z 0.0.0.0 $port; then + echo $port + return 0 + else + continue + fi + done +} diff --git a/plugins/config/commands b/plugins/config/commands index 191af9f52..f5b929449 100755 --- a/plugins/config/commands +++ b/plugins/config/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " config config:get config:set config:unset help config: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/config/functions" diff --git a/plugins/config/functions b/plugins/config/functions index eeb0db2f9..766de5103 100644 --- a/plugins/config/functions +++ b/plugins/config/functions @@ -214,6 +214,7 @@ config_unset() { ENV_TEMP=$(echo "${ENV_TEMP}" | sed "/^export $var=/ d") config_write "$ENV_TEMP" + [[ "$var" == "NO_VHOST" ]] && config_set --no-restart $APP NO_VHOST=0 && DOKKU_CONFIG_RESTART=true done if [[ "$DOKKU_CONFIG_RESTART" == "true" ]]; then diff --git a/plugins/docker-options/commands b/plugins/docker-options/commands index b7f63c55d..2a9589978 100755 --- a/plugins/docker-options/commands +++ b/plugins/docker-options/commands @@ -1,11 +1,8 @@ #!/usr/bin/env bash +[[ " docker-options docker-options:add docker-options:remove help docker-options:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -PHASES=(build deploy run) - -FILE_PREFIX="DOCKER_OPTIONS_" - get_app() { [[ -z $1 ]] && dokku_log_fail "Please specify an app to run the command on" verify_app_name "$1" @@ -25,17 +22,19 @@ get_phases() { verify_phase() { local phase - for phase in "${PHASES[@]}"; do + local phases=(build deploy run) + for phase in "${phases[@]}"; do if [[ "$phase" = "$1" ]]; then return 0 fi done - dokku_log_fail "Phase(s) must be one of [${PHASES[@]}]" + dokku_log_fail "Phase(s) must be one of [${phases[@]}]" } get_phase_file_path() { local phase=$1 - phase_file_path="${DOKKU_ROOT}/${APP}/${FILE_PREFIX}${phase^^}" + local prefix="DOCKER_OPTIONS_" + phase_file_path="${DOKKU_ROOT}/${APP}/${prefix}${phase^^}" } create_phase_file_if_required() { @@ -53,7 +52,8 @@ display_phase_options() { } display_all_phases_options() { - for phase in "${PHASES[@]}"; do + local phases=(build deploy run) + for phase in "${phases[@]}"; do get_phase_file_path $phase if [[ -s $phase_file_path ]]; then display_phase_options $phase $phase_file_path @@ -139,7 +139,7 @@ case "$1" in ;; # Display usage help - help) + help | docker-options:help) cat<, Display apps docker options for all phases docker-options , Display apps docker options for phase (comma separated phase list) diff --git a/plugins/domains/commands b/plugins/domains/commands index b7dea8836..0208368a9 100755 --- a/plugins/domains/commands +++ b/plugins/domains/commands @@ -1,28 +1,9 @@ #!/usr/bin/env bash +[[ " domains domains:setup domains:add domains:clear domains:remove help domains: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/config/functions" source "$PLUGIN_AVAILABLE_PATH/domains/functions" - -RE_IPV4="([0-9]{1,3}[\.]){3}[0-9]{1,3}" - -RE_IPV6="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" # TEST: 1:2:3:4:5:6:7:8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,7}:|" # TEST: 1:: 1:2:3:4:5:6:7:: -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" # TEST: 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" # TEST: 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" # TEST: 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" # TEST: 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" # TEST: 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8 -RE_IPV6="${RE_IPV6}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" # TEST: 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8 -RE_IPV6="${RE_IPV6}:((:[0-9a-fA-F]{1,4}){1,7}|:)|" # TEST: ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 :: -RE_IPV6="${RE_IPV6}fe08:(:[0-9a-fA-F]{1,4}){2,2}%[0-9a-zA-Z]{1,}|" # TEST: fe08::7:8%eth0 fe08::7:8%1 (link-local IPv6 addresses with zone index) -RE_IPV6="${RE_IPV6}::(ffff(:0{1,4}){0,1}:){0,1}${RE_IPV4}|" # TEST: ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses) -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}:${RE_IPV4}" # TEST: 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 - -# Ensure the ip address continues to the end of the line -# Fixes using a wildcard dns service such as xip.io which allows for *..xip.io -RE_IPV4="${RE_IPV4}\$" -RE_IPV6="${RE_IPV6}\$" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" case "$1" in domains) @@ -43,6 +24,7 @@ case "$1" in [[ -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" + RE_IPV4="$(get_ipv4_regex)"; RE_IPV6="$(get_ipv6_regex)" if [[ ! -f $VHOST_PATH ]]; then if [[ -f "$DOKKU_ROOT/VHOST" ]]; then @@ -52,7 +34,7 @@ case "$1" in fi if [[ "$VHOST" =~ $RE_IPV4 ]] || [[ "$VHOST" =~ $RE_IPV6 ]]; then dokku_log_info2 "unsupported vhost config found. disabling vhost support" - config_set --no-restart $APP NO_VHOST=1 + disable_app_vhost $APP --no-restart $APP else if [[ -f "$DOKKU_ROOT/VHOST" ]]; then dokku_log_info1 "Creating new $VHOST_PATH..." @@ -94,7 +76,7 @@ case "$1" in echo "$DOMAIN" >> "$DOKKU_ROOT/$APP/VHOST" done # we need to restart the app to make sure we're binding to the appropriate network interface - dokku nginx:build-config $APP + nginx_build_config $APP plugn trigger post-domains-update $APP for DOMAIN in "$@"; do dokku_log_info1 "Added $DOMAIN to $APP" diff --git a/plugins/domains/functions b/plugins/domains/functions index 2a69c42a0..4b5da1790 100755 --- a/plugins/domains/functions +++ b/plugins/domains/functions @@ -3,6 +3,9 @@ set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" get_app_domains() { - local APP=$1; verify_app_name $APP - cat "$DOKKU_ROOT/$APP/VHOST" + local APP=$1; local APP_VHOST_FILE="$DOKKU_ROOT/$APP/VHOST" + verify_app_name $APP + if [[ -f "$APP_VHOST_FILE" ]];then + cat "$APP_VHOST_FILE" + fi } diff --git a/plugins/enter/commands b/plugins/enter/commands index b8e0bdbdc..b69217e98 100755 --- a/plugins/enter/commands +++ b/plugins/enter/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " enter help enter:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" diff --git a/plugins/nginx-vhosts/bind-external-ip b/plugins/nginx-vhosts/bind-external-ip index b5f5c01d5..324d82852 100755 --- a/plugins/nginx-vhosts/bind-external-ip +++ b/plugins/nginx-vhosts/bind-external-ip @@ -1,41 +1,13 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" APP="$1" -NO_VHOST=$(config_get $APP NO_VHOST || true) -RE_IPV4="([0-9]{1,3}[\.]){3}[0-9]{1,3}" - -RE_IPV6="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" # TEST: 1:2:3:4:5:6:7:8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,7}:|" # TEST: 1:: 1:2:3:4:5:6:7:: -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" # TEST: 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" # TEST: 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" # TEST: 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" # TEST: 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8 -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" # TEST: 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8 -RE_IPV6="${RE_IPV6}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" # TEST: 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8 -RE_IPV6="${RE_IPV6}:((:[0-9a-fA-F]{1,4}){1,7}|:)|" # TEST: ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 :: -RE_IPV6="${RE_IPV6}fe08:(:[0-9a-fA-F]{1,4}){2,2}%[0-9a-zA-Z]{1,}|" # TEST: fe08::7:8%eth0 fe08::7:8%1 (link-local IPv6 addresses with zone index) -RE_IPV6="${RE_IPV6}::(ffff(:0{1,4}){0,1}:){0,1}${RE_IPV4}|" # TEST: ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses) -RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}:${RE_IPV4}" # TEST: 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 - - -# Ensure the ip address continues to the end of the line -# Fixes using a wildcard dns service such as xip.io which allows for *..xip.io -RE_IPV4="${RE_IPV4}\$" -RE_IPV6="${RE_IPV6}\$" - -[[ -f "$DOKKU_ROOT/VHOST" ]] && GLOBAL_VHOST=$(< "$DOKKU_ROOT/VHOST") - -if [[ -n "$NO_VHOST" ]]; then +if [[ "$(is_app_vhost_enabled $APP)" == "false" ]]; then echo true # bind to external ip. VHOST is disabled -elif [[ "$GLOBAL_VHOST" =~ $RE_IPV4 ]] || [[ "$GLOBAL_VHOST" =~ $RE_IPV6 ]]; then - echo true # bind to external ip. GLOBAL_VHOST is somehow an IPv4 or IPv6 address -elif [[ -z "$GLOBAL_VHOST" ]] && [[ ! -f "$DOKKU_ROOT/$APP/VHOST" ]]; then - echo true # bind to external ip. no GLOBAL_VHOST and no app vhost -elif [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then - echo false # bind to docker ip. this app has a vhost defined +elif [[ "$(is_global_vhost_enabled $APP)" == "false" ]] && [[ ! -f "$DOKKU_ROOT/$APP/VHOST" ]]; then + echo true # bind to external ip. no global vhost or global vhost is an ip else echo false fi diff --git a/plugins/nginx-vhosts/commands b/plugins/nginx-vhosts/commands index 3851cdceb..f48f29d6e 100755 --- a/plugins/nginx-vhosts/commands +++ b/plugins/nginx-vhosts/commands @@ -1,168 +1,17 @@ #!/usr/bin/env bash +[[ " nginx:build-config nginx:access-logs nginx:error-logs nginx:disable nginx:enable help nginx: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/config/functions" -source "$PLUGIN_AVAILABLE_PATH/domains/functions" - -validate_nginx () { - set +e - sudo /usr/sbin/nginx -t > /dev/null 2>&1 - exit_code=$? - set -e - if [[ "$exit_code" -ne "0" ]]; then - sudo /usr/sbin/nginx -t - exit "$exit_code" - fi -} - -restart_nginx () { - case "$DOKKU_DISTRO" in - ubuntu) - sudo /etc/init.d/nginx reload > /dev/null - ;; - - opensuse) - sudo /sbin/service nginx reload > /dev/null - ;; - esac -} +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" case "$1" in nginx:build-config) - APP="$2"; DOKKU_APP_LISTEN_PORT="$3"; DOKKU_APP_LISTEN_IP="${4}" - verify_app_name "$APP" - VHOST_PATH="$DOKKU_ROOT/$APP/VHOST" - URLS_PATH="$DOKKU_ROOT/$APP/URLS" - WILDCARD_SSL_PATH="$DOKKU_ROOT/tls" - APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" - APP_NGINX_TEMPLATE="$DOKKU_ROOT/$APP/nginx.conf.template" + APP="$2"; DOKKU_APP_LISTEN_PORT="$3"; DOKKU_APP_LISTEN_IP="$4" - if [[ -z "$DOKKU_APP_LISTEN_PORT" ]] && [[ -z "$DOKKU_APP_LISTEN_IP" ]]; then - shopt -s nullglob - for DOKKU_APP_IP_FILE in $DOKKU_ROOT/$APP/IP.web.*; do - DOKKU_APP_PORT_FILE=$(echo $DOKKU_APP_IP_FILE | sed -e "s:IP:PORT:g") - DOKKU_APP_LISTENER_IP=$(< $DOKKU_APP_IP_FILE) - DOKKU_APP_LISTENER_PORT=$(< $DOKKU_APP_PORT_FILE) + nginx_build_config $APP $DOKKU_APP_LISTEN_PORT $DOKKU_APP_LISTEN_IP - DOKKU_APP_LISTENERS+=" " - DOKKU_APP_LISTENERS+="$DOKKU_APP_LISTENER_IP:$DOKKU_APP_LISTENER_PORT" - DOKKU_APP_LISTENERS+=" " - done - shopt -u nullglob - fi - - DOKKU_APP_CIDS=($(get_app_container_ids $APP)) - docker cp "${DOKKU_APP_CIDS[0]}:/app/nginx.conf.template" "$DOKKU_ROOT/$APP/" 2> /dev/null || true - - eval "$(config_export global)" - eval "$(config_export app $APP)" - [[ -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=$(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 <> $NGINX_CONF" - done <<< "$SSL_VHOSTS" - fi - - if [[ -n "$NONSSL_VHOSTS" ]]; then - NOSSL_SERVER_NAME=$(echo $NONSSL_VHOSTS | tr '\n' ' ') - xargs -i echo "-----> Configuring {}..." <<< "$NONSSL_VHOSTS" - if [[ -n "$DOKKU_SSL_TERMINATED" ]] && [[ -z "$NGINX_CUSTOM_TEMPLATE" ]]; then - NGINX_TEMPLATE="$(dirname $0)/templates/nginx.conf.ssl_terminated.template" - elif [[ -z "$NGINX_CUSTOM_TEMPLATE" ]]; then - NGINX_TEMPLATE="$(dirname $0)/templates/nginx.conf.template" - fi - eval "cat <<< \"$(< $NGINX_TEMPLATE)\" >> $NGINX_CONF" - fi - - if [[ -n "$DOKKU_APP_LISTEN_PORT" ]] && [[ -n "$DOKKU_APP_LISTEN_IP" ]]; then - echo "upstream $APP { server $DOKKU_APP_LISTEN_IP:$DOKKU_APP_LISTEN_PORT; }" >> $NGINX_CONF - elif [[ -n "$DOKKU_APP_LISTENERS" ]]; then - echo "upstream $APP { " >> $NGINX_CONF - for listener in $DOKKU_APP_LISTENERS; do - echo " server $listener;" >> $NGINX_CONF - done - echo "}" >> $NGINX_CONF - fi - - dokku_log_info1 "Creating $SCHEME nginx.conf" - mv $NGINX_CONF "$DOKKU_ROOT/$APP/nginx.conf" - - if is_deployed "$APP"; then - dokku_log_info1 "Running nginx-pre-reload" - plugn trigger nginx-pre-reload $APP $DOKKU_APP_LISTEN_PORT $DOKKU_APP_LISTEN_IP - - dokku_log_verbose "Reloading nginx" - validate_nginx && restart_nginx - fi - - echo "# THIS FILE IS GENERATED BY DOKKU - DO NOT EDIT, YOUR CHANGES WILL BE OVERWRITTEN" > $URLS_PATH - xargs -i echo "https://{}" <<< "${SSL_VHOSTS}" >> $URLS_PATH - xargs -i echo "http://{}" <<< "${NONSSL_VHOSTS}" >> $URLS_PATH - else - if [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then - dokku_log_info1 "VHOST support disabled, deleting $APP/VHOST" - rm "$DOKKU_ROOT/$APP/VHOST" - fi - if [[ -f "$DOKKU_ROOT/$APP/URLS" ]]; then - dokku_log_info1 "VHOST support disabled, deleting $APP/URLS" - rm "$DOKKU_ROOT/$APP/URLS" - fi - if [[ -f "$DOKKU_ROOT/$APP/nginx.conf" ]]; then - dokku_log_info1 "VHOST support disabled, deleting nginx.conf" - rm "$DOKKU_ROOT/$APP/nginx.conf" - - if is_deployed "$APP"; then - dokku_log_info1 "VHOST support disabled, reloading nginx after nginx.conf deletion" - validate_nginx && restart_nginx - fi - fi - fi - ;; + ;; nginx:access-logs|nginx:error-logs) [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 @@ -180,18 +29,50 @@ EOF fi tail "$NGINX_LOGS_ARGS" "$NGINX_LOGS_PATH" - ;; + ;; + + nginx:disable) + [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + verify_app_name "$2" + APP="$2" + + if [[ "$(is_app_vhost_enabled $APP)" == "true" ]]; then + config_set --no-restart $APP DOKKU_NO_NGINX=1 + disable_app_vhost $APP + elif [[ "$(is_app_nginx_enabled $APP)" == "true" ]]; then + config_set --no-restart $APP DOKKU_NO_NGINX=1 + nginx_build_config $APP + else + dokku_log_info1 "nginx is already disable for app ($APP)" + fi + ;; + + nginx:enable) + [[ -z $2 ]] && echo "Please specify an app to run the command on" && exit 1 + verify_app_name "$2" + APP="$2" + + if [[ "$(is_app_nginx_enabled $APP)" == "true" ]]; then + config_unset --no-restart $APP DOKKU_NO_NGINX + unset DOKKU_NO_NGINX + nginx_build_config $APP + else + dokku_log_info1 "nginx is already enabled for app ($APP)" + fi + ;; help | nginx:help) cat<, (Re)builds nginx config for given app nginx:access-logs [-t], Show the nginx access logs for an application (-t follows) nginx:error-logs [-t], Show the nginx error logs for an application (-t follows) + nginx:disable , disable nginx for an application (forces container binding to external interface) + nginx:enable , enable nginx for an application EOF - ;; + ;; *) exit $DOKKU_NOT_IMPLEMENTED_EXIT - ;; + ;; esac diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions new file mode 100755 index 000000000..dd441d75d --- /dev/null +++ b/plugins/nginx-vhosts/functions @@ -0,0 +1,293 @@ +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/certs/functions" +source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/domains/functions" + +_ipv4_regex() { + echo "([0-9]{1,3}[\.]){3}[0-9]{1,3}" +} + +_ipv6_regex() { + local RE_IPV4="$(_ipv4_regex)" + local RE_IPV6="([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" # TEST: 1:2:3:4:5:6:7:8 + RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,7}:|" # TEST: 1:: 1:2:3:4:5:6:7:: + RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" # TEST: 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8 + RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" # TEST: 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8 + RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" # TEST: 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8 + RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" # TEST: 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8 + RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" # TEST: 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8 + RE_IPV6="${RE_IPV6}[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|" # TEST: 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8 + RE_IPV6="${RE_IPV6}:((:[0-9a-fA-F]{1,4}){1,7}|:)|" # TEST: ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 :: + RE_IPV6="${RE_IPV6}fe08:(:[0-9a-fA-F]{1,4}){2,2}%[0-9a-zA-Z]{1,}|" # TEST: fe08::7:8%eth0 fe08::7:8%1 (link-local IPv6 addresses with zone index) + RE_IPV6="${RE_IPV6}::(ffff(:0{1,4}){0,1}:){0,1}${RE_IPV4}|" # TEST: ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses) + RE_IPV6="${RE_IPV6}([0-9a-fA-F]{1,4}:){1,4}:${RE_IPV4}" # TEST: 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 + echo "$RE_IPV6" +} + +get_ipv4_regex() { + local RE_IPV4="$(_ipv4_regex)" + # Ensure the ip address continues to the end of the line + # Fixes using a wildcard dns service such as xip.io which allows for *..xip.io + echo "${RE_IPV4}\$" +} + +get_ipv6_regex() { + local RE_IPV6="$(_ipv4_regex)" + # Ensure the ip address continues to the end of the line + # Fixes using a wildcard dns service such as xip.io which allows for *..xip.io + echo "${RE_IPV6}\$" +} + +is_app_nginx_enabled() { + local APP="$1" + verify_app_name "$APP" + + local DOKKU_NO_NGINX=$(config_get $APP DOKKU_NO_NGINX) + if [[ -z "$DOKKU_NO_NGINX" ]]; then + echo true + else + echo false + fi +} + +is_global_vhost_enabled() { + local GLOBAL_VHOST_FILE="$DOKKU_ROOT/VHOST" + local GLOBAL_VHOST_ENABLED=true + [[ -f "$GLOBAL_VHOST_FILE" ]] && local GLOBAL_VHOST=$(< "$GLOBAL_VHOST_FILE") + + # Ensure the ip address continues to the end of the line + # Fixes using a wildcard dns service such as xip.io which allows for *..xip.io + local RE_IPV4="$(get_ipv4_regex)" + local RE_IPV6="$(get_ipv6_regex)" + + if [[ -z "$GLOBAL_VHOST" ]] || [[ "$GLOBAL_VHOST" =~ $RE_IPV4 ]] || [[ "$GLOBAL_VHOST" =~ $RE_IPV6 ]]; then + local GLOBAL_VHOST_ENABLED=false + fi + echo $GLOBAL_VHOST_ENABLED +} + +is_app_vhost_enabled() { + local APP=$1; local APP_VHOST_FILE="$DOKKU_ROOT/$APP/VHOST" + verify_app_name $APP + + local NO_VHOST=$(config_get $APP NO_VHOST) + local APP_VHOST_ENABLED=true + + if [[ "$NO_VHOST" == "1" ]]; then + local APP_VHOST_ENABLED=false + elif [[ -f "$DOKKU_ROOT/$APP/nginx.conf" ]] && [[ ! -f "$APP_VHOST_FILE" ]] && [[ "$NO_VHOST" != "0" ]]; then + local APP_VHOST_ENABLED=false + fi + + echo $APP_VHOST_ENABLED +} + +disable_app_vhost() { + local APP=$1; local APP_VHOST_FILE="$DOKKU_ROOT/$APP/VHOST" + verify_app_name $APP + + if [[ -f "$DOKKU_ROOT/$APP/VHOST" ]]; then + dokku_log_info1 "VHOST support disabled, deleting $APP/VHOST" + rm "$DOKKU_ROOT/$APP/VHOST" + fi + if [[ -f "$DOKKU_ROOT/$APP/URLS" ]]; then + dokku_log_info1 "VHOST support disabled, deleting $APP/URLS" + rm "$DOKKU_ROOT/$APP/URLS" + fi + + [[ "$2" == "--no-restart" ]] && local CONFIG_SET_ARGS=$2 + config_set $CONFIG_SET_ARGS $APP NO_VHOST=1 +} + +validate_nginx() { + set +e + sudo /usr/sbin/nginx -t > /dev/null 2>&1 + exit_code=$? + set -e + if [[ "$exit_code" -ne "0" ]]; then + sudo /usr/sbin/nginx -t + exit "$exit_code" + fi +} + +restart_nginx() { + case "$DOKKU_DISTRO" in + ubuntu) + sudo /etc/init.d/nginx reload > /dev/null + ;; + + opensuse) + sudo /sbin/service nginx reload > /dev/null + ;; + esac +} + +nginx_build_config() { + local APP="$1"; local DOKKU_APP_LISTEN_PORT="$2"; local DOKKU_APP_LISTEN_IP="$3" + verify_app_name "$APP" + VHOST_PATH="$DOKKU_ROOT/$APP/VHOST" + URLS_PATH="$DOKKU_ROOT/$APP/URLS" + WILDCARD_SSL_PATH="$DOKKU_ROOT/tls" + APP_SSL_PATH="$DOKKU_ROOT/$APP/tls" + APP_NGINX_TEMPLATE="$DOKKU_ROOT/$APP/nginx.conf.template" + + eval "$(config_export global)" + eval "$(config_export app $APP)" + + if [[ ! -n "$DOKKU_NO_NGINX" ]]; then + if [[ -z "$DOKKU_APP_LISTEN_PORT" ]] && [[ -z "$DOKKU_APP_LISTEN_IP" ]]; then + shopt -s nullglob + for DOKKU_APP_IP_FILE in $DOKKU_ROOT/$APP/IP.web.*; do + DOKKU_APP_PORT_FILE=$(echo $DOKKU_APP_IP_FILE | sed -e "s:IP:PORT:g") + DOKKU_APP_LISTENER_IP=$(< $DOKKU_APP_IP_FILE) + DOKKU_APP_LISTENER_PORT=$(< $DOKKU_APP_PORT_FILE) + + DOKKU_APP_LISTENERS+=" " + DOKKU_APP_LISTENERS+="$DOKKU_APP_LISTENER_IP:$DOKKU_APP_LISTENER_PORT" + DOKKU_APP_LISTENERS+=" " + done + shopt -u nullglob + fi + + DOKKU_APP_CIDS=($(get_app_container_ids $APP)) + docker cp "${DOKKU_APP_CIDS[0]}:/app/nginx.conf.template" "$DOKKU_ROOT/$APP/" 2> /dev/null || true + + [[ -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' + + NGINX_CONF=$(mktemp -t "nginx.conf.XXXXXX") + SCHEME="http" + + # if the app has is not vhost enabled then, let's make sure we're cleaned up + [[ "$(is_app_vhost_enabled $APP)" == "false" ]] && disable_app_vhost $APP --no-restart + + if [[ -z "$DOKKU_NGINX_PORT" ]]; then + if [[ "$(is_app_vhost_enabled $APP)" == "false" ]]; then + dokku_log_info1 "no nginx port set. setting to random open high port" + local NGINX_PORT=$(get_available_port) + else + local NGINX_PORT=80 + fi + config_set --no-restart $APP DOKKU_NGINX_PORT=${NGINX_PORT} + else + NGINX_PORT=$DOKKU_NGINX_PORT + fi + + NONSSL_VHOSTS=$(get_app_domains $APP) + if [[ -n "$(is_ssl_enabled $APP)" ]]; then + SSL_HOSTNAME=$(get_ssl_hostnames $APP) + SSL_INUSE=true + + [[ -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 &> /dev/null); then + dokku_log_info1 "No matching configured domains for $APP found in SSL certificate. Your app will show as insecure in a browser if accessed via SSL" + dokku_log_info1 "Please add appropriate domains via the dokku domains command" + [[ -n "$NONSSL_VHOSTS" ]] && dokku_log_info1 "Configured domains for app:" + for domain in $(echo $NONSSL_VHOSTS| xargs); do + dokku_log_info2 "$domain" + done + [[ -n "$SSL_HOSTNAME" ]] && 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 < /dev/null || true) + + if [[ -z "$DOKKU_NGINX_SSL_PORT" ]]; then + if [[ "$(is_app_vhost_enabled $APP)" == "false" ]]; then + dokku_log_info1 "no nginx ssl port set. setting to random open high port" + local NGINX_SSL_PORT=$(get_available_port) + else + NGINX_SSL_PORT=443 + fi + config_set --no-restart $APP DOKKU_NGINX_SSL_PORT=${NGINX_SSL_PORT} + else + local NGINX_SSL_PORT=$DOKKU_NGINX_SSL_PORT + fi + + while read line; do + [[ -z "$line" ]] && continue + dokku_log_info1 "Configuring SSL for $line..." + SSL_SERVER_NAME=$line + NOSSL_SERVER_NAME=$line + eval "cat <<< \"$(< $NGINX_TEMPLATE)\" >> $NGINX_CONF" + done <<< "$SSL_VHOSTS" + fi + + if [[ -n "$DOKKU_SSL_TERMINATED" ]] && [[ -z "$NGINX_CUSTOM_TEMPLATE" ]]; then + NGINX_TEMPLATE="$PLUGIN_AVAILABLE_PATH/nginx-vhosts/templates/nginx.conf.ssl_terminated.template" + elif [[ -z "$NGINX_CUSTOM_TEMPLATE" ]]; then + NGINX_TEMPLATE="$PLUGIN_AVAILABLE_PATH/nginx-vhosts/templates/nginx.conf.template" + fi + + if [[ -n "$NONSSL_VHOSTS" ]]; then + NOSSL_SERVER_NAME=$(echo $NONSSL_VHOSTS | tr '\n' ' ') + xargs -i echo "-----> Configuring {}..." <<< "$NONSSL_VHOSTS" + fi + eval "cat <<< \"$(< $NGINX_TEMPLATE)\" >> $NGINX_CONF" + if [[ "$(is_app_vhost_enabled $APP)" == "false" ]] || ([[ -z "$NONSSL_VHOSTS" ]] && [[ -z "$SSL_VHOSTS" ]]); then + sed --in-place -n -e '/^.*server_name.*$/!p' $NGINX_CONF + fi + + if [[ -n "$DOKKU_APP_LISTEN_PORT" ]] && [[ -n "$DOKKU_APP_LISTEN_IP" ]]; then + echo "upstream $APP { server $DOKKU_APP_LISTEN_IP:$DOKKU_APP_LISTEN_PORT; }" >> $NGINX_CONF + elif [[ -n "$DOKKU_APP_LISTENERS" ]]; then + echo "upstream $APP { " >> $NGINX_CONF + for listener in $DOKKU_APP_LISTENERS; do + echo " server $listener;" >> $NGINX_CONF + done + echo "}" >> $NGINX_CONF + fi + + dokku_log_info1 "Creating $SCHEME nginx.conf" + mv $NGINX_CONF "$DOKKU_ROOT/$APP/nginx.conf" + + if is_deployed "$APP"; then + dokku_log_info1 "Running nginx-pre-reload" + plugn trigger nginx-pre-reload $APP $DOKKU_APP_LISTEN_PORT $DOKKU_APP_LISTEN_IP + + dokku_log_verbose "Reloading nginx" + validate_nginx && restart_nginx + fi + + if [[ "$(is_app_vhost_enabled $APP)" == "true" ]]; then + echo "# THIS FILE IS GENERATED BY DOKKU - DO NOT EDIT, YOUR CHANGES WILL BE OVERWRITTEN" > $URLS_PATH + xargs -i echo "https://{}" <<< "${SSL_VHOSTS}" >> $URLS_PATH + xargs -i echo "http://{}" <<< "${NONSSL_VHOSTS}" >> $URLS_PATH + fi + else + # note because this clause is long. if $DOKKU_NO_NGINX is set: + dokku_log_info1 "nginx support is disabled for app ($APP)." + if [[ -f "$DOKKU_ROOT/$APP/nginx.conf" ]]; then + dokku_log_info1 "deleting nginx.conf" + rm "$DOKKU_ROOT/$APP/nginx.conf" + + if is_deployed "$APP"; then + dokku_log_info1 "reloading nginx after nginx.conf deletion" + validate_nginx && restart_nginx + fi + fi + fi +} diff --git a/plugins/nginx-vhosts/post-deploy b/plugins/nginx-vhosts/post-deploy index e3fd84430..7bdf10105 100755 --- a/plugins/nginx-vhosts/post-deploy +++ b/plugins/nginx-vhosts/post-deploy @@ -1,17 +1,15 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_AVAILABLE_PATH/config/functions" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" APP="$1" if [[ -f "$DOKKU_ROOT/$APP/IP.web.1" ]] && [[ -f "$DOKKU_ROOT/$APP/PORT.web.1" ]]; then - NO_VHOST=$(config_get $APP NO_VHOST || true) - - if [[ -n "$NO_VHOST" ]]; then - dokku_log_info1 "NO_VHOST config detected" + if [[ "$(is_app_vhost_enabled $APP)" == "false" ]]; then + dokku_log_info1 "VHOST support disabled. Skipping domains setup" elif [[ ! -f "$DOKKU_ROOT/$APP/VHOST" ]]; then dokku domains:setup $APP fi - dokku nginx:build-config $APP + nginx_build_config $APP fi diff --git a/plugins/nginx-vhosts/post-domains-update b/plugins/nginx-vhosts/post-domains-update index 20abd9d5e..b62f24ac5 100755 --- a/plugins/nginx-vhosts/post-domains-update +++ b/plugins/nginx-vhosts/post-domains-update @@ -1,7 +1,8 @@ #!/usr/bin/env bash set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions" APP="$1" -dokku nginx:build-config $APP +nginx_build_config $APP diff --git a/plugins/nginx-vhosts/templates/nginx.conf.ssl_terminated.template b/plugins/nginx-vhosts/templates/nginx.conf.ssl_terminated.template index bc7bc3a1d..642b0d653 100644 --- a/plugins/nginx-vhosts/templates/nginx.conf.ssl_terminated.template +++ b/plugins/nginx-vhosts/templates/nginx.conf.ssl_terminated.template @@ -1,8 +1,8 @@ # Nginx configuration when running behind a load balancer that terminates SSL # connections (e.g. AWS ELB) server { - listen [::]:80; - listen 80; + listen [::]:$NGINX_PORT; + listen $NGINX_PORT; server_name $NOSSL_SERVER_NAME; location / { proxy_pass http://$APP; diff --git a/plugins/nginx-vhosts/templates/nginx.conf.template b/plugins/nginx-vhosts/templates/nginx.conf.template index ad29248c2..23a88e1da 100644 --- a/plugins/nginx-vhosts/templates/nginx.conf.template +++ b/plugins/nginx-vhosts/templates/nginx.conf.template @@ -1,6 +1,6 @@ server { - listen [::]:80; - listen 80; + listen [::]:$NGINX_PORT; + listen $NGINX_PORT; server_name $NOSSL_SERVER_NAME; access_log /var/log/nginx/${APP}-access.log; error_log /var/log/nginx/${APP}-error.log; diff --git a/plugins/nginx-vhosts/templates/nginx.ssl.conf.template b/plugins/nginx-vhosts/templates/nginx.ssl.conf.template index 312cd83c3..ac49e6e9b 100644 --- a/plugins/nginx-vhosts/templates/nginx.ssl.conf.template +++ b/plugins/nginx-vhosts/templates/nginx.ssl.conf.template @@ -1,22 +1,22 @@ server { - listen [::]:80; - listen 80; + listen [::]:$NGINX_PORT; + listen $NGINX_PORT; server_name $NOSSL_SERVER_NAME; access_log /var/log/nginx/${APP}-access.log; error_log /var/log/nginx/${APP}-error.log; - return 301 https://\$host\$request_uri; + return 301 https://\$host:$NGINX_SSL_PORT\$request_uri; } server { - listen [::]:443 ssl spdy; - listen 443 ssl spdy; + listen [::]:$NGINX_SSL_PORT ssl spdy; + listen $NGINX_SSL_PORT ssl spdy; server_name $SSL_SERVER_NAME; access_log /var/log/nginx/${APP}-access.log; error_log /var/log/nginx/${APP}-error.log; $SSL_DIRECTIVES keepalive_timeout 70; - add_header Alternate-Protocol 443:npn-spdy/2; + add_header Alternate-Protocol $NGINX_SSL_PORT:npn-spdy/2; location / { gzip on; diff --git a/plugins/plugin/commands b/plugins/plugin/commands index 8df025c37..d4eb65327 100755 --- a/plugins/plugin/commands +++ b/plugins/plugin/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " plugin plugin:install plugin:install-dependencies plugin:update plugin:disable plugin:enable plugin:uninstall help plugin: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/plugin/functions" diff --git a/plugins/ps/commands b/plugins/ps/commands index d9614c623..3e0a7357a 100755 --- a/plugins/ps/commands +++ b/plugins/ps/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " ps ps:start ps:stop ps:rebuild ps:rebuildall ps:restart ps:restartall ps:scale help ps: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/ps/functions" diff --git a/plugins/shell/commands b/plugins/shell/commands index f64e231aa..16fec26f4 100755 --- a/plugins/shell/commands +++ b/plugins/shell/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " shell help shell:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x case "$1" in diff --git a/plugins/tags/commands b/plugins/tags/commands index 58061e835..9990485a9 100755 --- a/plugins/tags/commands +++ b/plugins/tags/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " tags tags:create tags:deploy tags:destroy help tags: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/tags/functions" diff --git a/plugins/tar/commands b/plugins/tar/commands index 76da3766f..519ae4e15 100755 --- a/plugins/tar/commands +++ b/plugins/tar/commands @@ -1,4 +1,5 @@ #!/usr/bin/env bash +[[ " tar-from tar:from tar-in tar:in tar-build tar-build-locked help tar:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" @@ -57,7 +58,7 @@ case "$1" in fi ;; - help) + help | tar:help) cat && cat<, Reads an tarball containing the app from stdin tar:from , Loads an app tarball from url. diff --git a/tests/unit/core_ports.bats b/tests/unit/core_ports.bats index 79f6c1b95..426de3ea8 100644 --- a/tests/unit/core_ports.bats +++ b/tests/unit/core_ports.bats @@ -142,16 +142,6 @@ check_urls() { check_urls http://www.test.app.dokku.me } -@test "(core) dockerfile port exposure" { - deploy_app dockerfile - run bash -c "grep -A1 upstream $DOKKU_ROOT/$TEST_APP/nginx.conf | grep -q 3000" - echo "output: "$output - echo "status: "$status - assert_success - - check_urls http://${TEST_APP}.dokku.me -} - @test "(core) port exposure (xip.io style hostnames)" { echo "127.0.0.1.xip.io" > "$DOKKU_ROOT/VHOST" deploy_app @@ -164,6 +154,16 @@ check_urls() { check_urls http://my-cool-guy-test-app.127.0.0.1.xip.io } +@test "(core) dockerfile port exposure" { + deploy_app dockerfile + run bash -c "grep -A1 upstream $DOKKU_ROOT/$TEST_APP/nginx.conf | grep -q 3000" + echo "output: "$output + echo "status: "$status + assert_success + + check_urls http://${TEST_APP}.dokku.me +} + @test "(core) port exposure (dockerfile raw port)" { source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" cat< $DOCKERFILE diff --git a/tests/unit/nginx-vhosts.bats b/tests/unit/nginx-vhosts.bats index fe2cf114a..1436c6a68 100644 --- a/tests/unit/nginx-vhosts.bats +++ b/tests/unit/nginx-vhosts.bats @@ -18,7 +18,7 @@ teardown() { assert_ssl_domain() { local domain=$1 assert_app_domain "${domain}" - assert_http_redirect "http://${domain}" "https://${domain}/" + assert_http_redirect "http://${domain}" "https://${domain}:443/" assert_http_success "https://${domain}" } diff --git a/tests/unit/test_helper.bash b/tests/unit/test_helper.bash index 5e8d3353c..32cba7a27 100644 --- a/tests/unit/test_helper.bash +++ b/tests/unit/test_helper.bash @@ -175,20 +175,20 @@ custom_ssl_nginx_template() { [[ -z "$APP" ]] && APP="$TEST_APP" cat< $DOKKU_ROOT/$APP/nginx.conf.template server { - listen [::]:80; - listen 80; + listen [::]:\$NGINX_PORT; + listen \$NGINX_PORT; server_name \$NOSSL_SERVER_NAME; - return 301 https://\$SSL_SERVER_NAME\\\$request_uri; + return 301 https://\\\$host:\$NGINX_SSL_PORT\\\$request_uri; } server { - listen [::]:443 ssl spdy; - listen 443 ssl spdy; + listen [::]:\$NGINX_SSL_PORT ssl spdy; + listen \$NGINX_SSL_PORT ssl spdy; server_name \$SSL_SERVER_NAME; \$SSL_DIRECTIVES keepalive_timeout 70; - add_header Alternate-Protocol 443:npn-spdy/2; + add_header Alternate-Protocol \$NGINX_SSL_PORT:npn-spdy/2; location / { proxy_pass http://\$APP; proxy_http_version 1.1; @@ -210,8 +210,8 @@ custom_nginx_template() { [[ -z "$APP" ]] && APP="$TEST_APP" cat< $DOKKU_ROOT/$APP/nginx.conf.template server { - listen [::]:80; - listen 80; + listen [::]:\$NGINX_PORT; + listen \$NGINX_PORT; server_name \$NOSSL_SERVER_NAME; location / {