Do not pipe into `grep -q`, because that stops reading as soon as a
[arvados.git] / tools / salt-install / provision.sh
index b840d86c6f360d3440328bd676dd66656739be5b..c9e535fad2dab3c105a8b6c627ec190d80840920 100755 (executable)
 # vagrant up
 
 set -o pipefail
-set -x
+
+_exit_handler() {
+  local rc="$?"
+  trap - EXIT
+  if [ "$rc" -ne 0 ]; then
+    echo "Error occurred ($rc) while running $0 at line $1 : $BASH_COMMAND"
+  fi
+  exit "$rc"
+}
+
+trap '_exit_handler $LINENO' EXIT ERR
 
 # capture the directory that the script is running from
 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
@@ -30,6 +40,7 @@ usage() {
   echo >&2 "                                                controller"
   echo >&2 "                                                dispatcher"
   echo >&2 "                                                keepproxy"
+  echo >&2 "                                                keepbalance"
   echo >&2 "                                                keepstore"
   echo >&2 "                                                keepweb"
   echo >&2 "                                                shell"
@@ -50,6 +61,7 @@ usage() {
   echo >&2 "                                                  for the selected role/s"
   echo >&2 "                                                - writes the resulting files into <dest_dir>"
   echo >&2 "  -v, --vagrant                               Run in vagrant and use the /vagrant shared dir"
+  echo >&2 "  --development                               Run in dev mode, using snakeoil certs"
   echo >&2
 }
 
@@ -61,7 +73,7 @@ arguments() {
   fi
 
   TEMP=$(getopt -o c:dhp:r:tv \
-    --long config:,debug,dump-config:,help,roles:,test,vagrant \
+    --long config:,debug,development,dump-config:,help,roles:,test,vagrant \
     -n "${0}" -- "${@}")
 
   if [ ${?} != 0 ];
@@ -99,11 +111,15 @@ arguments() {
         DUMP_CONFIG="yes"
         shift 2
         ;;
+      --development)
+        DEV_MODE="yes"
+        shift 1
+        ;;
       -r | --roles)
         for i in ${2//,/ }
           do
             # Verify the role exists
-            if [[ ! "database,api,controller,keepstore,websocket,keepweb,workbench2,webshell,keepproxy,shell,workbench,dispatcher" == *"$i"* ]]; then
+            if [[ ! "database,api,controller,keepstore,websocket,keepweb,workbench2,webshell,keepbalance,keepproxy,shell,workbench,dispatcher" == *"$i"* ]]; then
               echo "The role '${i}' is not a valid role"
               usage
               exit 1
@@ -132,6 +148,27 @@ arguments() {
   done
 }
 
+copy_custom_cert() {
+  cert_dir=${1}
+  cert_name=${2}
+
+  mkdir -p /srv/salt/certs
+
+  if [ -f ${cert_dir}/${cert_name}.crt ]; then
+    cp -v ${cert_dir}/${cert_name}.crt /srv/salt/certs/arvados-${cert_name}.pem
+  else
+    echo "${cert_dir}/${cert_name}.crt does not exist. Exiting"
+    exit 1
+  fi
+  if [ -f ${cert_dir}/${cert_name}.key ]; then
+    cp -v ${cert_dir}/${cert_name}.key /srv/salt/certs/arvados-${cert_name}.key
+  else
+    echo "${cert_dir}/${cert_name}.key does not exist. Exiting"
+    exit 1
+  fi
+}
+
+DEV_MODE="no"
 CONFIG_FILE="${SCRIPT_DIR}/local.params"
 CONFIG_DIR="local_config_dir"
 DUMP_CONFIG="no"
@@ -139,12 +176,13 @@ LOG_LEVEL="info"
 CONTROLLER_EXT_SSL_PORT=443
 TESTS_DIR="tests"
 
+NGINX_INSTALL_SOURCE="install_from_repo"
+
 CLUSTER=""
 DOMAIN=""
 
 # Hostnames/IPs used for single-host deploys
-HOSTNAME_EXT=""
-HOSTNAME_INT="127.0.1.1"
+IP_INT="127.0.1.1"
 
 # Initial user setup
 INITIAL_USER=""
@@ -160,6 +198,10 @@ WEBSOCKET_EXT_SSL_PORT=8002
 WORKBENCH1_EXT_SSL_PORT=443
 WORKBENCH2_EXT_SSL_PORT=3001
 
+SSL_MODE="self-signed"
+USE_LETSENCRYPT_ROUTE53="no"
+CUSTOM_CERTS_DIR="${SCRIPT_DIR}/certs"
+
 ## These are ARVADOS-related parameters
 # For a stable release, change RELEASE "production" and VERSION to the
 # package version (including the iteration, e.g. X.Y.Z-1) of the
@@ -176,9 +218,9 @@ VERSION="latest"
 # BRANCH="main"
 
 # Other formula versions we depend on
-POSTGRES_TAG="v0.41.6"
-NGINX_TAG="temp-fix-missing-statements-in-pillar"
-DOCKER_TAG="v2.0.7"
+POSTGRES_TAG="v0.44.0"
+NGINX_TAG="v2.8.1"
+DOCKER_TAG="v2.4.2"
 LOCALE_TAG="v0.3.4"
 LETSENCRYPT_TAG="v2.1.0"
 
@@ -226,7 +268,21 @@ if ! grep -qE '^[[:alnum:]]{5}$' <<<${CLUSTER} ; then
 fi
 
 # Only used in single_host/single_name deploys
-if [ "x${HOSTNAME_EXT}" = "x" ] ; then
+if [ ! -z "${HOSTNAME_EXT}" ] ; then
+  # We need to add some extra control vars to manage a single certificate vs. multiple
+  USE_SINGLE_HOSTNAME="yes"
+  # Make sure that the value configured as IP_INT is a real IP on the system.
+  # If we don't error out early here when there is a mismatch, the formula will
+  # fail with hard to interpret nginx errors later on.
+  ip addr list |grep "${IP_INT}/" >/dev/null
+  if [[ $? -ne 0 ]]; then
+    echo "Unable to find the IP_INT address '${IP_INT}' on the system, please correct the value in local.params. Exiting..."
+    exit 1
+  fi
+else
+  USE_SINGLE_HOSTNAME="no"
+  # We set this variable, anyway, so sed lines do not fail and we don't need to add more
+  # conditionals
   HOSTNAME_EXT="${CLUSTER}.${DOMAIN}"
 fi
 
@@ -241,7 +297,7 @@ else
   case ${OS_ID} in
     "centos")
       echo "WARNING! Disabling SELinux, see https://dev.arvados.org/issues/18019"
-      sed -i 's/SELINUX=enforcing/SELINUX=permissive' /etc/sysconfig/selinux
+      sed -i 's/SELINUX=enforcing/SELINUX=permissive/g' /etc/sysconfig/selinux
       setenforce permissive
       yum install -y  curl git jq
       ;;
@@ -285,24 +341,29 @@ rm -rf ${F_DIR}/* || exit 1
 git clone --quiet https://github.com/saltstack-formulas/docker-formula.git ${F_DIR}/docker
 ( cd docker && git checkout --quiet tags/"${DOCKER_TAG}" -b "${DOCKER_TAG}" )
 
+echo "...locale"
 git clone --quiet https://github.com/saltstack-formulas/locale-formula.git ${F_DIR}/locale
 ( cd locale && git checkout --quiet tags/"${LOCALE_TAG}" -b "${LOCALE_TAG}" )
 
-git clone --quiet https://github.com/netmanagers/nginx-formula.git ${F_DIR}/nginx
+echo "...nginx"
+git clone --quiet https://github.com/saltstack-formulas/nginx-formula.git ${F_DIR}/nginx
 ( cd nginx && git checkout --quiet tags/"${NGINX_TAG}" -b "${NGINX_TAG}" )
 
+echo "...postgres"
 git clone --quiet https://github.com/saltstack-formulas/postgres-formula.git ${F_DIR}/postgres
 ( cd postgres && git checkout --quiet tags/"${POSTGRES_TAG}" -b "${POSTGRES_TAG}" )
 
+echo "...letsencrypt"
 git clone --quiet https://github.com/saltstack-formulas/letsencrypt-formula.git ${F_DIR}/letsencrypt
 ( cd letsencrypt && git checkout --quiet tags/"${LETSENCRYPT_TAG}" -b "${LETSENCRYPT_TAG}" )
 
+echo "...arvados"
 git clone --quiet https://git.arvados.org/arvados-formula.git ${F_DIR}/arvados
 
 # If we want to try a specific branch of the formula
-if [ "x${BRANCH}" != "x" ]; then
+if [ "x${BRANCH}" != "x" -a $(git rev-parse --abbrev-ref HEAD) != "${BRANCH}" ]; then
   ( cd ${F_DIR}/arvados && git checkout --quiet -t origin/"${BRANCH}" -b "${BRANCH}" )
-elif [ "x${ARVADOS_TAG}" != "x" ]; then
+elif [ "x${ARVADOS_TAG}" != "x" -a $(git rev-parse --abbrev-ref HEAD) != "${ARVADOS_TAG}" ]; then
 ( cd ${F_DIR}/arvados && git checkout --quiet tags/"${ARVADOS_TAG}" -b "${ARVADOS_TAG}" )
 fi
 
@@ -333,7 +394,7 @@ for f in $(ls "${SOURCE_PILLARS_DIR}"/*); do
        s#__CLUSTER__#${CLUSTER}#g;
        s#__DOMAIN__#${DOMAIN}#g;
        s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
-       s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
+       s#__IP_INT__#${IP_INT}#g;
        s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
        s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
        s#__INITIAL_USER__#${INITIAL_USER}#g;
@@ -367,25 +428,34 @@ for f in $(ls "${SOURCE_PILLARS_DIR}"/*); do
   "${f}" > "${P_DIR}"/$(basename "${f}")
 done
 
-if [ "x${TEST}" = "xyes" ] && [ ! -d "${SOURCE_TESTS_DIR}" ]; then
-  echo "You requested to run tests, but ${SOURCE_TESTS_DIR} does not exist or is not a directory. Exiting."
-  exit 1
+if [ ! -d "${SOURCE_TESTS_DIR}" ]; then
+  echo "WARNING: The tests directory was not copied to \"${SOURCE_TESTS_DIR}\"."
+  if [ "x${TEST}" = "xyes" ]; then
+    echo "WARNING: Disabling tests for this installation."
+  fi
+  TEST="no"
+else
+  mkdir -p ${T_DIR}
+  # Replace cluster and domain name in the test files
+  for f in $(ls "${SOURCE_TESTS_DIR}"/*); do
+    FILTERS="s#__CLUSTER__#${CLUSTER}#g;
+         s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
+         s#__DOMAIN__#${DOMAIN}#g;
+         s#__IP_INT__#${IP_INT}#g;
+         s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
+         s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g
+         s#__INITIAL_USER__#${INITIAL_USER}#g;
+         s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
+         s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g"
+    if [ "$USE_SINGLE_HOSTNAME" = "yes" ]; then
+      FILTERS="s#__CLUSTER__.__DOMAIN__#${HOSTNAME_EXT}#g;
+         $FILTERS"
+    fi
+    sed "$FILTERS" \
+      "${f}" > ${T_DIR}/$(basename "${f}")
+  done
+  chmod 755 ${T_DIR}/run-test.sh
 fi
-mkdir -p ${T_DIR}
-# Replace cluster and domain name in the test files
-for f in $(ls "${SOURCE_TESTS_DIR}"/*); do
-  sed "s#__CLUSTER__#${CLUSTER}#g;
-       s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
-       s#__DOMAIN__#${DOMAIN}#g;
-       s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
-       s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
-       s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g
-       s#__INITIAL_USER__#${INITIAL_USER}#g;
-       s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
-       s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g" \
-  "${f}" > ${T_DIR}/$(basename "${f}")
-done
-chmod 755 ${T_DIR}/run-test.sh
 
 # Replace helper state files that differ from the formula's examples
 if [ -d "${SOURCE_STATES_DIR}" ]; then
@@ -398,7 +468,7 @@ if [ -d "${SOURCE_STATES_DIR}" ]; then
          s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
          s#__DOMAIN__#${DOMAIN}#g;
          s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
-         s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
+         s#__IP_INT__#${IP_INT}#g;
          s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
          s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
          s#__INITIAL_USER__#${INITIAL_USER}#g;
@@ -421,6 +491,7 @@ if [ -d "${SOURCE_STATES_DIR}" ]; then
          s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
          s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
          s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
+         s#__SHELL_INT_IP__#${SHELL_INT_IP}#g;
          s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
          s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
          s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
@@ -450,9 +521,21 @@ EOFPSLS
 
 # States, extra states
 if [ -d "${F_DIR}"/extra/extra ]; then
-  for f in $(ls "${F_DIR}"/extra/extra/*.sls); do
+  SKIP_SNAKE_OIL="snakeoil_certs"
+
+  if [[ "$DEV_MODE" = "yes" || "${SSL_MODE}" == "self-signed" ]] ; then
+    # In dev mode, we create some snake oil certs that we'll
+    # use as CUSTOM_CERTS, so we don't skip the states file.
+    # Same when using self-signed certificates.
+    SKIP_SNAKE_OIL="dont_add_snakeoil_certs"
+  fi
+  for f in $(ls "${F_DIR}"/extra/extra/*.sls | egrep -v "${SKIP_SNAKE_OIL}|shell_"); do
   echo "    - extra.$(basename ${f} | sed 's/.sls$//g')" >> ${S_DIR}/top.sls
   done
+  # Use byo or self-signed certificates
+  if [ "${SSL_MODE}" != "lets-encrypt" ]; then
+    mkdir -p "${F_DIR}"/extra/extra/files
+  fi
 fi
 
 # If we want specific roles for a node, just add the desired states
@@ -460,16 +543,29 @@ fi
 if [ -z "${ROLES}" ]; then
   # States
   echo "    - nginx.passenger" >> ${S_DIR}/top.sls
-  # Currently, only available on config_examples/multi_host/aws
-  if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
-    if [ "x${USE_LETSENCRYPT_IAM_USER}" = "xyes" ]; then
-      grep -q "aws_credentials" ${S_DIR}/top.sls || echo "    - aws_credentials" >> ${S_DIR}/top.sls
+  if [ "${SSL_MODE}" = "lets-encrypt" ]; then
+    if [ "${USE_LETSENCRYPT_ROUTE53}" = "yes" ]; then
+      grep -q "aws_credentials" ${S_DIR}/top.sls || echo "    - extra.aws_credentials" >> ${S_DIR}/top.sls
     fi
     grep -q "letsencrypt"     ${S_DIR}/top.sls || echo "    - letsencrypt" >> ${S_DIR}/top.sls
+  else
+    mkdir -p /srv/salt/certs
+    if [ "${SSL_MODE}" = "bring-your-own" ]; then
+      # Copy certs to formula extra/files
+      cp -rv ${CUSTOM_CERTS_DIR}/* /srv/salt/certs/
+      # We add the custom_certs state
+      grep -q "custom_certs"    ${S_DIR}/top.sls || echo "    - extra.custom_certs" >> ${S_DIR}/top.sls
+    fi
+    # In self-signed mode, the certificate files will be created and put in the
+    # destination directory by the snakeoil_certs.sls state file
   fi
+
   echo "    - postgres" >> ${S_DIR}/top.sls
   echo "    - docker.software" >> ${S_DIR}/top.sls
   echo "    - arvados" >> ${S_DIR}/top.sls
+  echo "    - extra.shell_sudo_passwordless" >> ${S_DIR}/top.sls
+  echo "    - extra.shell_cron_add_login_sync" >> ${S_DIR}/top.sls
+  echo "    - extra.passenger_rvm" >> ${S_DIR}/top.sls
 
   # Pillars
   echo "    - docker" >> ${P_DIR}/top.sls
@@ -483,16 +579,68 @@ if [ -z "${ROLES}" ]; then
   echo "    - nginx_workbench2_configuration" >> ${P_DIR}/top.sls
   echo "    - nginx_workbench_configuration" >> ${P_DIR}/top.sls
   echo "    - postgresql" >> ${P_DIR}/top.sls
-  # Currently, only available on config_examples/multi_host/aws
-  if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
-    if [ "x${USE_LETSENCRYPT_IAM_USER}" = "xyes" ]; then
+
+  # We need to tweak the Nginx's pillar depending whether we want plan nginx or nginx+passenger
+  NGINX_INSTALL_SOURCE="install_from_phusionpassenger"
+  sed -i "s/__NGINX_INSTALL_SOURCE__/${NGINX_INSTALL_SOURCE}/g" ${P_DIR}/nginx_passenger.sls
+
+  if [ "${SSL_MODE}" = "lets-encrypt" ]; then
+    if [ "${USE_LETSENCRYPT_ROUTE53}" = "yes" ]; then
       grep -q "aws_credentials" ${P_DIR}/top.sls || echo "    - aws_credentials" >> ${P_DIR}/top.sls
     fi
-    grep -q "letsencrypt"     ${P_DIR}/top.sls || echo "    - letsencrypt" >> ${P_DIR}/top.sls
+    grep -q "letsencrypt" ${P_DIR}/top.sls || echo "    - letsencrypt" >> ${P_DIR}/top.sls
+
+    # As the pillar differ whether we use LE or custom certs, we need to do a final edition on them
+    for c in controller websocket workbench workbench2 webshell download collections keepproxy; do
+      sed -i "s/__CERT_REQUIRES__/cmd: create-initial-cert-${c}.${CLUSTER}.${DOMAIN}*/g;
+              s#__CERT_PEM__#/etc/letsencrypt/live/${c}.${CLUSTER}.${DOMAIN}/fullchain.pem#g;
+              s#__CERT_KEY__#/etc/letsencrypt/live/${c}.${CLUSTER}.${DOMAIN}/privkey.pem#g" \
+      ${P_DIR}/nginx_${c}_configuration.sls
+    done
+  else
+    # Use custom certs (either dev mode or prod)
+    grep -q "extra_custom_certs" ${P_DIR}/top.sls || echo "    - extra_custom_certs" >> ${P_DIR}/top.sls
+    # And add the certs in the custom_certs pillar
+    echo "extra_custom_certs_dir: /srv/salt/certs" > ${P_DIR}/extra_custom_certs.sls
+    echo "extra_custom_certs:" >> ${P_DIR}/extra_custom_certs.sls
+
+    for c in controller websocket workbench workbench2 webshell keepweb keepproxy; do
+      # Are we in a single-host-single-hostname env?
+      if [ "${USE_SINGLE_HOSTNAME}" = "yes" ]; then
+        # Are we in a single-host-single-hostname env?
+        CERT_NAME=${HOSTNAME_EXT}
+      else
+        # We are in a multiple-hostnames env
+        CERT_NAME=${c}
+      fi
+
+      if [[ "$SSL_MODE" == "bring-your-own" ]]; then
+        copy_custom_cert ${CUSTOM_CERTS_DIR} ${CERT_NAME}
+      fi
+
+      grep -q ${CERT_NAME} ${P_DIR}/extra_custom_certs.sls || echo "  - ${CERT_NAME}" >> ${P_DIR}/extra_custom_certs.sls
+
+      # As the pillar differs whether we use LE or custom certs, we need to do a final edition on them
+      sed -i "s/__CERT_REQUIRES__/file: extra_custom_certs_file_copy_arvados-${CERT_NAME}.pem/g;
+              s#__CERT_PEM__#/etc/nginx/ssl/arvados-${CERT_NAME}.pem#g;
+              s#__CERT_KEY__#/etc/nginx/ssl/arvados-${CERT_NAME}.key#g" \
+      ${P_DIR}/nginx_${c}_configuration.sls
+    done
   fi
 else
   # If we add individual roles, make sure we add the repo first
   echo "    - arvados.repo" >> ${S_DIR}/top.sls
+  # We add the extra_custom_certs state
+  grep -q "extra.custom_certs"    ${S_DIR}/top.sls || echo "    - extra.custom_certs" >> ${S_DIR}/top.sls
+
+  # And we add the basic part for the certs pillar
+  if [ "${SSL_MODE}" != "lets-encrypt" ]; then
+    # And add the certs in the custom_certs pillar
+    echo "extra_custom_certs_dir: /srv/salt/certs" > ${P_DIR}/extra_custom_certs.sls
+    echo "extra_custom_certs:" >> ${P_DIR}/extra_custom_certs.sls
+    grep -q "extra_custom_certs" ${P_DIR}/top.sls || echo "    - extra_custom_certs" >> ${P_DIR}/top.sls
+  fi
+
   for R in ${ROLES}; do
     case "${R}" in
       "database")
