Skip to content

Commit 2f6ce4c

Browse files
committed
[NRL-1700] Add rotation tools to truststore.sh
1 parent 33cb256 commit 2f6ce4c

File tree

2 files changed

+190
-26
lines changed

2 files changed

+190
-26
lines changed

scripts/truststore.sh

Lines changed: 190 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
#!/bin/bash
22
# Script to manage the NRLF truststore files
3+
#
4+
# Note that all creative and destructive operations are local only apart from
5+
# "push-all". Files are not uploaded to or deleted from S3 automatically.
6+
#
7+
# Once changes have been made locally and have been tested thoroughly, the files
8+
# in the truststore directories can be uploaded to S3 using the appropriate AWS
9+
# CLI commands or the "push-all" command below.
10+
#
311
set -o errexit -o pipefail -o nounset
412

513
BUCKET="nhsd-nrlf--truststore"
@@ -14,8 +22,14 @@ function _truststore_help() {
1422
echo " build-cert <name> <ca> <fqdn> - Build a single client cert + private key"
1523
echo " build-all - Build the standard trust store certs"
1624
echo " pull-ca <ca> - Pull the certificate authority"
25+
echo " pull-ca-key <ca> - Pull the certificate authority private key"
1726
echo " pull-client <env> - pull the files needed for a client connection"
1827
echo " pull-server <env> - pull the files needed for a server connection"
28+
echo " rotate-ca <env> - rotate the certificate authority, archiving the previous one"
29+
echo " rotate-client <env> - rotate the client certificate, archiving the previous one"
30+
echo " disable-archived-ca <env> - disable an archived certificate authority"
31+
echo " restore-archived-ca <env> - restore an archived certificate authority"
32+
echo " restore-archived-client <env> - restore an archived client certificate"
1933
echo
2034
}
2135

@@ -40,80 +54,175 @@ function _truststore_build_ca() {
4054
exit 1
4155
fi
4256

43-
ca=$1
57+
env=$1
4458
fqdn=$2
4559

4660
substitute_env_in_file ./truststore/config/ca.conf /tmp/ca.conf
4761

48-
echo -e "🚧 Building CA: $ca \t(FQDN: $fqdn)"
62+
echo -e "🚧 Building CA: $env \t(FQDN: $fqdn)"
4963

5064
openssl req -newkey rsa:4096 \
5165
-nodes \
52-
-keyout ./truststore/ca/$ca.key \
66+
-keyout ./truststore/ca/$env.key \
5367
-new \
5468
-x509 \
5569
-days 36500 \
56-
-out ./truststore/ca/$ca.crt \
70+
-out ./truststore/ca/$env.crt \
5771
-config /tmp/ca.conf \
5872
-extensions v3_req \
5973
-extensions v3_ca &> /dev/null
6074

6175
rm /tmp/ca.conf
62-
echo -e "✅ Successfully Built CA: $ca"
76+
echo -e "✅ Successfully Built CA: $env (FQDN: $fqdn)"
77+
78+
cat ./truststore/ca/$env.crt > ./truststore/server/$env.pem
79+
echo -e "✅ Successfully Built Server Truststore: $env"
6380
}
6481

