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+ #
311set -o errexit -o pipefail -o nounset
412
513BUCKET=" 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
6683function _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
111220function _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
125234function _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+
130246function _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
136253function _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+
149307function _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}
0 commit comments