@@ -505,65 +653,135 @@ else
         # States
         # FIXME: https://dev.arvados.org/issues/17352
         grep -q "postgres.client" ${S_DIR}/top.sls || echo "    - postgres.client" >> ${S_DIR}/top.sls
-        grep -q "nginx.passenger" ${S_DIR}/top.sls || echo "    - nginx.passenger" >> ${S_DIR}/top.sls
+        if grep -q "    - nginx.*$" ${S_DIR}/top.sls; then
+          sed -i s/"^    - nginx.*$"/"    - nginx.passenger"/g ${S_DIR}/top.sls
+        else
+          echo "    - nginx.passenger" >> ${S_DIR}/top.sls
+        fi
+        echo "    - extra.passenger_rvm" >> ${S_DIR}/top.sls
         ### If we don't install and run LE before arvados-api-server, it fails and breaks everything
-        ### after it so we add this here, as we are, after all, sharing the host for api and controller
-        # Currently, only available on config_examples/multi_host/aws
-        if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
-          if [ "x${USE_LETSENCRYPT_IAM_USER}" = "xyes" ]; then
+        ### after it. So we add this here as we are, after all, sharing the host for api and controller
+        if [ "${SSL_MODE}" = "lets-encrypt" ]; then
+          if [ "${USE_LETSENCRYPT_ROUTE53}" = "yes" ]; then
             grep -q "aws_credentials" ${S_DIR}/top.sls || echo "    - aws_credentials" >> ${S_DIR}/top.sls
           fi