65-
# buld a certificate
82+
# build a certificate
6683
function _truststore_build_cert() {
6784
if [ $# -ne 3 ]; then
6885
echo "Usage: $0 build-cert <name> <ca> <fqdn>"
6986
exit 1;
7087
fi
7188

72-
client=$1
73-
ca=$2
89+
cert_name=$1
90+
ca_name=$2
7491
fqdn=$3
7592
serial=$(date +%s%3N)
7693

7794
substitute_env_in_file ./truststore/config/client.conf /tmp/client.conf
7895

79-
echo -e "🚧 Generating $client keypair"
96+
echo -e "🚧 Generating $cert_name keypair"
8097

8198
openssl req \
8299
-newkey rsa:4096 \
83100
-nodes \
84-
-keyout truststore/client/$client.key \
101+
-keyout truststore/client/$cert_name.key \
85102
-new \
86-
-out truststore/client/$client.csr \
103+
-out truststore/client/$cert_name.csr \
87104
-config /tmp/client.conf \
88105
-extensions v3_req \
89106
-extensions usr_cert &> /dev/null
90107

108+
echo -e "🚧 Signing $cert_name certificate with ca=$ca_name (serial: $serial)"
109+
91110
openssl x509 \
92111
-req \
93-
-in truststore/client/$client.csr \
94-
-CA truststore/ca/$ca.crt \
95-
-CAkey truststore/ca/$ca.key \
112+
-in truststore/client/$cert_name.csr \
113+
-CA truststore/ca/$ca_name.crt \
114+
-CAkey truststore/ca/$ca_name.key \
96115
-set_serial $serial \
97-
-out truststore/client/$client.crt \
116+
-out truststore/client/$cert_name.crt \
98117
-days 36500 \
99118
-sha256 \
100119
-extfile /tmp/client.conf \
101120
-extensions v3_req \
102121
-extensions usr_cert &> /dev/null
103122

104-
cat truststore/client/$client.crt truststore/ca/$ca.crt > truststore/server/$client.pem
105-
106-
echo -e "✅ Successfully generated $client keypair"
123+
echo -e "✅ Successfully generated $cert_name keypair for ca=$ca_name (FQDN: $fqdn)"
107124
rm /tmp/client.conf
125+
rm truststore/client/$cert_name.csr
126+
}
127+
128+
# Rotate a CA - archive the existing CA and build a new one
129+
# The previous CA is added to the new CA bundle (can be removed after clients have updated)
130+
function _truststore_rotate_ca() {
131+
if [ $# -ne 2 ]; then
132+
echo "Usage: $0 rotate-ca <env> <fqdn>"
133+
exit 1;
134+
fi
135+
136+
env=$1
137+
fqdn=$2
138+
139+
# Archive the existing ca certs
140+
archive_date=$(date +%Y-%m-%d)
141+
if [ -f "truststore/ca/$env.archived_$archive_date.crt" ] ||
142+
[ -f "truststore/ca/$env.archived_$archive_date.key" ] ||
143+
[ -f "truststore/server/$env.archived_$archive_date.pem" ]; then
144+
echo "Error: Archive files already exist for date $archive_date - please resolve before rotating the CA"
145+
exit 1
146+
fi
147+
148+
mv truststore/ca/$env.crt truststore/ca/$env.archived_$archive_date.crt
149+
mv truststore/ca/$env.key truststore/ca/$env.archived_$archive_date.key
150+
mv truststore/server/$env.pem truststore/server/$env.archived_$archive_date.pem
151+
152+
# Build a new CA
153+
_truststore_build_ca $env $fqdn
154+
155+
# Add the previous CA to the new CA bundle (can be removed after clients have updated)
156+
cat truststore/ca/$env.archived_$archive_date.crt >> truststore/server/$env.pem
157+
158+
echo -e "✅ Successfully rotated CA for $env - previous CA archived with date: $archive_date"
159+
}
160+
161+
# Rotate a client cert - archive the existing cert and build a new one
162+
function _truststore_rotate_cert() {
163+
if [ $# -ne 3 ]; then
164+
echo "Usage: $0 rotate-cert <env> <ca> <fqdn>"
165+
exit 1;
166+
fi
167+
168+
cert_name=$1
169+
ca_name=$2
170+
fqdn=$3
171+
172+
# Archive the existing client certs
173+
archive_date=$(date +%Y-%m-%d)
174+
if [ -f "truststore/client/$cert_name.archived_$archive_date.crt" ] ||
175+
[ -f "truststore/client/$cert_name.archived_$archive_date.key" ]; then
176+
echo "Error: Archive files already exist for date $archive_date - please resolve before rotating the client cert"
177+
exit 1
178+
fi
179+
180+
mv truststore/client/$cert_name.crt truststore/client/$cert_name.archived_$archive_date.crt
181+
mv truststore/client/$cert_name.key truststore/client/$cert_name.archived_$archive_date.key
182+
183+
# Build a new client cert
184+
_truststore_build_cert $cert_name $ca_name $fqdn
185+
186+
echo -e "✅ Successfully rotated client cert for $cert_name - previous cert archived with date: $archive_date"
187+
}
188+
189+
# Disable an archived CA by removing it from the server pem file
190+
function _disable_archived_ca() {
191+
env=$1
192+
cat truststore/ca/$env.crt > truststore/server/$env.pem
193+
194+
echo -e "✅ Successfully disabled archived CA for $env"
195+
}
196+
197+
# Restore an archived CA by moving the archived files back to their original names
198+
function _restore_archived_ca() {
199+
env=$1
200+
archive_date=$2
201+
202+
mv truststore/ca/$env.archived_$archive_date.crt truststore/ca/$env.crt
203+
mv truststore/ca/$env.archived_$archive_date.key truststore/ca/$env.key
204+
mv truststore/server/$env.archived_$archive_date.pem truststore/server/$env.pem
205+
206+
echo -e "✅ Successfully restored archived CA for $env from date: $archive_date"
108207
}
109208

209+
# Restore an archived client cert by moving the archived files back to their original names
210+
function _restore_archived_cert() {
211+
env=$1
212+
archive_date=$2
213+
214+
mv truststore/client/$env.archived_$archive_date.crt truststore/client/$env.crt
215+
mv truststore/client/$env.archived_$archive_date.key truststore/client/$env.key
216+
217+
echo -e "✅ Successfully restored archived client cert for $env from date: $archive_date"
218+
}
110219

111220
function _truststore_build_all() {
112-
_truststore_build_ca "prod" "record-locator.national.nhs.uk"
113-
_truststore_build_ca "int" "record-locator.int.national.nhs.uk"
114-
_truststore_build_ca "ref" "record-locator.ref.national.nhs.uk"
115-
_truststore_build_ca "qa" "qa.record-locator.national.nhs.uk"
116-
_truststore_build_ca "dev" "record-locator.dev.national.nhs.uk"
221+
_truststore_build_ca "prod" "record-locator.national.nhs.uk_CA2"
222+
_truststore_build_ca "int" "record-locator.int.national.nhs.uk_CA2"
223+
_truststore_build_ca "ref" "record-locator.ref.national.nhs.uk_CA2"
224+
_truststore_build_ca "qa" "qa.record-locator.national.nhs.uk_CA2"
225+
_truststore_build_ca "dev" "record-locator.dev.national.nhs.uk_CA2"
117226

118227
_truststore_build_cert "prod" "prod" "api.record-locator.national.nhs.uk"
119228
_truststore_build_cert "int" "int" "int.api.record-locator.int.national.nhs.uk"
@@ -124,18 +233,26 @@ function _truststore_build_all() {
124233

125234
function _truststore_pull_ca() {
126235
env=$1
236+
echo "Pulling ${env} ca certificate"
127237
aws s3 cp "s3://${BUCKET}/ca/${env}.crt" "truststore/ca/${env}.crt"
128238
}
129239

240+
function _truststore_pull_ca_key() {
241+
env=$1
242+
echo "Pulling ${env} ca private key"
243+
aws s3 cp "s3://${BUCKET}/ca/${env}.key" "truststore/ca/${env}.key"
244+
}
245+
130246
function _truststore_pull_client() {
131247
env=$1
248+
echo "Pulling ${env} client certificate"
132249
aws s3 cp "s3://${BUCKET}/client/${env}.key" "truststore/client/${env}.key"
133250
aws s3 cp "s3://${BUCKET}/client/${env}.crt" "truststore/client/${env}.crt"
134251
}
135252

136253
function _truststore_pull_server() {
137254
env=$1
138-
echo "Pulling truststore/server/${env}.pem"
255+
echo "Pulling ${env} server trust certificate"
139256
aws s3 cp "s3://${BUCKET}/server/${env}.pem" "truststore/server/${env}.pem"
140257
}
141258

@@ -146,6 +263,47 @@ function _truststore_pull_all() {
146263
_truststore_pull_server $env
147264
}
148265

266+
function _truststore_push_all() {
267+
env=$1
268+
269+
timestamp="$(date +%Y-%m-%d_%H%M%S)"
270+
backup_dir="truststore/backup/${env}_${timestamp}"
271+
272+
echo -e "Backing up existing files to ${backup_dir}...."
273+
mkdir -p "${backup_dir}/ca" "${backup_dir}/client" "${backup_dir}/server"
274+
for f in "ca/${env}.crt" "ca/${env}.key" "client/${env}.crt" "client/${env}.key" "server/${env}.pem"
275+
do
276+
echo
277+
aws s3 cp "s3://${BUCKET}/$f" "${backup_dir}/$f" || echo "No existing file s3://${BUCKET}/$f to back up"
278+
279+
if [ -f "${backup_dir}/$f" ]
280+
then
281+
diff --brief "truststore/$f" "${backup_dir}/$f" || true
282+
fi
283+
done
284+
285+
echo
286+
echo -n "WARNING: You are about to upload files to the ${env} truststore - are you sure? [yes/NO] "
287+
read answer
288+
if [ "$answer" != "yes" ]; then
289+
echo "Aborting upload to ${env} truststore"
290+
exit 1
291+
fi
292+
293+
echo "Uploading ${env} ca certificate"
294+
aws s3 cp "truststore/ca/${env}.crt" "s3://${BUCKET}/ca/${env}.crt"
295+
296+
echo "Uploading ${env} ca private key"
297+
aws s3 cp "truststore/ca/${env}.key" "s3://${BUCKET}/ca/${env}.key"
298+
299+
echo "Uploading ${env} client certificate"
300+
aws s3 cp "truststore/client/${env}.key" "s3://${BUCKET}/client/${env}.key"
301+
aws s3 cp "truststore/client/${env}.crt" "s3://${BUCKET}/client/${env}.crt"
302+
303+
echo "Uploading ${env} server trust certificate"
304+
aws s3 cp "truststore/server/${env}.pem" "s3://${BUCKET}/server/${env}.pem"
305+
}
306+
149307
function _truststore() {
150308
local command=$1; shift
151309
local args=$@
@@ -157,7 +315,14 @@ function _truststore() {
157315
"pull-all") _truststore_pull_all $args ;;
158316
"pull-server") _truststore_pull_server $args ;;
159317
"pull-client") _truststore_pull_client $args ;;
160-
"pull-ca") _truststore_pull_server $args ;;
318+
"pull-ca") _truststore_pull_ca $args ;;
319+
"pull-ca-key") _truststore_pull_ca_key $args ;;
320+
"push-all") _truststore_push_all $args ;;
321+
"rotate-ca") _truststore_rotate_ca $args ;;
322+
"rotate-cert") _truststore_rotate_cert $args ;;
323+
"disable-archived-ca") _disable_archived_ca $args ;;
324+
"restore-archived-ca") _restore_archived_ca $args ;;
325+
"restore-archived-cert") _restore_archived_cert $args ;;
161326
*) _truststore_help $args ;;
162327
esac
163328
}

truststore/config/client.conf

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,3 @@ nsCertType = client
2828
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
2929
extendedKeyUsage = critical, clientAuth, codeSigning
3030
subjectKeyIdentifier = hash
31-
authorityKeyIdentifier = keyid, issuer

0 commit comments

Comments
 (0)