#! /bin/bash
#
set -e
set -u

export PATH="/opt/puppet/bin:$PATH"

# Steps
# * Stop the master
# * Remove the existing files related to $certname.  If we don't clean up
#   first, we'll get the old certificate which does not match the private key and
#   is therefore useless.
# * Issue a new certificate for $certname using the new CA.  Agents were
#   configured to trust only the new CA in step 4.  This new certificate allows
#   the agents to obtain a catalog again.
# * Replace the trusted CA bundle (both CA certs) with a file containing only the
#   new CA cert.  This means only agents who possess a cert issued by the new ca will
#   be authenticated.
# * Start the Master

timestamp="$(ruby -e 'puts Time.now.to_i')"

module="cve20113872"
apachevhost="/etc/puppetlabs/httpd/conf.d/puppetmaster.conf"
vardir="$(puppet master --configprint vardir)"
ssldir="$(puppet master --configprint ssldir)"
certname="$(puppet master --configprint certname)"
manifest="$(puppet master --configprint manifest)"

# This is some shell magic to read a file into a variable if the
# variable isn't already set.
: ${DNS_NAME:=$(cat "${vardir}/${module}/dns_name")}
if [[ -z "${DNS_NAME}" ]]; then
  echo "Error: Could not determine the intermediate dns name from step 1." >&2
  echo "Did you run step 1 first?" >&2
  exit 1
fi
: ${DNS_ALT_NAMES:=$(cat "${vardir}/${module}/alt_names")}
if [[ -z "${DNS_ALT_NAMES}" ]]; then
  echo "Error: Could not determine the certdnsnames from step 1." >&2
  echo "Did you run step 1 first?" >&2
  echo "If you do not want any alternate names in your new master certificate" >&2
  echo "simply touch ${vardir}/${module}/alt_names (Create an empty file)" >&2
  exit 1
fi

# Figure out if we've been patched or not.
if puppet master --configprint dns_alt_names &>/dev/null; then
  HAVE_PATCHED='true'
  alt_names_option="dns_alt_names"
  alt_names_separator=","
else
  HAVE_PATCHED='false'
  alt_names_option="certdnsnames"
  alt_names_separator=":"
fi

# This is disabled at this point in puppet.conf, so we have to read it from what
# we wrote out in step1
alt_names_value="${DNS_ALT_NAMES}"
intermediate_name="${DNS_NAME}"

echo -n "Stopping Puppet Master..." >&2
puppet resource service pe-httpd ensure=stopped hasstatus=true &> /dev/null
echo "done." >&2

echo -n "Issuing new certificate for ${certname} ..." >&2
for d in certs private_keys public_keys ca/signed; do
  mv "${ssldir}/${d}/${certname}.pem" "${ssldir}/${d}/${certname}.pem.previous"
done
# Now issue the new certificate
puppet cert --generate "--${alt_names_option}" "${alt_names_value}" "${certname}" >/dev/null
echo "done." >&2

# Replace the certificate bundles which authenticate the previous ca with a
# "bundle" of one certificate containing only the new ca.  This will mean the
# master will no longer authenticate agents who possess certificates issued by
# the previous ca.
cp -p "${ssldir}/ca/ca_crt.pem" "${ssldir}/certs/ca.pem"
cp -p "${ssldir}/ca/ca_crl.pem" "${ssldir}/crl.pem"

## Dashboard Certificate ##
#
# This section should only run if the Puppet Dashboard is installed on the same
# host as the Puppet Master.  It will issue a new CSR for the dashboard and
# sign it.  If the dashboard is not on the same host as the Puppet CA, then the
# normal rake task should be used to re-issue the dashboard certificate.
#
# To manually do this process (e.g. if you have Dashboard on a different host.)
#
#     # Clean out the existing scripts.
#     find . -name '*.pem' -print0 | xargs -0 rm
#     # Generate a new CSR
#     /opt/puppet/bin/rake cert:create_key_pair
#     /opt/puppet/bin/rake cert:request
#     # Sign the certificate request on the master.
#     puppet cert sign dashboard
#     # Retrieve the signed certificate
#     /opt/puppet/bin/rake cert:retrieve
#     # Fix permissions (If you ran this command as root)
#     find . -name '*.pem' -print0 | xargs -0 chown puppet-dashboard:puppet-dashboard

: ${DASHBOARD_ROOT:="/opt/puppet/share/puppet-dashboard"}
if [[ -d "${DASHBOARD_ROOT}/certs" ]]; then
  for filetype in cert private_key ca_crl public_key ca_cert; do
    pemfile="${DASHBOARD_ROOT}/certs/dashboard.${filetype}.pem"
    if [[ -f "${pemfile}" ]]; then
      cp -p "${pemfile}" "${pemfile}.previous"
    fi
  done
  # Now issue the new certificate
  echo -n "Issuing new certificate for dashboard..." >&2
  puppet cert --generate dashboard &>/dev/null
  # Move the private key into place.  I use a redirection to preserve the owner and permissions
  # of the original files.
  cat "${ssldir}/certs/dashboard.pem"        > "${DASHBOARD_ROOT}/certs/dashboard.cert.pem"
  cat "${ssldir}/private_keys/dashboard.pem" > "${DASHBOARD_ROOT}/certs/dashboard.private_key.pem"
  cat "${ssldir}/public_keys/dashboard.pem"  > "${DASHBOARD_ROOT}/certs/dashboard.public_key.pem"
  # Clean up the generated dashboard cert
  rm  "${ssldir}/certs/dashboard.pem" \
      "${ssldir}/private_keys/dashboard.pem" \
      "${ssldir}/public_keys/dashboard.pem"
  # This is the new certificate only, not the bundle.
  cat "${ssldir}/certs/ca.pem" > "${DASHBOARD_ROOT}/certs/dashboard.ca_cert.pem"
  cat "${ssldir}/crl.pem" > "${DASHBOARD_ROOT}/certs/dashboard.ca_crl.pem"
  echo "done." >&2
fi
## End Dashboard Certificate ##

## Restore site.pp to the original state when we started this process ##
cp -p "${manifest}.original" "${manifest}"
rm "${manifest}.original"

echo -n "Starting Puppet Master..." >&2
puppet resource service pe-httpd ensure=running hasstatus=true &> /dev/null
echo "done." >&2

cat <<EOMESSAGE

The puppet master has been issued an SSL certificate by the new CA.  The
migration to the new CA is now complete.  Puppet agents which have been
migrated should now be able to reconnect to this master.

The manifest ${manifest} has been restored to its
original state.  The cve20113872::step2 and cve20113872::step4 classes will no
longer be included in node catalogs.

EOMESSAGE