-          grep -q "letsencrypt"     ${S_DIR}/top.sls || echo "    - letsencrypt" >> ${S_DIR}/top.sls
+          grep -q "letsencrypt" ${S_DIR}/top.sls || echo "    - letsencrypt" >> ${S_DIR}/top.sls
+        else
+          # Use custom certs
+          if [ "${SSL_MODE}" = "bring-your-own" ]; then
+            copy_custom_cert ${CUSTOM_CERTS_DIR} controller
+          fi
+          grep -q controller ${P_DIR}/extra_custom_certs.sls || echo "  - controller" >> ${P_DIR}/extra_custom_certs.sls
         fi
         grep -q "arvados.${R}" ${S_DIR}/top.sls    || echo "    - arvados.${R}" >> ${S_DIR}/top.sls
         # Pillars
         grep -q "aws_credentials" ${P_DIR}/top.sls          || echo "    - aws_credentials" >> ${P_DIR}/top.sls
-        grep -q "docker" ${P_DIR}/top.sls                   || echo "    - docker" >> ${P_DIR}/top.sls
         grep -q "postgresql" ${P_DIR}/top.sls               || echo "    - postgresql" >> ${P_DIR}/top.sls
         grep -q "nginx_passenger" ${P_DIR}/top.sls          || echo "    - nginx_passenger" >> ${P_DIR}/top.sls
         grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo "    - nginx_${R}_configuration" >> ${P_DIR}/top.sls
