diff --git a/docs/nginx.md b/docs/nginx.md index 1490a7c3bcd..a27347ab1cd 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 c3dd3633783..b15993024d2 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 af5e4377015..a3dc43779e8 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 11f3a2b1569..582b5c38f59 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 93052e0d0e9..a73e97975ec 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 2fa16097951..90f28a4adad 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 ba6e62bc013..870cd48cfd9 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 fecfce1ef3a..69e4c6315da 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 191af9f5249..f5b929449d3 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 eeb0db2f94c..766de510359 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 b7f63c55dbb..2a9589978ac 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 b7dea883631..0208368a90c 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 2a69c42a07d..4b5da179013 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 b8e0bdbdc82..b69217e980f 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 b5f5c01d59d..324d82852c7 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 3851cdcebd7..f48f29d6e96 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" - - 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 - - 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) + APP="$2"; DOKKU_APP_LISTEN_PORT="$3"; DOKKU_APP_LISTEN_IP="$4" - [[ -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 + nginx_build_config $APP $DOKKU_APP_LISTEN_PORT $DOKKU_APP_LISTEN_IP - 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 00000000000..dd441d75d66 --- /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 e3fd844309c..7bdf101053a 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 20abd9d5eb4..b62f24ac588 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 bc7bc3a1dd5..642b0d65370 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 ad29248c2ca..23a88e1dab9 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 312cd83c36c..ac49e6e9bcb 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 8df025c375f..d4eb6532772 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 d9614c623ba..3e0a7357ab9 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 f64e231aa85..16fec26f4c2 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 58061e8353b..9990485a9b2 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 76da3766f2a..519ae4e15d5 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 79f6c1b95a5..426de3ea8bc 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 fe2cf114a73..1436c6a681f 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 5e8d3353ce5..32cba7a272f 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 / {