From 2f5405d86de7f935d576a5d5ebd9817dbc2d3a3f Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Wed, 27 Sep 2023 13:38:47 +0530 Subject: [PATCH] chore: Run NGINX with readonly root FS support (#27453) Part of supporting readonly root filesystem, gets NGINX to start without doing any writes to the filesystem, except for in `/tmp`. --- Dockerfile | 8 +++--- deploy/docker/fs/opt/appsmith/entrypoint.sh | 20 ++++++++------ .../docker/fs/opt/appsmith/init_ssl_cert.sh | 5 +--- .../docker/fs/opt/appsmith/prepare-image.mjs | 27 +++++++++++++++++++ deploy/docker/fs/opt/appsmith/run-nginx.sh | 9 +++---- .../fs/opt/appsmith/run-starting-page-init.sh | 4 +-- .../fs/opt/appsmith/starting-page-init.py | 2 +- .../opt/appsmith/templates/nginx-app.conf.sh | 9 +------ 8 files changed, 53 insertions(+), 31 deletions(-) create mode 100644 deploy/docker/fs/opt/appsmith/prepare-image.mjs diff --git a/Dockerfile b/Dockerfile index 49c93815cb..e885af058d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -53,7 +53,8 @@ RUN rm -rf \ VOLUME [ "/appsmith-stacks" ] # ------------------------------------------------------------------------ -ENV TMP /tmp/appsmith +ENV TMP="/tmp/appsmith" +ENV NGINX_WWW_PATH="$TMP/www" # Add backend server - Application Layer ARG JAR_FILE=./app/server/dist/server-*.jar @@ -81,9 +82,10 @@ COPY ./app/client/packages/rts/package.json ./app/client/packages/rts/dist rts/ RUN cd ./utils && npm install --only=prod && npm install --only=prod -g . && cd - \ && chmod 0644 /etc/cron.d/* \ - && chmod +x entrypoint.sh renew-certificate.sh healthcheck.sh /watchtower-hooks/*.sh \ + && chmod +x entrypoint.sh renew-certificate.sh healthcheck.sh templates/nginx-app.conf.sh /watchtower-hooks/*.sh \ # Disable setuid/setgid bits for the files inside container. - && find / \( -path /proc -prune \) -o \( \( -perm -2000 -o -perm -4000 \) -print -exec chmod -s '{}' + \) || true + && find / \( -path /proc -prune \) -o \( \( -perm -2000 -o -perm -4000 \) -print -exec chmod -s '{}' + \) || true \ + && node prepare-image.mjs # Update path to load appsmith utils tool as default ENV PATH /opt/appsmith/utils/node_modules/.bin:$PATH diff --git a/deploy/docker/fs/opt/appsmith/entrypoint.sh b/deploy/docker/fs/opt/appsmith/entrypoint.sh index 72679bbfbd..2d42520f26 100644 --- a/deploy/docker/fs/opt/appsmith/entrypoint.sh +++ b/deploy/docker/fs/opt/appsmith/entrypoint.sh @@ -7,7 +7,7 @@ stacks_path=/appsmith-stacks export SUPERVISORD_CONF_TARGET="$TMP/supervisor-conf.d/" # export for use in supervisord.conf export MONGODB_TMP_KEY_PATH="$TMP/mongodb-key" # export for use in supervisor process mongodb.conf -mkdir -pv "$SUPERVISORD_CONF_TARGET" +mkdir -pv "$SUPERVISORD_CONF_TARGET" "$NGINX_WWW_PATH" # ip is a reserved keyword for tracking events in Mixpanel. Instead of showing the ip as is Mixpanel provides derived properties. # As we want derived props alongwith the ip address we are sharing the ip address in separate keys @@ -392,15 +392,19 @@ init_postgres || runEmbeddedPostgres=0 } init_loading_pages(){ - # The default NGINX configuration includes an IPv6 listen directive. But not all - # servers support it, and we don't need it. So we remove it here before starting - # NGINX. - sed -i '/\[::\]:80 default_server;/d' /etc/nginx/sites-available/default local starting_page="/opt/appsmith/templates/appsmith_starting.html" local initializing_page="/opt/appsmith/templates/appsmith_initializing.html" - local editor_load_page="/opt/appsmith/editor/loading.html" - # Update default nginx page for initializing page - cp "$initializing_page" /var/www/html/index.nginx-debian.html + local editor_load_page="$NGINX_WWW_PATH/loading.html" + cp "$initializing_page" "$NGINX_WWW_PATH/index.html" + # TODO: Also listen on 443, if HTTP certs are available. + cat < "$TMP/nginx-app.conf" + server { + listen ${PORT:-80} default_server; + location / { + try_files \$uri \$uri/ /index.html =404; + } + } +EOF # Start nginx page to display the Appsmith is Initializing page nginx # Update editor nginx page for starting page diff --git a/deploy/docker/fs/opt/appsmith/init_ssl_cert.sh b/deploy/docker/fs/opt/appsmith/init_ssl_cert.sh index f8f6c4d282..2a384a78b5 100755 --- a/deploy/docker/fs/opt/appsmith/init_ssl_cert.sh +++ b/deploy/docker/fs/opt/appsmith/init_ssl_cert.sh @@ -9,10 +9,7 @@ init_ssl_cert() { mkdir -p "$data_path/www" echo "Re-generating nginx config template with domain" - bash "/opt/appsmith/templates/nginx-app.conf.sh" "0" "$APPSMITH_CUSTOM_DOMAIN" \ - | envsubst "$(printf '$%s,' $(env | grep -Eo '^APPSMITH_[A-Z0-9_]+'))" \ - | sed -e 's|\${\(APPSMITH_[A-Z0-9_]*\)}||g' \ - > /etc/nginx/sites-available/default + /opt/appsmith/templates/nginx-app.conf.sh "0" "$APPSMITH_CUSTOM_DOMAIN" echo "Start Nginx to verify certificate" nginx diff --git a/deploy/docker/fs/opt/appsmith/prepare-image.mjs b/deploy/docker/fs/opt/appsmith/prepare-image.mjs new file mode 100644 index 0000000000..eeced714fe --- /dev/null +++ b/deploy/docker/fs/opt/appsmith/prepare-image.mjs @@ -0,0 +1,27 @@ +import * as fs from "fs/promises"; + +const TMP = process.env.TMP; +const NGINX_WWW_PATH = process.env.NGINX_WWW_PATH; + +async function applyNginxChanges() { + const contents = await fs.readFile("/etc/nginx/nginx.conf", "utf8") + + const modContents = contents + .replace("pid /run/nginx.pid;", `pid ${TMP}/nginx.pid;`) + .replace("# server_tokens off;", "server_tokens off; more_set_headers 'Server: ';") + .replace("gzip on;", "gzip on; gzip_types *;") + .replace("include /etc/nginx/conf.d/*.conf;", [ + "include /etc/nginx/conf.d/*.conf;", + `include ${TMP}/nginx-app.conf;`, + `root ${NGINX_WWW_PATH};`, + ].join("\n")); + + await Promise.all([ + fs.writeFile("/etc/nginx/nginx.conf.original", contents), + fs.writeFile("/etc/nginx/nginx.conf", modContents), + fs.rm("/etc/nginx/sites-enabled", { recursive: true }), + fs.rm("/etc/nginx/conf.d", { recursive: true }), + ]) +} + +await applyNginxChanges(); diff --git a/deploy/docker/fs/opt/appsmith/run-nginx.sh b/deploy/docker/fs/opt/appsmith/run-nginx.sh index 35f1b412b0..e89933b8a4 100755 --- a/deploy/docker/fs/opt/appsmith/run-nginx.sh +++ b/deploy/docker/fs/opt/appsmith/run-nginx.sh @@ -65,14 +65,13 @@ if [[ -n ${APPSMITH_CUSTOM_DOMAIN-} ]] && [[ -z ${DYNO-} ]]; then fi fi -bash /opt/appsmith/templates/nginx-app.conf.sh "$use_https" "${APPSMITH_CUSTOM_DOMAIN-}" > /etc/nginx/sites-available/default +/opt/appsmith/templates/nginx-app.conf.sh "$use_https" "${APPSMITH_CUSTOM_DOMAIN-}" + +cp -r /opt/appsmith/editor/* "$NGINX_WWW_PATH" apply-env-vars() { original="$1" served="$2" - if [[ ! -f $original ]]; then - cp -v "$served" "$original" - fi node -e ' const fs = require("fs") const content = fs.readFileSync("'"$original"'", "utf8").replace( @@ -86,6 +85,6 @@ apply-env-vars() { popd } -apply-env-vars /opt/appsmith/index.html.original /opt/appsmith/editor/index.html +apply-env-vars /opt/appsmith/editor/index.html "$NGINX_WWW_PATH/index.html" exec nginx -g "daemon off;error_log stderr info;" diff --git a/deploy/docker/fs/opt/appsmith/run-starting-page-init.sh b/deploy/docker/fs/opt/appsmith/run-starting-page-init.sh index 98f6503f42..310d67f18e 100644 --- a/deploy/docker/fs/opt/appsmith/run-starting-page-init.sh +++ b/deploy/docker/fs/opt/appsmith/run-starting-page-init.sh @@ -1,4 +1,4 @@ #!/bin/bash -python3 /opt/appsmith/starting-page-init.py -rm -f /opt/appsmith/editor/loading.html \ No newline at end of file +python3 /opt/appsmith/starting-page-init.py +rm -f "$NGINX_WWW_PATH/loading.html" diff --git a/deploy/docker/fs/opt/appsmith/starting-page-init.py b/deploy/docker/fs/opt/appsmith/starting-page-init.py index c86ec6faf2..45fc9cb7ef 100644 --- a/deploy/docker/fs/opt/appsmith/starting-page-init.py +++ b/deploy/docker/fs/opt/appsmith/starting-page-init.py @@ -9,7 +9,7 @@ import urllib.request LOADING_TEMPLATE_PAGE = r'/opt/appsmith/templates/appsmith_starting.html' -LOADING_PAGE_EDITOR = r'/opt/appsmith/editor/loading.html' +LOADING_PAGE_EDITOR = os.getenv("NGINX_WWW_PATH") + '/loading.html' BACKEND_HEALTH_ENDPOINT = "http://localhost:8080/api/v1/health" LOG_FILE = r'/appsmith-stacks/logs/backend/starting_page_init.log' LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' diff --git a/deploy/docker/fs/opt/appsmith/templates/nginx-app.conf.sh b/deploy/docker/fs/opt/appsmith/templates/nginx-app.conf.sh index 07a368a62c..fe79b3b06c 100755 --- a/deploy/docker/fs/opt/appsmith/templates/nginx-app.conf.sh +++ b/deploy/docker/fs/opt/appsmith/templates/nginx-app.conf.sh @@ -22,7 +22,7 @@ additional_downstream_headers=' add_header X-Content-Type-Options "nosniff"; ' -cat < "$TMP/nginx-app.conf" map \$http_x_forwarded_proto \$origin_scheme { default \$http_x_forwarded_proto; '' \$scheme; @@ -41,9 +41,6 @@ map \$http_forwarded \$final_forwarded { # redirect log to stdout for supervisor to capture access_log /dev/stdout; -server_tokens off; -more_set_headers 'Server: '; - server { $( @@ -72,10 +69,6 @@ fi client_max_body_size 150m; - gzip on; - gzip_types *; - - root /opt/appsmith/editor; index index.html; error_page 404 /;