+
+        # We need to tweak the Nginx's pillar depending whether we want plain nginx or nginx+passenger
+        NGINX_INSTALL_SOURCE="install_from_phusionpassenger"
+        sed -i "s/__NGINX_INSTALL_SOURCE__/${NGINX_INSTALL_SOURCE}/g" ${P_DIR}/nginx_passenger.sls
       ;;
       "controller" | "websocket" | "workbench" | "workbench2" | "webshell" | "keepweb" | "keepproxy")
+        NGINX_INSTALL_SOURCE="install_from_repo"
         # States
-        grep -q "nginx.passenger" ${S_DIR}/top.sls || echo "    - nginx.passenger" >> ${S_DIR}/top.sls
-        # Currently, only available on config_examples/multi_host/aws
-        if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
-          if [ "x${USE_LETSENCRYPT_IAM_USER}" = "xyes" ]; then
+        if [ "${R}" = "workbench" ]; then
+          NGINX_INSTALL_SOURCE="install_from_phusionpassenger"
+          if grep -q "    - nginx$" ${S_DIR}/top.sls; then
+            sed -i s/"^    - nginx.*$"/"    - nginx.passenger"/g ${S_DIR}/top.sls
+          else
+            echo "    - nginx.passenger" >> ${S_DIR}/top.sls
+          fi
+        else
+          grep -q "nginx" ${S_DIR}/top.sls || echo "    - nginx" >> ${S_DIR}/top.sls
+        fi
+        if [ "${SSL_MODE}" = "lets-encrypt" ]; then
+          if [ "x${USE_LETSENCRYPT_ROUTE53}" = "xyes" ]; then
             grep -q "aws_credentials" ${S_DIR}/top.sls || echo "    - aws_credentials" >> ${S_DIR}/top.sls
           fi
           grep -q "letsencrypt"     ${S_DIR}/top.sls || echo "    - letsencrypt" >> ${S_DIR}/top.sls
