Skip to content

Commit

Permalink
improve create ca script (#161)
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-nhs authored Sep 14, 2023
1 parent f263c77 commit 3de4e27
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 32 deletions.
2 changes: 2 additions & 0 deletions privateCA/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ private
crl
config
certs
certs_backup_*
private_backup_*
12 changes: 9 additions & 3 deletions privateCA/README.md
Original file line number Diff line number Diff line change
@@ -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.
142 changes: 113 additions & 29 deletions privateCA/create_certs.sh
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -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"

Expand All @@ -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"
}
Expand All @@ -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"
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -104,21 +123,24 @@ 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"
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"

Expand Down Expand Up @@ -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

0 comments on commit 3de4e27

Please sign in to comment.