PromucFlow_constructor/app/client/start-https.sh
Shrikant Sharat Kandula b3dbd85dbe
fix: Fix container-internal communication when using IPv6 (#20981)
When services within the fat container try to communicate with each
other, like backend to RTS etc., if they use the loopback address of
IPv4, `127.0.0.1`, it works. But if they use the loopback address of
IPv6, `::1`, it fails because the NGINX inside the fat container isn't
set to bind to IPv6.

This PR fixes this.

In EE, we attempt to make connections to Keycloak without setting the
hostname on the `WebClient`. This picks up the hostname of `::1` on
systems with IPv6 as default, and so the communication between backend
and Keycloak fails.

This is affecting users on ECS Fargate, for example.
2023-02-27 19:51:54 +05:30

378 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
if [[ -n ${TRACE-} ]]; then
set -o xtrace
fi
cd "$(dirname "$0")"
if [[ ${1-} =~ ^-*h(elp)?$ ]]; then
echo 'Run '"$0"' [option...]
--with-docker: Start NGINX with Docker. Fail if Docker is not available.
--without-docker: Start NGINX directly. Fail if NGINX is not installed.
If neither of the above are set, we check if NGINX is installed, and use that if yes, or Docker otherwise.
--https: Require start with https.
--http: Require start with http.
If neither of the above ar set, then we check if mkcert is available, and use https if yes, or http otherwise.
--https-port: Port to use for https. Default: 443.
--http-port: Port to use for http. Default: 80.
If neither of the above are set, then we use 443 for https, and 80 for http.
--env-file: Specify an alternate env file. Defaults to '.env' at the root of the project.
A single positional argument can be given to set the backend server proxy address. Example:
'"$0"' https://localhost:8080
'"$0"' https://host.docker.internal:8080
'"$0"' https://release.app.appsmith.com
'"$0"' release # This is identical to the one above
' >&2
exit
fi
while [[ $# -gt 0 ]]; do
case $1 in
--with-docker)
run_as=docker
shift
;;
--without-docker)
run_as=nginx
shift
;;
--https)
use_https=1
shift
;;
--http)
use_https=0
shift
;;
--https-port)
https_listen_port=$2
shift 2
;;
--http-port)
http_listen_port=$2
shift 2
;;
--env-file)
env_file=$2
shift
shift
;;
-*|--*)
echo "Unknown option $1" >&2
exit 1
;;
*)
backend="$1"
shift
;;
esac
done
if [[ ${backend-} == release ]]; then
# Special shortcut for release environment.
backend=https://release.app.appsmith.com
fi
if [[ -z ${run_as-} ]]; then
if type nginx; then
run_as=nginx
elif type docker; then
run_as=docker
else
echo 'Could not find nginx or docker. Do a "brew install nginx" and try again.'
exit
fi
fi
if [[ $run_as == docker ]]; then
echo 'Running with Docker. You may "brew install nginx" to run this without Docker.'
fi
working_dir="$PWD/nginx"
mkdir -pv "$working_dir"
domain=dev.appsmith.com
key_file="$working_dir/$domain-key.pem"
cert_file="$working_dir/$domain.pem"
if [[ -z ${use_https-} ]]; then
if type mkcert; then
use_https=1
else
echo 'SSL cert isn'\''t there, and "mkcert" is not installed either. Starting with http.' >&2
echo 'Please "brew install mkcert" and re-run this script to start with https.' >&2
use_https=0
fi
fi
if [[ $use_https == 1 ]]; then
if ! [[ -f $key_file && -f $cert_file ]]; then
if type mkcert; then
pushd "$working_dir"
mkcert -install
mkcert "$domain"
popd
else
echo 'I got "--use-https", but "mkcert" is not available. Please "brew install mkcert" and try again.' >&2
exit 1
fi
fi
fi
upstream_host=localhost
if [[ $run_as == docker ]]; then
upstream_host=host.docker.internal
fi
frontend_host=${frontend_host-$upstream_host}
backend_host=${backend_host-$upstream_host}
rts_host=${rts_host-$upstream_host}
frontend_port=${frontend_port-3000}
backend_port=${backend_port-8080}
rts_port=${rts_port-8091}
backend="${backend-http://$backend_host:$backend_port}"
frontend="http://$frontend_host:$frontend_port"
rts="http://$rts_host:$rts_port"
http_listen_port="${http_listen_port-80}"
https_listen_port="${https_listen_port-443}"
if [[ -n ${env_file-} && ! -f $env_file ]]; then
echo "I got --env-file as '$env_file', but I cannot access it." >&2
exit 1
elif [[ -n ${env_file-} || -f ../../.env ]]; then
set -o allexport
# shellcheck disable=SC1090
source "${env_file-../../.env}"
set +o allexport
else
echo "
Please populate the .env at the root of the project and run again
Or add the environment variables defined in .env.example to the environment
-- to enable features
" >&2
fi
if [[ -f /etc/nginx/mime.types || $run_as == docker ]]; then
mime_types=/etc/nginx/mime.types
elif type nginx >/dev/null; then
mime_types="$(dirname "$(nginx -t 2>&1 | head -1 | cut -d' ' -f5)")/mime.types"
if [[ ! -f $mime_types ]]; then
mime_types=
fi
fi
if [[ -z ${mime_types-} ]]; then
echo "No mime.types file found. Can't start NGINX." >&2
exit 1
fi
nginx_pid="$working_dir/wildcard-nginx.pid"
nginx_access_log="$working_dir/access.log"
nginx_error_log="$working_dir/error.log"
rm -f "$nginx_access_log" "$nginx_error_log"
nginx_dev_conf="$working_dir/nginx.dev.conf"
# Rare case, if this file doesn't exist, and the `docker run` command
# (from further below) the script runs, then it'll auto-create a _directory_
# at this path, breaking this script after that.
rm -rf "$nginx_dev_conf"
worker_connections=1024
echo "
worker_processes 1;
error_log $nginx_error_log info;
$(if [[ $run_as == nginx ]]; then echo "pid $nginx_pid;"; fi)
events {
worker_connections $worker_connections;
}
http {
map \$http_x_forwarded_proto \$origin_scheme {
default \$http_x_forwarded_proto;
'' \$scheme;
}
include $mime_types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
access_log $nginx_access_log;
gzip on;
gzip_types *;
$(if [[ $use_https == 1 ]]; then echo "
server {
listen $http_listen_port default_server;
listen [::]:$http_listen_port default_server;
server_name $domain;
return 301 https://\$host$(if [[ $https_listen_port != 443 ]]; then echo ":$https_listen_port"; fi)\$request_uri;
}
"; fi)
server {
$(if [[ $use_https == 1 ]]; then echo "
listen $https_listen_port ssl http2 default_server;
listen [::]:$https_listen_port ssl http2 default_server;
server_name $domain;
ssl_certificate '$cert_file';
ssl_certificate_key '$key_file';
"; else echo "
listen $http_listen_port default_server;
listen [::]:$http_listen_port default_server;
server_name _;
"; fi)
client_max_body_size 150m;
gzip on;
proxy_ssl_server_name on;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header X-Forwarded-Proto \$origin_scheme;
proxy_set_header X-Forwarded-Host \$host;
proxy_set_header Accept-Encoding '';
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors
add_header Content-Security-Policy \"frame-ancestors ${APPSMITH_ALLOWED_FRAME_ANCESTORS-'self' *}\";
sub_filter_once off;
location / {
proxy_pass $frontend;
sub_filter __APPSMITH_SENTRY_DSN__ '${APPSMITH_SENTRY_DSN-}';
sub_filter __APPSMITH_SMART_LOOK_ID__ '${APPSMITH_SMART_LOOK_ID-}';
sub_filter __APPSMITH_OAUTH2_GOOGLE_CLIENT_ID__ '${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID-}';
sub_filter __APPSMITH_OAUTH2_GITHUB_CLIENT_ID__ '${APPSMITH_OAUTH2_GITHUB_CLIENT_ID-}';
sub_filter __APPSMITH_MARKETPLACE_ENABLED__ '${APPSMITH_MARKETPLACE_ENABLED-}';
sub_filter __APPSMITH_SEGMENT_KEY__ '${APPSMITH_SEGMENT_KEY-}';
sub_filter __APPSMITH_ALGOLIA_API_ID__ '${APPSMITH_ALGOLIA_API_ID-}';
sub_filter __APPSMITH_ALGOLIA_SEARCH_INDEX_NAME__ '${APPSMITH_ALGOLIA_SEARCH_INDEX_NAME-}';
sub_filter __APPSMITH_ALGOLIA_API_KEY__ '${APPSMITH_ALGOLIA_API_KEY-}';
sub_filter __APPSMITH_CLIENT_LOG_LEVEL__ '${APPSMITH_CLIENT_LOG_LEVEL-}';
sub_filter __APPSMITH_TNC_PP__ '${APPSMITH_TNC_PP-}';
sub_filter __APPSMITH_SENTRY_RELEASE__ '${APPSMITH_SENTRY_RELEASE-}';
sub_filter __APPSMITH_SENTRY_ENVIRONMENT__ '${APPSMITH_SENTRY_ENVIRONMENT-}';
sub_filter __APPSMITH_VERSION_ID__ '${APPSMITH_VERSION_ID-}';
sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '${APPSMITH_VERSION_RELEASE_DATE-}';
sub_filter __APPSMITH_INTERCOM_APP_ID__ '${APPSMITH_INTERCOM_APP_ID-}';
sub_filter __APPSMITH_MAIL_ENABLED__ '${APPSMITH_MAIL_ENABLED-}';
sub_filter __APPSMITH_CLOUD_SERVICES_BASE_URL__ '${APPSMITH_CLOUD_SERVICES_BASE_URL-}';
sub_filter __APPSMITH_RECAPTCHA_SITE_KEY__ '${APPSMITH_RECAPTCHA_SITE_KEY-}';
sub_filter __APPSMITH_DISABLE_INTERCOM__ '${APPSMITH_DISABLE_INTERCOM-}';
sub_filter __APPSMITH_FORM_LOGIN_DISABLED__ '${APPSMITH_FORM_LOGIN_DISABLED-}';
sub_filter __APPSMITH_SIGNUP_DISABLED__ '${APPSMITH_SIGNUP_DISABLED-}';
sub_filter __APPSMITH_ZIPY_SDK_KEY__ '${APPSMITH_ZIPY_SDK_KEY-}';
sub_filter __APPSMITH_HIDE_WATERMARK__ '${APPSMITH_HIDE_WATERMARK-}';
sub_filter __APPSMITH_DISABLE_IFRAME_WIDGET_SANDBOX__ '${APPSMITH_DISABLE_IFRAME_WIDGET_SANDBOX-}';
}
location /api {
proxy_pass $backend;
}
location /oauth2 {
proxy_pass $backend;
}
location /login {
proxy_pass $backend;
}
location /rts {
proxy_pass $rts;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header Connection upgrade;
proxy_set_header Upgrade \$http_upgrade;
}
}
}
" > "$nginx_dev_conf"
if type docker &>/dev/null; then
docker rm --force wildcard-nginx >/dev/null 2>&1 || true
fi
if [[ -f $nginx_pid ]]; then
# We never run `nginx` without the `-c` argument, since that can load the default system configuration, which is
# different for different systems. It introduces too many unknowns, with little value.
# So we build a temp config, just to have a predictable value for the `pid` directive.
temp_nginx_conf="$working_dir/temp.nginx.conf"
echo "pid $nginx_pid; events { worker_connections $worker_connections; }" > "$temp_nginx_conf"
if ! nginx -c "$temp_nginx_conf" -s quit; then
echo "Failed to stop nginx. This is _probably_ okay, and things should work fine." >&2
echo " If not, try running 'lsof -iTCP:80 -sTCP:LISTEN -nPt | xargs kill', and then re-run this script." >&2
fi
# The above stop command will delete the pid file, but we still do this to ensure it really is gone.
rm -f "$nginx_pid" "$temp_nginx_conf"
unset temp_nginx_conf
fi
if [[ $run_as == nginx ]]; then
nginx -c "$nginx_dev_conf"
stop_cmd="nginx -c '$nginx_dev_conf' -s quit"
elif [[ $run_as == docker ]]; then
docker run \
--name wildcard-nginx \
--detach \
--publish 80:80 \
--publish 443:443 \
--add-host=host.docker.internal:host-gateway \
--volume "$nginx_dev_conf:/etc/nginx/nginx.conf:ro" \
--volume "$working_dir:$working_dir" \
nginx:alpine \
>/dev/null
stop_cmd='docker rm --force wildcard-nginx'
else
echo "I don't know how to start NGINX with '$run_as'."
fi
url_to_open=""
if [[ $use_https == 1 ]]; then
url_to_open="https://$domain"
if [[ $https_listen_port != 443 ]]; then
url_to_open="$url_to_open:$https_listen_port"
fi
else
url_to_open="http://localhost"
if [[ $http_listen_port != 80 ]]; then
url_to_open="$url_to_open:$http_listen_port"
fi
fi
echo '✅ Started NGINX'
echo " Stop with: $stop_cmd"
echo "🎉 $url_to_open"