#!/usr/bin/bash

#
# Create an openssl CA for a MySQL replication cluster
# issue certificates for specified cluster members
# then install the certificates and configuration for
# this server
#


if [ $# -lt 1 ] && [ $# -ne 2 ]; then
	echo
	echo "Usage: $0 [--force] <Name> [<thishostname> [<otherhostname1>...<otherhostnameN>]]"
    echo 
	echo "This script will generate a CA plus the standard ceritifcates"
	echo "required for a mysql replication cluster"
	echo
	echo "If the optional <hostnameN> argument is specified then a"
	echo "certificate for <hostnameN> will be generated and signed"
	echo ""
	echo "NB: if the CA and standard certs for <Name> don't yet exist they"
	echo "will also be generated."
	echo
	exit 1
fi

# Currently this does nothing....
FORCE=
if [ "$1" == "--force" ]; then
	FORCE="$1"
	shift
fi

# Actually a name....
DOMAIN=$1

shift
ACCOUNTS="$*"

BASEDATE="2014-11-01 00:00:00"
BASEDIR=$HOME/mysql-cluster/X.509
CADIR=$BASEDIR/$DOMAIN
echo
echo " # Using domain $DOMAIN"
echo " # CA Directory is $CADIR"
echo

SECNUM=1
function section { echo -e "\n\\033[1;4m$SECNUM.  $1\\033[0m"; SECNUM=$[SECNUM+1]; };
function item    { echo -e "\n\\033[1m$1\\033[0m"; };

section "Configuring CA"

# If the directory doesn't exist yet we have to generate all the default stuff first
if [ ! -d $CADIR ]; then

	echo " - Creating $CADIR"
	mkdir -p $CADIR/{certs,crls,keys,csrs,keys+crts,db}
	mkdir -p $CADIR/certs/byserial

	echo " - Generating config ($CADIR/db/openssl.cnf)"
	cat <<-EOD >$CADIR/db/openssl.cnf
		[ ca ]
		default_ca      = CA_default            # The default ca section

		[ CA_default ]
		dir            = $CADIR                 # top dir
		database       = \$dir/db/index.txt     # index file.
		new_certs_dir  = \$dir/certs/byserial   # new certs dir

		certificate    = \$dir/certs/mysql-CA.crt     # The CA cert
		serial         = \$dir/db/serial              # serial no file
		private_key    = \$dir/keys/mysql-CA.key      # CA private key
		RANDFILE       = \$dir/db/.rand               # random number file

		default_days     = 5475                 # how long to certify for
		default_startdate = 20141101000000Z     # For repeatability, these are 15 years certs anyway
		default_crl_days = 30                   # how long before next CRL
		default_md       = sha256               # md to use

		policy         = policy_any            # default policy
		email_in_dn    = yes                   # Don't add the email into cert DN

		name_opt       = ca_default            # Subject name display option
		cert_opt       = ca_default            # Certificate display option
		copy_extensions = none                 # Don't copy extensions from request

		[ policy_any ]
		countryName            = supplied
		stateOrProvinceName    = optional
		organizationName       = optional
		organizationalUnitName = optional
		commonName             = supplied
		emailAddress           = optional
		
		[ req ]
		dir                    = $CADIR
		default_keyfile        = \$dir/keys/mysql-CA.key
		default_bits           = 4096
		default_md             = sha256
		x509_extensions        = v3_ca
		prompt                 = no
		distinguished_name     = root_ca_DN

		[ root_ca_DN ]
		commonName = $DOMAIN MySQL CA
		countryName = AU
		stateOrProvinceName = New South Wales
		localityName = Sydney
		0.organizationName = Noggin Pty Ltd
		emailAddress = sysadmin@noggin.com.au

		[ v3_ca ]
		# PKIX recommendation.
		subjectKeyIdentifier=hash
		authorityKeyIdentifier=keyid:always,issuer:always
		basicConstraints = CA:true
		
		[ v3_usr ]
		subjectKeyIdentifier=hash
		basicConstraints = critical,CA:false
		keyUsage = critical,digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment


	EOD

	echo '01' >$CADIR/db/serial
	touch $CADIR/db/index.txt

	section "Generating CA Cert"

	# Generate the CA private key 
	ng-mk-rsa-key "mysql::${DOMAIN}:CA" 4096 >$CADIR/keys/mysql-CA.key

	# Now generate the CA's root certificate (20 year expiry)
	env TZ=UTC faketime -f "${BASEDATE}" openssl req -new -x509 -utf8 -batch -config $CADIR/db/openssl.cnf \
		-key $CADIR/keys/mysql-CA.key \
		-days $[25*365]  -outform PEM -out $CADIR/certs/mysql-CA.crt \
		2>&1 | sed -ure 's/^/   /g'
  
else 
	echo "   CA has already been initialised, skipping"
fi

# Now generate the CSRs
section "Generating CSRs"
for ACCOUNT in $ACCOUNTS; do
  FN="mysql-${ACCOUNT//.*}"
  item " * $FN ($ACCOUNT)"
  # Skip existing hosts
  if [ -f $CADIR/certs/$FN.crt ]; then
    echo "  Certificate for $ACCOUNT already exists, skipping"
    continue
  fi

  # Generate the account private key
  ng-mk-rsa-key "mysql::${DOMAIN}:${ACCOUNT}" 2048 >$CADIR/keys/$FN.key.pkcs8

  openssl req -new -utf8 -batch \
    -key $CADIR/keys/$FN.key.pkcs8 \
    -subj "/C=AU/ST=New South Wales/O=Noggin Pty Ltd/CN=$ACCOUNT" \
    -outform PEM -out $CADIR/csrs/$FN.csr 2>&1 | sed -ure 's/^/   /g'

  # MySQL (with yaSSL) Doesn't like PKCS#8 it needs PKCS#1
  # Just changing the head and footer per the below link will NOT work
  #   -- http://forums.mysql.com/read.php?11,400856,401127#msg-401127
  # see http://bugs.mysql.com/bug.php?id=64870 (comment [26 Mar 3:58] noah williamsson)
  openssl rsa -in $CADIR/keys/$FN.key.pkcs8 -out  $CADIR/keys/$FN.key

done

# And sign them
section "Signing CSRs"
for ACCOUNT in $ACCOUNTS; do
  FN="mysql-${ACCOUNT//.*}"
  item " * $FN ($ACCOUNT)"
  # Skip existing hosts
  if [ -f $CADIR/certs/$FN.crt ]; then
    echo "  Certificate for $ACCOUNT already exists, skipping"
    continue
  fi
  env TZ=UTC faketime -f "${BASEDATE}" openssl ca -batch -utf8 -config $CADIR/db/openssl.cnf \
   -in  $CADIR/csrs/$FN.csr \
   -out $CADIR/certs/$FN.crt -notext 2>&1  | sed -ure 's/^/   /g'
done

section "Creating PEM KEY+CRT bundles"
for i in $CADIR/certs/*.crt; do
  NAME=$(basename $i)
  NAME=${NAME%%.crt}
  item " * $NAME"
  echo "   cat $CADIR/keys/$NAME.key $i >$CADIR/pems/$NAME.pem"
  cat $CADIR/keys/$NAME.key $i >$CADIR/keys+crts/$NAME.pem
done


