diff --git a/privateCA/.gitignore b/privateCA/.gitignore index 20ecf1b6a..7e452ae9d 100644 --- a/privateCA/.gitignore +++ b/privateCA/.gitignore @@ -2,3 +2,5 @@ private crl config certs +certs_backup_* +private_backup_* diff --git a/privateCA/README.md b/privateCA/README.md index fd5684d63..9c06a02a7 100644 --- a/privateCA/README.md +++ b/privateCA/README.md @@ -1,8 +1,14 @@ This folder contains scripts to initialize the private CA used for mutual TLS in each environment. -The script ./create_certs.sh creates new self signed CA certificate and a client certificate. +The script ./create_certs.sh creates new self signed CA certificate and a client certificate. These are valid for 1 year. The private keys and certificates for these are stored in AWS secrets manager. -The public CA cert is uploaded to s3://${TRUSTSTORE_BUCKET_NAME}/truststore.pem where it used by API gateway as a truststore to indicate what certificates should be trusted for mutual TLS. +The existing truststore file is downloaded from S3 and the new CA certificate is appended to the end of the existing one before uploading to enable old certificates to still to be used. +The public CA cert is uploaded to s3://TRUSTSTORE_BUCKET_NAME/truststore.pem and s3://TRUSTSTORE_BUCKET_NAME/sandbox-truststore.pem where it used by API gateway as a truststore to indicate what certificates should be trusted for mutual TLS. Before running the script, you should set environment variable AWS_PROFILE to indicate what environment this is being run for. -You should pass the environment name as a param when calling the script +You should pass the environment name using the -e flag when calling the script. +By default the script does not upload new secrets or truststore files. If you want to do this, you must pass flag `-d false`. + +Existing certificates and keys are backed up locally before new ones are uploaded. + +To get API gateway to use the new truststore, you must do a redeploy of the cloud formation stack using github action. diff --git a/privateCA/create_certs.sh b/privateCA/create_certs.sh index 1b11e5b58..74f94c1a8 100755 --- a/privateCA/create_certs.sh +++ b/privateCA/create_certs.sh @@ -1,8 +1,32 @@ #!/usr/bin/env bash +while getopts e:d: flag +do + case "$flag" in + e) environment=${OPTARG};; + d) dry_run_param=${OPTARG};; + *) echo "usage: $0 [-e] environment [-d] dry run" >&2 + exit 1 ;; + esac +done + +if [ "$dry_run_param" = "false" ]; then + DRY_RUN=false +else + DRY_RUN=true +fi + +if [ "$environment" = "" ]; then + echo "You must pass in an environment name using the e flag" + exit 1 +fi + readonly BASE_DIR=$(pwd) readonly CERTS_DIR="${BASE_DIR}/certs" readonly KEYS_DIR="${BASE_DIR}/private" +readonly DATESTAMP=$(date +%Y%m%d_%H%M%S) +readonly BACKUP_CERTS_DIR="${BASE_DIR}/certs_backup_${DATESTAMP}" +readonly BACKUP_KEYS_DIR="${BASE_DIR}/private_backup_${DATESTAMP}" readonly CRL_DIR="${BASE_DIR}/crl" readonly CONFIG_DIR="${BASE_DIR}/config" @@ -14,7 +38,7 @@ readonly CERT_VALIDITY_DAYS="365" readonly CA_NAME="ca" readonly CA_CERTIFICATE_SUBJECT="/C=GB/ST=Leeds/L=Leeds/O=nhs/OU=prescriptions for patients private CA/CN=prescriptions for patients Private CA $(date +%Y%m%d_%H%M%S)" -readonly CERT_PREFIX="$1-" +readonly CERT_PREFIX="${environment}-" readonly CERT_PREFIX_CI="ci" readonly CERT_PREFIX_SANDBOX="sandbox" @@ -23,11 +47,6 @@ readonly CLIENT_CERT_SUBJECT_PREFIX="/C=GB/ST=Leeds/L=Leeds/O=nhs/OU=prescriptio # v3 extensions readonly V3_EXT="$BASE_DIR/v3.ext" -function get_timestamp { - echo $(date +%Y%m%d_%H%M%S) -} - - function generate_crl { openssl ca -config openssl-ca.conf -gencrl -out "$CRL_DIR/$CA_NAME.crl" } @@ -41,14 +60,14 @@ function convert_cert_to_der { function generate_key { local readonly key_name="$1" echo "@ Generating key '$key_name'..." - openssl genrsa -out "$KEYS_DIR/$key_name.pem" 2048 + openssl genrsa -out "$KEYS_DIR/$key_name.key" 2048 } function generate_ca_cert { local readonly key_name="$1" echo "@ Generating CA certificate..." openssl req -new -x509 -days "$CERT_VALIDITY_DAYS" -config "$BASE_DIR/$CA_CERT_SIGNING_CONFIG" \ - -key "$KEYS_DIR/$key_name.pem" \ + -key "$KEYS_DIR/$key_name.key" \ -out "$CERTS_DIR/$key_name.pem" -outform PEM -subj "$CA_CERTIFICATE_SUBJECT" convert_cert_to_der "$key_name" @@ -62,14 +81,14 @@ function create_csr { then echo "@ Creating CSR for '$key_name'..." openssl req -config "$BASE_DIR/$SMARTCARD_CERT_SIGNING_CONFIG" -new \ - -key "$KEYS_DIR/$key_name.pem" \ + -key "$KEYS_DIR/$key_name.key" \ -out "$CERTS_DIR/$key_name.csr" -outform PEM \ -subj "${CLIENT_CERT_SUBJECT_PREFIX}${CERT_PREFIX}${CERT_PREFIX_CI}${client_description}" elif [ "$key_name" = "apigee_client_cert_sandbox" ] then echo "@ Creating CSR for '$key_name'..." openssl req -config "$BASE_DIR/$SMARTCARD_CERT_SIGNING_CONFIG" -new \ - -key "$KEYS_DIR/$key_name.pem" \ + -key "$KEYS_DIR/$key_name.key" \ -out "$CERTS_DIR/$key_name.csr" -outform PEM \ -subj "${CLIENT_CERT_SUBJECT_PREFIX}${CERT_PREFIX}${CERT_PREFIX_SANDBOX}${client_description}" fi @@ -80,7 +99,7 @@ function sign_csr_with_ca { echo "@ Using CSR to generate signed cert for '$key_name'..." openssl ca -batch \ -config "$BASE_DIR/$CA_CERT_SIGNING_CONFIG" -policy signing_policy -extensions signing_req \ - -keyfile "$KEYS_DIR/$CA_NAME.pem" -cert "$CERTS_DIR/$CA_NAME.pem" \ + -keyfile "$KEYS_DIR/$CA_NAME.key" -cert "$CERTS_DIR/$CA_NAME.pem" \ -days "$CERT_VALIDITY_DAYS" -out "$CERTS_DIR/$key_name.pem" -in "$CERTS_DIR/$key_name.csr" \ -notext # don't output the text form of a certificate to the output file } @@ -104,13 +123,16 @@ function generate_client_cert { convert_cert_to_der "$name" } +echo "Going to create mutual TLS certs with these details" echo "AWS_PROFILE: ${AWS_PROFILE}" echo "CERT_PREFIX ${CERT_PREFIX}" -read -p "Press any key to resume ..." +echo "DRY_RUN ${DRY_RUN}" +read -p "Press any key to resume or press ctrl+c to exit ..." # Recreate output dirs rm -rf "$CERTS_DIR" "$KEYS_DIR" "$CRL_DIR" "$CONFIG_DIR" -mkdir "$CERTS_DIR" "$KEYS_DIR" "$CRL_DIR" "$CONFIG_DIR" +mkdir "$CERTS_DIR" "$KEYS_DIR" "$CRL_DIR" "$CONFIG_DIR" "$BACKUP_CERTS_DIR" "$BACKUP_KEYS_DIR" + # Create database and serial files touch "$CONFIG_DIR/index.txt" @@ -118,7 +140,7 @@ echo '1000' > "$CONFIG_DIR/crlnumber.txt" echo '01' > "$CONFIG_DIR/serial.txt" # Generate CA key and self-signed cert -echo "@ Generating CA credentials..." +echo "Generating CA credentials..." generate_key "$CA_NAME" generate_ca_cert "$CA_NAME" @@ -148,26 +170,88 @@ TRUSTSTORE_BUCKET_ARN=$(aws cloudformation describe-stacks \ --query 'Stacks[0].Outputs[?OutputKey==`TrustStoreBucket`].OutputValue' --output text) TRUSTSTORE_BUCKET_NAME=$(echo ${TRUSTSTORE_BUCKET_ARN} | cut -d ":" -f 6) -aws secretsmanager put-secret-value \ +echo "Backing up existing secrets to local file" + +aws secretsmanager get-secret-value \ --secret-id ${CA_KEY_ARN} \ - --secret-string file://${KEYS_DIR}/${CA_NAME}.pem -aws secretsmanager put-secret-value \ + --query SecretString \ + --output text > ${BACKUP_KEYS_DIR}/${CA_NAME}.key + +aws secretsmanager get-secret-value \ --secret-id ${CA_CERT_ARN} \ - --secret-string file://${CERTS_DIR}/${CA_NAME}.pem + --query SecretString \ + --output text > ${BACKUP_CERTS_DIR}/${CA_NAME}.pem -aws secretsmanager put-secret-value \ +aws secretsmanager get-secret-value \ --secret-id ${CLIENT_KEY_ARN} \ - --secret-string file://${KEYS_DIR}/apigee_client_cert.pem -aws secretsmanager put-secret-value \ + --query SecretString \ + --output text > ${BACKUP_KEYS_DIR}/apigee_client_cert.key + +aws secretsmanager get-secret-value \ --secret-id ${CLIENT_CERT_ARN} \ - --secret-string file://${CERTS_DIR}/apigee_client_cert.pem + --query SecretString \ + --output text > ${BACKUP_CERTS_DIR}/apigee_client_cert.pem -aws secretsmanager put-secret-value \ +aws secretsmanager get-secret-value \ --secret-id ${CLIENT_SANDBOX_KEY_ARN} \ - --secret-string file://${KEYS_DIR}/apigee_client_cert_sandbox.pem -aws secretsmanager put-secret-value \ + --query SecretString \ + --output text > ${BACKUP_KEYS_DIR}/apigee_client_cert_sandbox.key +aws secretsmanager get-secret-value \ --secret-id ${CLIENT_SANDBOX_CERT_ARN} \ - --secret-string file://${CERTS_DIR}/apigee_client_cert_sandbox.pem - -aws s3 cp ${CERTS_DIR}/${CA_NAME}.pem s3://${TRUSTSTORE_BUCKET_NAME}/truststore.pem -aws s3 cp ${CERTS_DIR}/${CA_NAME}.pem s3://${TRUSTSTORE_BUCKET_NAME}/sandbox-truststore.pem + --query SecretString \ + --output text > ${BACKUP_CERTS_DIR}/apigee_client_cert_sandbox.pem + +echo "Creating new combined truststore files for upload" + +aws s3api head-object --bucket ${TRUSTSTORE_BUCKET_NAME} --key truststore.pem || NOT_EXIST=true +if [ $NOT_EXIST ]; then + echo "" > ${BACKUP_CERTS_DIR}/s3_truststore.pem +else + aws s3 cp s3://${TRUSTSTORE_BUCKET_NAME}/truststore.pem ${BACKUP_CERTS_DIR}/s3_truststore.pem +fi + +aws s3api head-object --bucket ${TRUSTSTORE_BUCKET_NAME} --key sandbox-truststore.pem || NOT_EXIST=true +if [ $NOT_EXIST ]; then + echo "" > ${BACKUP_CERTS_DIR}/s3_sandbox_truststore.pem +else + aws s3 cp s3://${TRUSTSTORE_BUCKET_NAME}/sandbox-truststore.pem ${BACKUP_CERTS_DIR}/s3_sandbox_truststore.pem +fi + + +cat ${BACKUP_CERTS_DIR}/s3_truststore.pem ${CERTS_DIR}/${CA_NAME}.pem > ${CERTS_DIR}/truststore.pem +cat ${BACKUP_CERTS_DIR}/s3_sandbox_truststore.pem ${CERTS_DIR}/${CA_NAME}.pem > ${CERTS_DIR}/sandbox_truststore.pem + + +if [ "$DRY_RUN" = "false" ]; then + echo "Setting new keys in secrets manager" + read -p "Press any key to resume or press ctrl+c to exit ..." + aws secretsmanager put-secret-value \ + --secret-id ${CA_KEY_ARN} \ + --secret-string file://${KEYS_DIR}/${CA_NAME}.key + aws secretsmanager put-secret-value \ + --secret-id ${CA_CERT_ARN} \ + --secret-string file://${CERTS_DIR}/${CA_NAME}.pem + + aws secretsmanager put-secret-value \ + --secret-id ${CLIENT_KEY_ARN} \ + --secret-string file://${KEYS_DIR}/apigee_client_cert.key + aws secretsmanager put-secret-value \ + --secret-id ${CLIENT_CERT_ARN} \ + --secret-string file://${CERTS_DIR}/apigee_client_cert.pem + + aws secretsmanager put-secret-value \ + --secret-id ${CLIENT_SANDBOX_KEY_ARN} \ + --secret-string file://${KEYS_DIR}/apigee_client_cert_sandbox.key + aws secretsmanager put-secret-value \ + --secret-id ${CLIENT_SANDBOX_CERT_ARN} \ + --secret-string file://${CERTS_DIR}/apigee_client_cert_sandbox.pem + + echo "Going to create new truststore files on S3" + read -p "Press any key to resume or press ctrl+c to exit ..." + + aws s3 cp ${CERTS_DIR}/truststore.pem s3://${TRUSTSTORE_BUCKET_NAME}/truststore.pem + aws s3 cp ${CERTS_DIR}/sandbox_truststore.pem s3://${TRUSTSTORE_BUCKET_NAME}/sandbox-truststore.pem + +else + echo "Not setting new secrets or upleading truststore files as dry run set to true" +fi