+        else
+          # Use custom certs, special case for keepweb
+          if [ ${R} = "keepweb" ]; then
+            if [ "${SSL_MODE}" = "bring-your-own" ]; then
+              copy_custom_cert ${CUSTOM_CERTS_DIR} download
+              copy_custom_cert ${CUSTOM_CERTS_DIR} collections
+            fi
+          else
+            if [ "${SSL_MODE}" = "bring-your-own" ]; then
+              copy_custom_cert ${CUSTOM_CERTS_DIR} ${R}
+            fi
+          fi
         fi
         # webshell role is just a nginx vhost, so it has no state
         if [ "${R}" != "webshell" ]; then
-          grep -q "arvados.${R}" ${S_DIR}/top.sls    || echo "    - arvados.${R}" >> ${S_DIR}/top.sls
+          grep -q "arvados.${R}" ${S_DIR}/top.sls || echo "    - arvados.${R}" >> ${S_DIR}/top.sls
         fi
         # Pillars
         grep -q "nginx_passenger" ${P_DIR}/top.sls          || echo "    - nginx_passenger" >> ${P_DIR}/top.sls
         grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo "    - nginx_${R}_configuration" >> ${P_DIR}/top.sls
-        # Currently, only available on config_examples/multi_host/aws
-        if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
-          if [ "x${USE_LETSENCRYPT_IAM_USER}" = "xyes" ]; then
+        # Special case for keepweb
+        if [ ${R} = "keepweb" ]; then
+          grep -q "nginx_download_configuration" ${P_DIR}/top.sls || echo "    - nginx_download_configuration" >> ${P_DIR}/top.sls
+          grep -q "nginx_collections_configuration" ${P_DIR}/top.sls || echo "    - nginx_collections_configuration" >> ${P_DIR}/top.sls
+        fi
+
+        if [ "${SSL_MODE}" = "lets-encrypt" ]; then
+          if [ "${USE_LETSENCRYPT_ROUTE53}" = "yes" ]; then
             grep -q "aws_credentials" ${P_DIR}/top.sls || echo "    - aws_credentials" >> ${P_DIR}/top.sls
           fi
           grep -q "letsencrypt"     ${P_DIR}/top.sls || echo "    - letsencrypt" >> ${P_DIR}/top.sls
           grep -q "letsencrypt_${R}_configuration" ${P_DIR}/top.sls || echo "    - letsencrypt_${R}_configuration" >> ${P_DIR}/top.sls
