#!/bin/bash
################################################################################
# Copyright (c) 2016-2017 VMware, Inc. All rights reserved.
################################################################################
# Wrapper script aimed at refreshing SSL certificates on a vCenter server
# for PostgreSQL.

if [ -z $VMWARE_POSTGRES_BASE ]; then
   echo "VMWARE_POSTGRES_BASE is not set."
   echo "Check your installation."
   exit 1
fi
SANITY_FILE=$VMWARE_POSTGRES_BASE/scripts/vpostgres_sanity_checks
if [ -f $SANITY_FILE ]; then
   source $SANITY_FILE
else
   echo "Sanity check file for environment variables of VMware Postgres"
   echo "is not available. Check your installation."
   exit 1
fi

if [ -z $VMWARE_CIS_HOME ]; then
   echo "VMWARE_CIS_HOME is not set."
   echo "Check your installation."
   exit 1
fi

# Wrapper to log given message to stderr, which is something that
# can be digested by vMon when running this script as part of the
# pre-start command of service vmware-vpostgres.
log_stderr()
{
   >&2 echo ${1}
}

# Show utility help
show_help()
{
   ERROR_NUM=$1
   log_stderr "Usage: `basename $0`"
   log_stderr "Example: `basename $0`"
   exit $ERROR_NUM
}

