#!/usr/local/bin/bash

# This function wraps sed:
# * Makes sure its atomic by writing to a temp file and moving it _after_
# * Escapes any forward slashes and ampersands on the RHS for you
function sedeasy {
  tmp=$3.tmp.`date +%s`
  sed "s/$1/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3 > $tmp
  mv $tmp $3
}

# Display usage information
function usage {
  echo "Usage: $0 [-if]"
  echo "Configuration helper for enabling SSL for PuppetDB."
  echo
  echo "This tool will attempt to create the necessary JKS trust & keystore for" \
    "PuppetDB SSL to operate from the local Puppet agents certificate, key and"\
    "CA certificate. It also is able to update the necessary PuppetDB"\
    "configuration files if necessary to point to the location of these" \
    "stores, configure the correct passwords and also configures the host and" \
    "port for SSL to listen on."
  echo
  echo "Options:"
  echo " -i  Interactive mode"
  echo " -f  Force configuration file update. By default if the configuration"\
    "already exists in your jetty.ini, you must use this option to override it"
  echo " -h  Help"
  exit 0
}

while getopts "ifh" opt;
do
    case $opt in
        i)
            interactive=true ;;
        f)
            force=true ;;
        h)
            usage ;;
        *)
            usage ;;
    esac
done

${interactive:=false}
${force:=false}