+
+          # As the pillar differ whether we use LE or custom certs, we need to do a final edition on them
+          # Special case for keepweb
+          if [ ${R} = "keepweb" ]; then
+            for kwsub in download collections; do
+              sed -i "s/__CERT_REQUIRES__/cmd: create-initial-cert-${kwsub}.${CLUSTER}.${DOMAIN}*/g;
+                      s#__CERT_PEM__#/etc/letsencrypt/live/${kwsub}.${CLUSTER}.${DOMAIN}/fullchain.pem#g;
+                      s#__CERT_KEY__#/etc/letsencrypt/live/${kwsub}.${CLUSTER}.${DOMAIN}/privkey.pem#g" \
+              ${P_DIR}/nginx_${kwsub}_configuration.sls
+            done
+          else
+            sed -i "s/__CERT_REQUIRES__/cmd: create-initial-cert-${R}.${CLUSTER}.${DOMAIN}*/g;
+                    s#__CERT_PEM__#/etc/letsencrypt/live/${R}.${CLUSTER}.${DOMAIN}/fullchain.pem#g;
+                    s#__CERT_KEY__#/etc/letsencrypt/live/${R}.${CLUSTER}.${DOMAIN}/privkey.pem#g" \
+            ${P_DIR}/nginx_${R}_configuration.sls
+          fi
+        else
+          # As the pillar differ whether we use LE or custom certs, we need to do a final edition on them
+          # Special case for keepweb
+          if [ ${R} = "keepweb" ]; then
+            for kwsub in download collections; do
+              sed -i "s/__CERT_REQUIRES__/file: extra_custom_certs_file_copy_arvados-${kwsub}.pem/g;
+                      s#__CERT_PEM__#/etc/nginx/ssl/arvados-${kwsub}.pem#g;
+                      s#__CERT_KEY__#/etc/nginx/ssl/arvados-${kwsub}.key#g" \
+              ${P_DIR}/nginx_${kwsub}_configuration.sls
+              grep -q ${kwsub} ${P_DIR}/extra_custom_certs.sls || echo "  - ${kwsub}" >> ${P_DIR}/extra_custom_certs.sls
+            done
+          else
+            sed -i "s/__CERT_REQUIRES__/file: extra_custom_certs_file_copy_arvados-${R}.pem/g;
+                    s#__CERT_PEM__#/etc/nginx/ssl/arvados-${R}.pem#g;
+                    s#__CERT_KEY__#/etc/nginx/ssl/arvados-${R}.key#g" \
+            ${P_DIR}/nginx_${R}_configuration.sls
+            grep -q ${R}$ ${P_DIR}/extra_custom_certs.sls || echo "  - ${R}" >> ${P_DIR}/extra_custom_certs.sls
+          fi
         fi