EXPECTED_ARGS=0
if [ $# -ne $EXPECTED_ARGS ]; then
   # Leave with an error code
   show_help 1
fi

# Create path in charge of storing all the SSL-related data if not
# present yet. Permission are enforced on purpose so as subsequent
# startup of PostgreSQL is never messed up.
if [ ! -d "${VMWARE_POSTGRES_SSL_DATA}" ]; then
   mkdir ${VMWARE_POSTGRES_SSL_DATA}
fi
chmod 700 ${VMWARE_POSTGRES_SSL_DATA}
chown ${VMWARE_POSTGRES_OS_ADMIN}:${VMWARE_POSTGRES_VMON_GROUP} \
      ${VMWARE_POSTGRES_SSL_DATA}

# Utility routine able to print out all certificates in a given store.
print_full_store()
{
   STORE_NAME=$1
   OUTPUT_FILE=$2
   OUTPUT_TMP=$OUTPUT_FILE.tmp
   ALIAS_TMP=$OUTPUT_FILE.alias

   log_stderr "Generating ${OUTPUT_FILE} using store ${STORE_NAME}"

   MAX_RETRY=10
   RETRY_COUNT=0
   while [[ ${RETRY_COUNT} -lt ${MAX_RETRY} ]]; do
      ((RETRY_COUNT++))

      if [[ ${RETRY_COUNT} -gt 1 ]]; then
         log_stderr "Sleeping before retry..."
         sleep 1
      fi
      # Store all the aliases from this store, all are prefixed by the
      # string "Alias : ". It is unreliable to expect such a format, but
      # as there are no APIs able to dump all the certificates present
      # in a single store to a file, this is the only way to go. Dumping
      # those aliases into a file also prevents from hazards induced by
      # aliases using spaces or characters that could be used as separators.
      log_stderr "Grabbing alias list for store ${STORE_NAME}, attempt ${RETRY_COUNT}"
      ${VMWARE_CIS_HOME}/vmware-vmafd/bin/vecs-cli entry list \
          --store "${STORE_NAME}" | grep "Alias" > ${ALIAS_TMP}
      ERRNUM=$?
      if [ "$ERRNUM" != 0 ]; then
         log_stderr "Could not fetch aliases from store ${STORE_NAME}"
         # Cleanup the temporary file before next iteration.
         rm -f ${ALIAS_TMP}
         continue
      fi

      # Read each alias, ripping off the prefix always present.
      # XXX: Once vecs-cli offers a cleaner API, we should really get rid off
      # this custom parsing.
      while IFS= read -r entry; do
         alias=${entry:8}
         log_stderr "Using alias ${alias} to fetch data from store ${STORE_NAME}, attempt ${RETRY_COUNT}"
         ${VMWARE_CIS_HOME}/vmware-vmafd/bin/vecs-cli entry getcert \
             --store "${STORE_NAME}" --alias "${alias}" >> ${OUTPUT_TMP}
         ERRNUM=$?
         if [ "$ERRNUM" != 0 ]; then
            log_stderr "Could not fetch certificate of alias ${alias}"
            # Cleanup the temporary files before next iteration.
            rm -f ${ALIAS_TMP} ${OUTPUT_TMP}
            continue
         fi
      done < "${ALIAS_TMP}" # end of read loop
      # We reach this point if everything was successful, so no more retry is
      # needed.
      break
   done # end of retry loop

   # Detect if we could get the certificate or not.
   if [[ ! -f ${OUTPUT_TMP} ]]; then
       log_stderr "Could not generate ${OUTPUT_FILE} using store ${STORE_NAME}"
       exit 1
   fi

   # Rename result in place and set permissions
   log_stderr "Copying ${OUTPUT_TMP} to ${OUTPUT_FILE}"
   mv "${OUTPUT_TMP}" "${OUTPUT_FILE}"
   ERRNUM=$?
   if [ "$ERRNUM" != 0 ]; then
      log_stderr "Could not create ${OUTPUT_FILE}"
      exit 1
   fi
   log_stderr "Removing alias data ${ALIAS_TMP}"
   rm "${ALIAS_TMP}"
   ERRNUM=$?
   if [ "$ERRNUM" != 0 ]; then
      log_stderr "Could not remove ${ALIAS_TMP}"
      # this is not critical, we can continue
   fi
   log_stderr "Updating ownership of ${OUTPUT_FILE}"
   chmod 600 ${OUTPUT_FILE}
   ERRNUM=$?
   if [ "$ERRNUM" != 0 ]; then
      log_stderr "Could not change permission of ${OUTPUT_FILE}"
      exit 1
   fi
   chown ${VMWARE_POSTGRES_OS_ADMIN}:${VMWARE_POSTGRES_VMON_GROUP} ${OUTPUT_FILE}
   if [ "$ERRNUM" != 0 ]; then
      log_stderr "Could not change ownership of ${OUTPUT_FILE}"
      exit 1
   fi
}

# Request certificates from vecs via its client and update the certificates.
# Those have been created at firstboot with the correct permissions, so
# overwriting them is not an issue even with --output that truncates the
# old file before writing it. If this command fails, it is likely that
# the set of certificates is not available, hence rely on the next restart
# done on the PostgreSQL server to check if something unexpected is going
# on. Do not do any validity checks here for simplicity. Note that those
# commands will also fail on VCHA deployments as the service storing all
# the certificate-related data, vmware-vmafd is not up and running, but
# this does not matter much as all the needed data has already been copied
# using the initial VM cloning.
source /usr/sbin/bash-feature-state-wrapper.sh
if isFeatureDisabled VPG_CERT_REDESIGN ; then
   CERT_FILE=${VMWARE_POSTGRES_SSL_DATA}/server.crt
   KEY_FILE=${VMWARE_POSTGRES_SSL_DATA}/server.key
   log_stderr "Generating SSL key file ${KEY_FILE}"
   ${VMWARE_CIS_HOME}/vmware-vmafd/bin/vecs-cli entry getkey \
         --store MACHINE_SSL_CERT  --alias __MACHINE_CERT \
         --output ${KEY_FILE}
   ERRNUM=$?
   if [ "$ERRNUM" != 0 ]; then
      log_stderr "Could not generate SSL key file ${KEY_FILE}"
      exit 1
   fi

   log_stderr "Generating SSL certificate file ${CERT_FILE}"
   ${VMWARE_CIS_HOME}/vmware-vmafd/bin/vecs-cli entry getcert \
         --store MACHINE_SSL_CERT --alias __MACHINE_CERT \
         --output ${CERT_FILE}
   ERRNUM=$?
   if [ "$ERRNUM" != 0 ]; then
      log_stderr "Could not generate SSL certificate file ${CERT_FILE}"
      exit 1
   fi

   log_stderr "Updating ownership for SSL certificate file ${CERT_FILE} and key file ${KEY_FILE}"
   chmod 600 ${KEY_FILE} ${CERT_FILE}
   chown ${VMWARE_POSTGRES_OS_ADMIN}:${VMWARE_POSTGRES_VMON_GROUP} \
         ${KEY_FILE} ${CERT_FILE}
fi

# Request and save list of CA and CRL files to be loaded in Postgres
CA_FILE=${VMWARE_POSTGRES_SSL_DATA}/root_ca.pem
CRL_FILE=${VMWARE_POSTGRES_SSL_DATA}/root_crl.pem
print_full_store "TRUSTED_ROOTS" "${CA_FILE}"
print_full_store "TRUSTED_ROOT_CRLS" "${CRL_FILE}"

exit 0
