diff --git a/deploy/docker/README.md b/deploy/docker/README.md index f43f0872e3..4b3c31a824 100644 --- a/deploy/docker/README.md +++ b/deploy/docker/README.md @@ -87,6 +87,16 @@ To make Appsmith available on a custom domain, please update your domain's DNS r * [NameCheap](https://www.namecheap.com/support/knowledgebase/article.aspx/9776/2237/how-to-create-a-subdomain-for-my-domain) * [Domain.com](https://www.domain.com/help/article/domain-management-how-to-update-subdomains) +## Custom SSL Certificate + +In our container, we support to generate a free SSL certificate If you have your owned certificate, please follow these steps to use it inside the container. +- Firstly, please rename your certificate file as `fullchain.pem` and key file as `privkey.pem` +- Then, copy these files into the sub-directory `/ssl/` (*Note: Please change `` by the mounting volume directory in the `docker-compose.yml`. Default is `./stacks`*) +- Restart the container using `docker restart appsmith` + +The container will check the certificate files in the folder `/ssl` and use them if they are existed. + +*Note: In case of the certificate files have different name from `fullchain.pem` and `privkey.pem`, it will be considered as missing custom certificate and auto-provisioning the certificate by Let's Encrypt* ## Instance Management Utilities The image includes an `appsmithctl` command to help with the management and maintenance of your instance. The following subsections describe what's available. diff --git a/deploy/docker/entrypoint.sh b/deploy/docker/entrypoint.sh index 1273f0a49b..cfd81f3b69 100755 --- a/deploy/docker/entrypoint.sh +++ b/deploy/docker/entrypoint.sh @@ -3,193 +3,216 @@ set -e check_initialized_db() { - echo 'Check initialized database' - shouldPerformInitdb=1 - for path in \ - "$MONGO_DB_PATH/WiredTiger" \ - "$MONGO_DB_PATH/journal" \ - "$MONGO_DB_PATH/local.0" \ - "$MONGO_DB_PATH/storage.bson"; do - if [ -e "$path" ]; then - shouldPerformInitdb=0 - return - fi - done - echo "Should initialize database" + echo 'Check initialized database' + shouldPerformInitdb=1 + for path in \ + "$MONGO_DB_PATH/WiredTiger" \ + "$MONGO_DB_PATH/journal" \ + "$MONGO_DB_PATH/local.0" \ + "$MONGO_DB_PATH/storage.bson"; do + if [ -e "$path" ]; then + shouldPerformInitdb=0 + return + fi + done + echo "Should initialize database" } init_mongodb() { - echo "Init database" - MONGO_DB_PATH="/appsmith-stacks/data/mongodb" - MONGO_LOG_PATH="$MONGO_DB_PATH/log" - MONGO_DB_KEY="$MONGO_DB_PATH/key" - mkdir -p "$MONGO_DB_PATH" - touch "$MONGO_LOG_PATH" + echo "Init database" + MONGO_DB_PATH="/appsmith-stacks/data/mongodb" + MONGO_LOG_PATH="$MONGO_DB_PATH/log" + MONGO_DB_KEY="$MONGO_DB_PATH/key" + mkdir -p "$MONGO_DB_PATH" + touch "$MONGO_LOG_PATH" - check_initialized_db + check_initialized_db - if [[ $shouldPerformInitdb -gt 0 ]]; then - # Start installed MongoDB service - Dependencies Layer - mongod --fork --port 27017 --dbpath "$MONGO_DB_PATH" --logpath "$MONGO_LOG_PATH" - echo "Waiting 10s for mongodb init" - sleep 10 - bash "/opt/appsmith/templates/mongo-init.js.sh" "$MONGO_INITDB_ROOT_USERNAME" "$MONGO_INITDB_ROOT_PASSWORD" >"/appsmith-stacks/configuration/mongo-init.js" - mongo "127.0.0.1/${MONGO_INITDB_DATABASE}" /appsmith-stacks/configuration/mongo-init.js - echo "Seeding db done" + if [[ $shouldPerformInitdb -gt 0 ]]; then + # Start installed MongoDB service - Dependencies Layer + mongod --fork --port 27017 --dbpath "$MONGO_DB_PATH" --logpath "$MONGO_LOG_PATH" + echo "Waiting 10s for mongodb init" + sleep 10 + bash "/opt/appsmith/templates/mongo-init.js.sh" "$MONGO_INITDB_ROOT_USERNAME" "$MONGO_INITDB_ROOT_PASSWORD" >"/appsmith-stacks/configuration/mongo-init.js" + mongo "127.0.0.1/${MONGO_INITDB_DATABASE}" /appsmith-stacks/configuration/mongo-init.js + echo "Seeding db done" - echo "Enable replica set" - mongod --dbpath "$MONGO_DB_PATH" --shutdown || true - echo "Fork process" - openssl rand -base64 756 >"$MONGO_DB_KEY" - chmod go-rwx,u-wx "$MONGO_DB_KEY" - mongod --fork --port 27017 --dbpath "$MONGO_DB_PATH" --logpath "$MONGO_LOG_PATH" --replSet mr1 --keyFile "$MONGO_DB_KEY" --bind_ip localhost - echo "Waiting 10s for mongodb init with replica set" - sleep 10 - mongo "$APPSMITH_MONGODB_URI" --eval 'rs.initiate()' - mongod --dbpath "$MONGO_DB_PATH" --shutdown || true - fi + echo "Enable replica set" + mongod --dbpath "$MONGO_DB_PATH" --shutdown || true + echo "Fork process" + openssl rand -base64 756 >"$MONGO_DB_KEY" + chmod go-rwx,u-wx "$MONGO_DB_KEY" + mongod --fork --port 27017 --dbpath "$MONGO_DB_PATH" --logpath "$MONGO_LOG_PATH" --replSet mr1 --keyFile "$MONGO_DB_KEY" --bind_ip localhost + echo "Waiting 10s for mongodb init with replica set" + sleep 10 + mongo "$APPSMITH_MONGODB_URI" --eval 'rs.initiate()' + mongod --dbpath "$MONGO_DB_PATH" --shutdown || true + fi } init_ssl_cert() { - local domain="$1" - NGINX_SSL_CMNT="" + local domain="$1" + NGINX_SSL_CMNT="" - local rsa_key_size=4096 - local data_path="/appsmith-stacks/data/certificate" + local rsa_key_size=4096 + local data_path="/appsmith-stacks/data/certificate" - mkdir -p "$data_path" "$data_path"/{conf,www} + mkdir -p "$data_path" "$data_path"/{conf,www} - if ! [[ -e "$data_path/conf/options-ssl-nginx.conf" && -e "$data_path/conf/ssl-dhparams.pem" ]]; then - echo "Downloading recommended TLS parameters..." - curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf >"$data_path/conf/options-ssl-nginx.conf" - curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem >"$data_path/conf/ssl-dhparams.pem" - echo - fi + if ! [[ -e "$data_path/conf/options-ssl-nginx.conf" && -e "$data_path/conf/ssl-dhparams.pem" ]]; then + echo "Downloading recommended TLS parameters..." + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf >"$data_path/conf/options-ssl-nginx.conf" + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem >"$data_path/conf/ssl-dhparams.pem" + echo + fi - echo "Re-generating nginx config template with domain" - bash "/opt/appsmith/templates/nginx_app.conf.sh" "$NGINX_SSL_CMNT" "$APPSMITH_CUSTOM_DOMAIN" >"/etc/nginx/conf.d/nginx_app.conf.template" + echo "Re-generating nginx config template with domain" + bash "/opt/appsmith/templates/nginx_app.conf.sh" "$NGINX_SSL_CMNT" "$APPSMITH_CUSTOM_DOMAIN" >"/etc/nginx/conf.d/nginx_app.conf.template" - echo "Generating nginx configuration" - cat /etc/nginx/conf.d/nginx_app.conf.template | envsubst "$(printf '$%s,' $(env | grep -Eo '^APPSMITH_[A-Z0-9_]+'))" | sed -e 's|\${\(APPSMITH_[A-Z0-9_]*\)}||g' >/etc/nginx/sites-available/default + echo "Generating nginx configuration" + cat /etc/nginx/conf.d/nginx_app.conf.template | envsubst "$(printf '$%s,' $(env | grep -Eo '^APPSMITH_[A-Z0-9_]+'))" | sed -e 's|\${\(APPSMITH_[A-Z0-9_]*\)}||g' >/etc/nginx/sites-available/default - local live_path="/etc/letsencrypt/live/$domain" - if [[ -e "$live_path" ]]; then - echo "Existing certificate for domain $domain" - nginx -s reload - return - fi + local live_path="/etc/letsencrypt/live/$domain" + local ssl_path="/appsmith-stacks/ssl" + if [[ -e "$ssl_path/fullchain.pem" ]] && [[ -e "$ssl_path/privkey.pem" ]]; then + echo "Existing custom certificate" + nginx -s reload + return + fi - echo "Creating certificate for '$domain'" + if [[ -e "$live_path" ]]; then + echo "Existing certificate for domain $domain" + nginx -s reload + return + fi - echo "Requesting Let's Encrypt certificate for '$domain'..." - echo "Generating OpenSSL key for '$domain'..." + echo "Creating certificate for '$domain'" - mkdir -p "$live_path" && openssl req -x509 -nodes -newkey rsa:2048 -days 1 \ - -keyout "$live_path/privkey.pem" \ - -out "$live_path/fullchain.pem" \ - -subj "/CN=localhost" + echo "Requesting Let's Encrypt certificate for '$domain'..." + echo "Generating OpenSSL key for '$domain'..." - echo "Reload Nginx" - nginx -s reload + mkdir -p "$live_path" && openssl req -x509 -nodes -newkey rsa:2048 -days 1 \ + -keyout "$live_path/privkey.pem" \ + -out "$live_path/fullchain.pem" \ + -subj "/CN=localhost" - echo "Removing key now that validation is done for $domain..." - rm -Rfv /etc/letsencrypt/live/$domain /etc/letsencrypt/archive/$domain /etc/letsencrypt/renewal/$domain.conf + echo "Reload Nginx" + nginx -s reload - echo "Generating certification for domain $domain" - mkdir -p "$data_path/certbot" - certbot certonly --webroot --webroot-path="$data_path/certbot" \ - --register-unsafely-without-email \ - --domains $domain \ - --rsa-key-size $rsa_key_size \ - --agree-tos \ - --force-renewal + echo "Removing key now that validation is done for $domain..." + rm -Rfv /etc/letsencrypt/live/$domain /etc/letsencrypt/archive/$domain /etc/letsencrypt/renewal/$domain.conf - echo "Reload nginx" - nginx -s reload + echo "Generating certification for domain $domain" + mkdir -p "$data_path/certbot" + certbot certonly --webroot --webroot-path="$data_path/certbot" \ + --register-unsafely-without-email \ + --domains $domain \ + --rsa-key-size $rsa_key_size \ + --agree-tos \ + --force-renewal + + echo "Reload nginx" + + cat /etc/nginx/conf.d/nginx_app.conf.template | envsubst "$(printf '$%s,' $(env | grep -Eo '^APPSMITH_[A-Z0-9_]+'))" | sed -e 's|\${\(APPSMITH_[A-Z0-9_]*\)}||g' >/etc/nginx/sites-available/default + nginx -s reload } configure_ssl() { - NGINX_SSL_CMNT="#" + NGINX_SSL_CMNT="#" - echo "Mounting Let's encrypt folder" - rm -rf /etc/letsencrypt - mkdir -p /appsmith-stacks/letsencrypt - ln -s /appsmith-stacks/letsencrypt /etc/letsencrypt + echo "Mounting Let's encrypt folder" + rm -rf /etc/letsencrypt + mkdir -p /appsmith-stacks/{letsencrypt,ssl} + ln -s /appsmith-stacks/letsencrypt /etc/letsencrypt - echo "Generating nginx config template without domain" - bash "/opt/appsmith/templates/nginx_app.conf.sh" "$NGINX_SSL_CMNT" "$APPSMITH_CUSTOM_DOMAIN" > "/etc/nginx/conf.d/nginx_app.conf.template" + echo "Generating nginx config template without domain" + bash "/opt/appsmith/templates/nginx_app.conf.sh" "$NGINX_SSL_CMNT" "$APPSMITH_CUSTOM_DOMAIN" >"/etc/nginx/conf.d/nginx_app.conf.template" - echo "Generating nginx configuration" - cat /etc/nginx/conf.d/nginx_app.conf.template | envsubst "$(printf '$%s,' $(env | grep -Eo '^APPSMITH_[A-Z0-9_]+'))" | sed -e 's|\${\(APPSMITH_[A-Z0-9_]*\)}||g' > /etc/nginx/sites-available/default - nginx + echo "Generating nginx configuration" + cat /etc/nginx/conf.d/nginx_app.conf.template | envsubst "$(printf '$%s,' $(env | grep -Eo '^APPSMITH_[A-Z0-9_]+'))" | sed -e 's|\${\(APPSMITH_[A-Z0-9_]*\)}||g' >/etc/nginx/sites-available/default + nginx - if [[ -n $APPSMITH_CUSTOM_DOMAIN ]]; then - init_ssl_cert "$APPSMITH_CUSTOM_DOMAIN" - fi - nginx -s stop + if [[ -n $APPSMITH_CUSTOM_DOMAIN ]]; then + init_ssl_cert "$APPSMITH_CUSTOM_DOMAIN" + fi + nginx -s stop } configure_supervisord() { - SUPERVISORD_CONF_PATH="/opt/appsmith/templates/supervisord" - if [[ -f "/etc/supervisor/conf.d/"*.conf ]]; then - rm "/etc/supervisor/conf.d/"* - fi + SUPERVISORD_CONF_PATH="/opt/appsmith/templates/supervisord" + if [[ -z "$(ls -A /etc/supervisor/conf.d)" ]]; then + rm "/etc/supervisor/conf.d/"* + fi - cp -f "$SUPERVISORD_CONF_PATH/application_process/"*.conf /etc/supervisor/conf.d - if [[ "$APPSMITH_MONGODB_URI" = "mongodb://appsmith:$MONGO_INITDB_ROOT_PASSWORD@localhost/appsmith" ]]; then - cp "$SUPERVISORD_CONF_PATH/mongodb.conf" /etc/supervisor/conf.d/ - fi - if [[ "$APPSMITH_REDIS_URL" = "redis://127.0.0.1:6379" ]]; then - cp "$SUPERVISORD_CONF_PATH/redis.conf" /etc/supervisor/conf.d/ + cp -f "$SUPERVISORD_CONF_PATH/application_process/"*.conf /etc/supervisor/conf.d + + # Disable services based on configuration + if [[ "$APPSMITH_MONGODB_URI" = "mongodb://appsmith:$MONGO_INITDB_ROOT_PASSWORD@localhost/appsmith" ]]; then + cp "$SUPERVISORD_CONF_PATH/mongodb.conf" /etc/supervisor/conf.d/ + fi + if [[ "$APPSMITH_REDIS_URL" = "redis://127.0.0.1:6379" ]]; then + cp "$SUPERVISORD_CONF_PATH/redis.conf" /etc/supervisor/conf.d/ # Enable saving Redis session data to disk more often, so recent sessions aren't cleared on restart. sed -i 's/^# save 60 10000$/save 60 1/g' /etc/redis/redis.conf - fi + fi + if ! [[ -e "/appsmith-stacks/ssl/fullchain.pem" ]] || ! [[ -e "/appsmith-stacks/ssl/privkey.pem" ]]; then + cp "$SUPERVISORD_CONF_PATH/cron.conf" /etc/supervisor/conf.d/ + fi } echo 'Checking configuration file' CONF_PATH="/appsmith-stacks/configuration" ENV_PATH="$CONF_PATH/docker.env" if ! [[ -e "$ENV_PATH" ]]; then - echo "Generating default configuration file" - mkdir -p "$CONF_PATH" - AUTO_GEN_MONGO_PASSWORD=$(tr -dc A-Za-z0-9 "$ENV_PATH" + echo "Generating default configuration file" + mkdir -p "$CONF_PATH" + AUTO_GEN_MONGO_PASSWORD=$( + tr -dc A-Za-z0-9 "$ENV_PATH" fi if [[ -f "$ENV_PATH" ]]; then - sed -i 's/APPSMITH_MONGO_USERNAME/MONGO_INITDB_ROOT_USERNAME/; s/APPSMITH_MONGO_PASSWORD/MONGO_INITDB_ROOT_PASSWORD/; s/APPSMITH_MONGO_DATABASE/MONGO_INITDB_DATABASE/' "$ENV_PATH" - echo 'Load environment configuration' - set -o allexport - . "$ENV_PATH" - set +o allexport + sed -i 's/APPSMITH_MONGO_USERNAME/MONGO_INITDB_ROOT_USERNAME/; s/APPSMITH_MONGO_PASSWORD/MONGO_INITDB_ROOT_PASSWORD/; s/APPSMITH_MONGO_DATABASE/MONGO_INITDB_DATABASE/' "$ENV_PATH" + echo 'Load environment configuration' + set -o allexport + . "$ENV_PATH" + set +o allexport fi # Check for enviroment vairalbes echo 'Checking environment configuration' if [[ -z "${APPSMITH_MAIL_ENABLED}" ]]; then - unset APPSMITH_MAIL_ENABLED # If this field is empty is might cause application crash + unset APPSMITH_MAIL_ENABLED # If this field is empty is might cause application crash fi if [[ -z "${APPSMITH_OAUTH2_GITHUB_CLIENT_ID}" ]] || [[ -z "${APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET}" ]]; then - unset APPSMITH_OAUTH2_GITHUB_CLIENT_ID # If this field is empty is might cause application crash - unset APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET + unset APPSMITH_OAUTH2_GITHUB_CLIENT_ID # If this field is empty is might cause application crash + unset APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET fi if [[ -z "${APPSMITH_OAUTH2_GOOGLE_CLIENT_ID}" ]] || [[ -z "${APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET}" ]]; then - unset APPSMITH_OAUTH2_GOOGLE_CLIENT_ID # If this field is empty is might cause application crash - unset APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET + unset APPSMITH_OAUTH2_GOOGLE_CLIENT_ID # If this field is empty is might cause application crash + unset APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET fi if [[ -z "${APPSMITH_GOOGLE_MAPS_API_KEY}" ]]; then - unset APPSMITH_GOOGLE_MAPS_API_KEY + unset APPSMITH_GOOGLE_MAPS_API_KEY fi if [[ -z "${APPSMITH_RECAPTCHA_SITE_KEY}" ]] || [[ -z "${APPSMITH_RECAPTCHA_SECRET_KEY}" ]] || [[ -z "${APPSMITH_RECAPTCHA_ENABLED}" ]]; then - unset APPSMITH_RECAPTCHA_SITE_KEY # If this field is empty is might cause application crash - unset APPSMITH_RECAPTCHA_SECRET_KEY - unset APPSMITH_RECAPTCHA_ENABLED + unset APPSMITH_RECAPTCHA_SITE_KEY # If this field is empty is might cause application crash + unset APPSMITH_RECAPTCHA_SECRET_KEY + unset APPSMITH_RECAPTCHA_ENABLED fi # Main Section diff --git a/deploy/docker/templates/nginx_app.conf.sh b/deploy/docker/templates/nginx_app.conf.sh index 1063995178..ece3efd5fb 100644 --- a/deploy/docker/templates/nginx_app.conf.sh +++ b/deploy/docker/templates/nginx_app.conf.sh @@ -5,69 +5,79 @@ set -o nounset NGINX_SSL_CMNT="$1" CUSTOM_DOMAIN="$2" +# By default, container will use the auto-generate certificate by Let's Encrypt +SSL_CERT_PATH="/etc/letsencrypt/live/$CUSTOM_DOMAIN/fullchain.pem" +SSL_KEY_PATH="/etc/letsencrypt/live/$CUSTOM_DOMAIN/privkey.pem" + +# In case of existing custom certificate, container will use them to configure SSL +if [[ -e "/appsmith-stacks/ssl/fullchain.pem" ]] && [[ -e "/appsmith-stacks/ssl/privkey.pem" ]]; then + SSL_CERT_PATH="/appsmith-stacks/ssl/fullchain.pem" + SSL_KEY_PATH="/appsmith-stacks/ssl/privkey.pem" +fi + cat <