+        # We need to tweak the Nginx's pillar depending whether we want plain nginx or nginx+passenger
+        sed -i "s/__NGINX_INSTALL_SOURCE__/${NGINX_INSTALL_SOURCE}/g" ${P_DIR}/nginx_passenger.sls
       ;;
       "shell")
         # States
+        echo "    - extra.shell_sudo_passwordless" >> ${S_DIR}/top.sls
+        echo "    - extra.shell_cron_add_login_sync" >> ${S_DIR}/top.sls
         grep -q "docker" ${S_DIR}/top.sls       || echo "    - docker.software" >> ${S_DIR}/top.sls
         grep -q "arvados.${R}" ${S_DIR}/top.sls || echo "    - arvados.${R}" >> ${S_DIR}/top.sls
         # Pillars
-        grep -q "" ${P_DIR}/top.sls                             || echo "    - docker" >> ${P_DIR}/top.sls
+        grep -q "docker" ${P_DIR}/top.sls       || echo "    - docker" >> ${P_DIR}/top.sls
       ;;
-      "dispatcher")
-        # States
-        grep -q "docker" ${S_DIR}/top.sls       || echo "    - docker.software" >> ${S_DIR}/top.sls
-        grep -q "arvados.${R}" ${S_DIR}/top.sls || echo "    - arvados.${R}" >> ${S_DIR}/top.sls
-        # Pillars
-        # ATM, no specific pillar needed
-      ;;
-      "keepstore")
+      "dispatcher" | "keepbalance" | "keepstore")
         # States
         grep -q "arvados.${R}" ${S_DIR}/top.sls || echo "    - arvados.${R}" >> ${S_DIR}/top.sls
         # Pillars