if $interactive
then
    dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
    cd $dir
    answers_file="puppetdb-ssl-setup-answers.txt"
    if [ -f "$answers_file" ]
    then
        echo "Reading answers file '$answers_file'"
        . $answers_file
    fi

    vars=( agent_confdir agent_vardir puppetdb_confdir )
    prompts=( "Puppet Agent confdir" "Puppet Agent vardir" "PuppetDB confdir" )

    for (( i=0; i<${#vars[@]}; i++ ))
    do
        read -p "${prompts[$i]} [${!vars[$i]}]: " input
        export ${vars[$i]}=${input:-${!vars[$i]}}
    done

    cat /dev/null > $answers_file
    for (( i=0; i<${#vars[@]}; i++ ))
    do
        echo "${vars[$i]}=${!vars[$i]}" >> $answers_file
    done
else
    # This should be run on the host with PuppetDB
    PATH=/opt/puppet/bin:$PATH
    agent_confdir=`puppet agent --configprint confdir`
    agent_vardir=`puppet agent --configprint vardir`

    if [ -d "/etc/puppetlabs/puppetdb" ] ; then
      puppetdb_confdir="/etc/puppetlabs/puppetdb"
      user=pe-puppetdb
    else
      puppetdb_confdir="/etc/puppetdb"
      user=_puppetdb
    fi
fi

set -e

fqdn=`facter fqdn`
# use hostname if fqdn is not available
if [ ! -n "$fqdn" ] ; then
  fqdn=`facter hostname`
fi

mycertname=`puppet master --confdir=$agent_confdir --vardir=$agent_vardir --configprint  certname`
mycert=`puppet master --confdir=$agent_confdir --vardir=$agent_vardir --configprint  hostcert`
myca=`puppet master --confdir=$agent_confdir --vardir=$agent_vardir --configprint localcacert`
privkey=`puppet master --confdir=$agent_confdir --vardir=$agent_vardir --configprint hostprivkey`

pw_file=${puppetdb_confdir}/ssl/puppetdb_keystore_pw.txt

if [ -f $pw_file ]; then
  password=`cat $pw_file`
else
  password=`export LC_ALL=C; dd if=/dev/urandom count=20 2> /dev/null | tr -cd '[:alnum:]' | cut -c 1-25`
  tmpdir=`mktemp -t -d tmp.puppetdbXXXXXX`
  rm -rf $tmpdir
  mkdir -p $tmpdir
  cp $myca $tmpdir/ca.pem
  cp $privkey $tmpdir/privkey.pem
  cp $mycert $tmpdir/pubkey.pem

  cd $tmpdir
  keytool -import -alias "PuppetDB CA" -keystore truststore.jks -storepass "$password" -trustcacerts -file ca.pem -noprompt
  cat privkey.pem pubkey.pem > temp.pem
  echo "$password" | openssl pkcs12 -export -in temp.pem -out puppetdb.p12 -name $fqdn -passout fd:0
  keytool -importkeystore -destkeystore keystore.jks -srckeystore puppetdb.p12 -srcstoretype PKCS12 -alias $fqdn -deststorepass "$password" -srcstorepass "$password"

  rm -rf $puppetdb_confdir/ssl
  mkdir -p $puppetdb_confdir/ssl
  cp -pr *jks $puppetdb_confdir/ssl
  echo $password > ${puppetdb_confdir}/ssl/puppetdb_keystore_pw.txt
fi

jettyfile="${puppetdb_confdir}/conf.d/jetty.ini"
if [ -f "$jettyfile" ] ; then
  jettyfile_bak="${jettyfile}.bak.`date +%s`"
  cp -p $jettyfile $jettyfile_bak
  echo "Backing up ${jettyfile} to ${jettyfile_bak} before making changes"

  settings=('ssl-host' 'ssl-port' 'key-password' 'trust-password' 'keystore' 'truststore')
  values=($fqdn 8081 $password $password "${puppetdb_confdir}/ssl/keystore.jks" "${puppetdb_confdir}/ssl/truststore.jks")

  for (( i=0; i<${#settings[@]}; i++ )); do
    if grep -qe "^${settings[$i]}" ${jettyfile}; then
      if grep -qe "^${settings[$i]} = ${values[$i]}$" ${jettyfile}; then
        echo "Setting ${settings[$i]} in ${jettyfile} already correct."
      else
        if $force; then
          sedeasy "^${settings[$i]}.*" "${settings[$i]} = ${values[$i]}" ${jettyfile}
          echo "Updated setting ${settings[$i]} in ${jettyfile}."
        else
          echo "Setting ${settings[$i]} in ${jettyfile} doesn't seem to be correct. This can be remedied with $0 -f."
        fi
      fi
    else
      if grep -qE "^# ${settings[$i]} = <[A-Z]+>$" ${jettyfile}; then
        sedeasy "^# ${settings[$i]}.*" "${settings[$i]} = ${values[$i]}" ${jettyfile}
        echo "Updated default settings from package installation for ${settings[$i]} in ${jettyfile}."
      else
        if $force; then
          cat ${jettyfile} > ${jettyfile}.tmp
          echo "${settings[$i]} = ${values[$i]}" >> ${jettyfile}.tmp
          mv ${jettyfile}.tmp ${jettyfile}
          echo "Added setting ${settings[$i]} to ${jettyfile}."
        else
          echo "$jettyfile exists, but could not find active ${settings[$i]} setting. Include that setting yourself manually. Or force with $0 -f."
        fi
      fi
    fi
  done
else
  echo "Unable to find PuppetDB Jetty configuration at ${jettyfile}. Unable to provide automatic configuration for that file."
fi

chmod 600 ${puppetdb_confdir}/ssl/*
chmod 700 ${puppetdb_confdir}/ssl
chown -R ${user}:${user} ${puppetdb_confdir}/ssl
rm -rf $tmpdir

if $interactive
then
    echo "Certificate generation complete.  You will need to make sure that the puppetdb.conf"
    echo " file on your puppet master looks like this:"
    echo "    [main]"
    echo "    server = ${mycertname}"
    echo "    port   = 8081"
    echo
    echo " And that the config.ini (or other .ini) on your puppetdb system contains the"
    echo "  following:"
    echo
    echo "    [jetty]"
    echo "    #host           = localhost"
    echo "    port           = 8080"
    echo "    ssl-host       = ${fqdn}"
    echo "    ssl-port       = 8081"
    echo "    keystore       = ${puppetdb_confdir}/ssl/keystore.jks"
    echo "    truststore     = ${puppetdb_confdir}/ssl/truststore.jks"
    echo "    key-password   = ${password}"
    echo "    trust-password = ${password}"
fi