@@ -582,44 +800,29 @@ if [ "${DUMP_CONFIG}" = "yes" ]; then
   exit 0
 fi
 
-# FIXME! #16992 Temporary fix for psql call in arvados-api-server
-if [ -e /root/.psqlrc ]; then
-  if ! ( grep 'pset pager off' /root/.psqlrc ); then
-    RESTORE_PSQL="yes"
-    cp /root/.psqlrc /root/.psqlrc.provision.backup
-  fi
-else
-  DELETE_PSQL="yes"
-fi
-
-echo '\pset pager off' >> /root/.psqlrc
-# END FIXME! #16992 Temporary fix for psql call in arvados-api-server
-
 # Now run the install
 salt-call --local state.apply -l ${LOG_LEVEL}
 
-# FIXME! #16992 Temporary fix for psql call in arvados-api-server
-if [ "x${DELETE_PSQL}" = "xyes" ]; then
-  echo "Removing .psql file"
-  rm /root/.psqlrc
-fi
-
-if [ "x${RESTORE_PSQL}" = "xyes" ]; then
-  echo "Restoring .psql file"
-  mv -v /root/.psqlrc.provision.backup /root/.psqlrc
+# Finally, make sure that /etc/hosts is not overwritten on reboot
+if [ -d /etc/cloud/cloud.cfg.d ]; then
+  # TODO: will this work on CentOS?
+  sed -i 's/^manage_etc_hosts: true/#manage_etc_hosts: true/g' /etc/cloud/cloud.cfg.d/*
 fi
-# END FIXME! #16992 Temporary fix for psql call in arvados-api-server
 
 # Leave a copy of the Arvados CA so the user can copy it where it's required
-echo "Copying the Arvados CA certificate to the installer dir, so you can import it"
-# If running in a vagrant VM, also add default user to docker group
-if [ "x${VAGRANT}" = "xyes" ]; then
-  cp /etc/ssl/certs/arvados-snakeoil-ca.pem /vagrant/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
-
-  echo "Adding the vagrant user to the docker group"
-  usermod -a -G docker vagrant
-else
-  cp /etc/ssl/certs/arvados-snakeoil-ca.pem ${SCRIPT_DIR}/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
+if [ "$DEV_MODE" = "yes" ]; then
+  ARVADOS_SNAKEOIL_CA_DEST_FILE="${SCRIPT_DIR}/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem"
+
+  # If running in a vagrant VM, also add default user to docker group
+  if [ "x${VAGRANT}" = "xyes" ]; then
+    echo "Adding the vagrant user to the docker group"
+    usermod -a -G docker vagrant
+    ARVADOS_SNAKEOIL_CA_DEST_FILE="/vagrant/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem"
+  fi
+  if [ -f /etc/ssl/certs/arvados-snakeoil-ca.pem ]; then
+    echo "Copying the Arvados CA certificate to the installer dir, so you can import it"
+    cp /etc/ssl/certs/arvados-snakeoil-ca.pem ${ARVADOS_SNAKEOIL_CA_DEST_FILE}
+  fi
 fi
 
 # Test that the installation finished correctly