Rotating secrets - alphagov/notifications-manuals GitHub Wiki
- API key for our apps to talk to our API
- API key for our apps to talk to document download API
- API key for our apps to talk to template preview
For our applications to talk our API they must use one of the API keys found in internal_client_api_keys in credentials/<env>/ssm/. If you want to rotate these keys then you should:
- Add the new secret to
credentials/<env>/ssm/internal_client_api_keys - Deploy the API so it now allows apps to auth with the new secret
- Change e.g.
credentials/staging/ssm/admin_client_secretto be the new secret - Deploy the affected apps e.g. document-download-frontend and the admin app
- Remove the old secret from
credentials/<env>/ssm/internal_client_api_keys - Deploy the API to preview and then run functional tests for all the other apps (antivirus, document download etc) to check they can still talk to the API. If so, you can then continue to deploy the API to production
For our applications to talk to the document download API they must use one of the API keys found in auth_tokens in credentials/<env>/ssm/. If you want to rotate these keys then you should:
- Add the new secret to
credentials/<env>/ssm/auth_tokensusing a:separated list, for examplesecret1:secret2 - Deploy the document download API so it now allows apps to auth with the new secret
- Change
credentials/<env>/ssm/document_download_api_keyandDOCUMENT_DOWNLOAD_API_KEYincredentials/functional-tests/<env>-functionalto be the new secret - Deploy the API
- Remove the old secret from
credentials/<env>/ssm/auth_tokens - Deploy the document download API to preview and then run functional tests for all the other apps (admin, api, document download, antivirus) to check they can still talk to the document download API. If so, you can then continue to deploy the document download API to production
For our applications to talk to the template preview they must use one of the API keys found in template_preview_internal_secrets in credentials/<env>/ssm. If you want to rotate these keys then you should:
- Add the new secret to
credentials/<env>/ssm/template_preview_internal_secrets - Deploy template preview so it now allows apps to auth with the new secret
- Change
template_preview_api_keyincredentials/<env>/ssm - Deploy the API and the admin apps
- Remove the old secret from
credentials/<env>/ssm/template_preview_internal_secrets - Deploy template preview to preview and then run functional tests for the admin app and api to check they can still talk to template preview. If so, you can then continue to deploy template preview to production
This is achievable without downtime.
From the Logit stack's settings, go to the "Endpoints" page and hit "Add API Key" under "HTTPS API endpoint details":
Open a PR to notifications-credentials, updating credentials/<environment-name>/logit/api_key with the new api key for each affected environment (some Logit stacks have several environments sending logs to them) and get it merged.
Run scripts/upload-credentials/upload-credentials.sh (from the notifications-aws repo) against the affected environments (remember, these credentials are uploaded to the environment's aws account, not the notify-deploy account).
(Re-)run the deploy job of that environment's pipeline - the secret is read from the parameter store by terraform when it runs and provided to the lambda as a standard environment variable (no, this is not ideal).
After checking we're still getting app logs through to Logit, delete the old API key from the Logit control panel:
In theory this should be zero-downtime, but in practise switching the keys in logit doesn't always go smoothly. However, there shouldn't be any danger of permanent log loss even in the worst case scenario.
For an environment, somehow assume a working terraform shell (my favourite way is to fly hijack the deploy-infra-terraform task and manually initialise the shell by copy-pasting the first few commands of the task up to the terraform init). Then detach the currently provisioned access key from the terraform state using:
terraform state rm 'module.logit_user_cloudfront.aws_iam_access_key.iam_user_key["logit_cloudfront_<env-name>"]'
After this, re-run the deploy concourse job, during which terraform will provision a new access key to replace the detached one. The new key's credentials will be stored in the parameter store variables /notify/<env-name>/apps/logit/generated/logit_cloudfront_<env-name>/access_key and /notify/<env-name>/apps/logit/generated/logit_cloudfront_<env-name>/access_key. Set these values as the "Access Key ID" and "Secret Access Key" for all the S3 buckets configured as inputs for the environment's stack.
Once you're confident the stack is still receiving Cloudfront logs, can see the new access key being used in the AWS IAM console and the old access key not being used for some time (check this - do not take it for granted because sometimes logit needs to have its stack restarted or other manual intervention before it will successfully and exclusively be using the new key), delete the old access key manually.
Time-based one time passcodes (TOPTs) are generated using a seed key stored in the notifications-credentials repos. This is used to derive the OTP required to log into the SMS providers' UI. Seed keys must not be associated with a mobile authenticator app.
This section is written as if MFA does not exist on the account you're trying to log into. If it does then go to the Logging into the UI link here section for instructions on how to do this.
Log into Firetext, as per linked instructions, then click on the cog, then 2FA under My Settings.
The next steps vary depending on whether MFA already exists:
1a. MFA exists:
Disable the existing MFA by entering your password and a 2FA Code and click Save Changes. This will immediately remove your MFA and cannot be undone.
1b. MFA does not exist:
You can then enable 2FA, which will generate a QR code, containing the seed key. Take a screen grab of this seed key.
-
Go to https://zxing.org/w/decode.jspx and upload the picture of the QR code.
-
This will decode the image and give you the seed key and the backup
-
In
notifications-credentialsedit thenotifications/firetext.gpgcredential and replace the seed key and backup code (labelledOTP,Recovery key) for the appropriate env with the new key. -
Commit and merge this change in.
This section assumes knowledge of the notifications-credentials repo, how to force ECS to do new deploys and that the rotation is for the production environment.
It does not talk about testing the key in different envs first, it is assumed sensible practice is being followed. The steps for rotating the key are applicable to all envs.
This API key can be rotated with zero downtime.
notifications-credentials/credentials/firetext.gpg contains the login details for all envs.
You will need to generate an OTP when logging into Firetext. To do this, decrypt the secret, which will give you something like this:
notify-pass show credentials/firetext
# Other envs omiited for brevity
Development environments
------------------------
Email: notify-provider-accounts+firetext-dev@digital.cabinet-office.gov.uk
Password: Omitted
API key: Omitted
OTP: NRVWS2TVNB4WQZ3GOR4XK2TJNNXWY23KNBTXI6LIOVVGS23JOV4Q==== # Made up for this example
Recovery key: OmittedThen generate a 6 digit OTP from the seed secret listed above as OTP
pip install pyotp
echo "<your OTP key here>" | python -c "import sys, pyotp; print(pyotp.TOTP(sys.stdin.read().strip()).now())" | pbcopye.g.
python -c 'import pyotp; print(pyotp.TOTP("NRVWS2TVNB4WQZ3GOR4XK2TJNNXWY23KNBTXI6LIOVVGS23JOV4Q====").now())' | pbcopy
# paste 526762- Go to settings > cog in top right corner > API > Add Key
You must then edit the existing secret in the notifications-credentials repo, using the instructions here and merge the change in. The credential can be found under the notifications-credentials/credentials/<env-name>/ssm/firetext_api_key path.
The ECS tasks then need rotating to force them to use the new API key which can be done in one of 2 ways:
- (Recommended) a new deployment will rotate all existing workers and force them to use the new key.
- Forcing a new deployment for the following services.
AWS console > ECS >
notifycluster >- for
devenvsapi-worker,api-web - for
stagingandprod:
- api-web
- api-worker-internal
- api worker jobs
- api worker letters
- api-worker-periodic
- api-worker-receipts
- api-worker-report-requests-notifications
- api-worker-reporting,
- api-worker-research,
- api-worker-retry-tasks
- api-worker-sender
- api-worker-sender-letters
- api-worker-service-callbacks
- api-worker-service-callbacks-retry
- for
3. (Optional) You can check that the new task definition is being used by exec'ing onto the new container./
gds aws notify-<env name>-admin -- aws ecs execute-command --cluster notify --task <task-name> --container <service name> --interactive --command '/bin/sh'Then checking the env vars for the new API key
env | grep FIRETEXT_API_KEYOnce you are happy your new API key is being used successfully, you should initially deactivate (not delete) your old API key by clicking the cog icon next to the old API key, setting the status to off and clicking Save Changes.
After a period of at least a month of using your new API key with the old one deactivated you can safely delete the old key. Click on the bin icon of the disabled API key and click Remove Key. Deleted keys cannot be recovered.
- Decrypt the MMG creds
cd path/to/notifications-credentials
pass credentials/mmg- Log into the relevant MMG account using the creds and go to the
Servicestab.
- Under
Create New API Connection:set:- The
API Group Name:to the environment (Dev|Staging|Prod). - The
API Account TypetoLive. - The
Delivery Receipt URL:to one of the following, depending on the env:-
Dev-https://api.notify.works/notifications/sms/mmg -
Staging-https://api.staging-notify.works/notifications/sms/mmg -
Prod-https://api.notifications.service.gov.uk/notifications/sms/mmg
-
- Click
Create API Connection.
- The
An active API key has now been created.
- You will then be given your
UsernameandPasswordfor this API connection.
You must add them to the credentials/mmg credential now, as you will not be shown these details again. You must generate your API key using this info and add it to the same credential. You can do this by adding your integration name and password to the below code and running it.
echo -n 'integration-name:password' | openssl base64
e.g.
echo -n 'MMG_cabo1721_2:e7awwbX2i5KjUhI' | openssl base64
TU1HX2NhYm8xNzIxXzI6ZTdhd3diWDJpNUtqVWhJ <-- This is the API key-
You must then edit the existing secret in the
notifications-credentialsrepo, using the instructions here and merge the change in. The credential can be found under thenotifications-credentials/credentials/<env-name>/ssm/mmg_api_keypath. -
Raise a PR for the changes and merge them in.
The ECS tasks then need rotating to force them to use the new API key which can be done in one of 2 ways:
- (Recommended) a new deployment will rotate all existing workers and force them to use the new key.
- Forcing a new deployment for the following services.
AWS console > ECS >
notifycluster >- for
devenvsapi-worker,api-web - for
stagingandprod:
- api-web
- api-worker-internal
- api worker jobs
- api worker letters
- api-worker-periodic
- api-worker-receipts
- api-worker-report-requests-notifications
- api-worker-reporting,
- api-worker-research,
- api-worker-retry-tasks
- api-worker-sender
- api-worker-sender-letters
- api-worker-service-callbacks
- api-worker-service-callbacks-retry
- for
3. (Optional) You can check that the new task definition is being used by exec'ing onto the new container.
gds aws notify-<env name>-admin -- aws ecs execute-command --cluster notify --task <task-name> --container <service name> --interactive --command '/bin/sh'Then checking the env vars for the new API key
env | grep MMG_API_KEYOnce you are happy your new API key is being used successfully, you should initially disable (not delete) your old API key by clicking Service > Modify on the key you want to disable > change API status to Disabled, click Update API.
After a period of at least a month of using your new API key with the old one disabled you can safely revoke the old key.
Go to Service > Modify on the key you want to disable > click Revoke API.
Pagerduty (PD) has many 'integrations' which can trigger PD alerts (e.g. Concourse, Cronitor etc). As such there are multiple credentials associated with Pagerduty.
PD rotation is different to other credentials. You must be a PD admin to create new integration keys (the credential required for telling PD to trigger an alert). There is generally one PD admin per service area. At the time of writing this is Ishwar Pandeya. If they are absent, then communicate with Dean Wilson and failing that EE for help with the rotation to help with the rotation.
If you wish to check who is currently set as the PD admin then get a Github admin to go to this page and check.
Pagerduty's docs for rotating credentials are good. But please note that there is no option for creating multiple integration keys. Creating a new one will immediately overwrite the old one. Rotation cannot happen without some downtime.
There are around 20 PD credential entries in notifications-credentials, however these are not all unique entries. Below is a table stating which are shared, incidated by a matching letter in the Matching cred column.
| Matching cred | Credential entry in notifications-aws | Where is it used / where does it exist. |
|---|---|---|
| a | credentials/alertmanager/pagerduty-p2.gpg | concourse/deploy-notify/deploy-notify.yml.j2 (only for prod, disabled for dev / staging) Pagerduty > services > # GOV.UK Notify Warnings > RE Prometheus and Alertmanager |
| b | credentials/concourse/pagerduty-p1 | concourse/deploy-notify/deploy-notify.yml.j2 (only for prod, disabled for dev / staging) Pagerduty > services > # GOV.UK Notify P1 outages > Concourse |
| b | credentials/production/concourse-ssm/pagerduty_p1_service_key.gpg | concourse/deploy-notify/deploy-notify.yml.j2 (only for prod, disabled for dev / staging) Pagerduty > services > # GOV.UK Notify P1 outages > Concourse |
| c | credentials/concourse/pagerduty-p2 | concourse/deploy-notify/deploy-notify.yml.j2 (only for prod, disabled for dev / staging) Pagerduty > services > # GOV.UK Notify Warnings > Concourse |
| c | credentials/production/concourse-ssm/pagerduty_p2_service_key.gpg | concourse/deploy-notify/deploy-notify.yml.j2 (only for prod, disabled for dev / staging) Pagerduty > services > # GOV.UK Notify Warnings > Concourse |
| d | credentials/dev-shared/ssm/pagerduty_warning_routing_key | aws-prometheus/templates/alertmanager_config.yaml Pagerduty > services > # GOV.UK Notify Warnings > AWS Managed Prometheus Events API V2 |
| d | credentials/pagerduty/notify_warnings_events_v2_integration_key.gpg | aws-prometheus/templates/alertmanager_config.yaml Pagerduty > services > # GOV.UK Notify Warnings > AWS Managed Prometheus Events API V2 |
| d | credentials/staging/ssm/pagerduty_critical_routing_key.gpg | AKA the integration key (allows the notifications to go to the right team) aws-prometheus/templates/alertmanager_config.yaml Pagerduty > services > # GOV.UK Notify Warnings > AWS Managed Prometheus Events API V2 |
| d | credentials/staging/ssm/pagerduty_warning_routing_key.gpg | AKA the integration key (allows the notifications to go to the right team) aws-prometheus/templates/alertmanager_config.yaml Pagerduty > services > # GOV.UK Notify Warnings > AWS Managed Prometheus Events API V2 |
| d | credentials/production/ssm/pagerduty_warning_routing_key.gpg | AKA the integration key (allows the notifications to go to the right team) aws-prometheus/templates/alertmanager_config.yaml Pagerduty > services > # GOV.UK Notify Warnings > AWS Managed Prometheus Events API V2 |
| e | credentials/pagerduty/notify_outages_events_v2_integration_key.gpg | aws-prometheus/templates/alertmanager_config.yaml Pagerduty > services > # GOV.UK Notify P1 outages > AWS Managed Prometheus Events API V2 |
| e | credentials/dev-shared/ssm/pagerduty_critical_routing_key.gpg |
aws-prometheus/templates/alertmanager_config.yaml data.aws_ssm_parameter.pagerduty_warning_routing_key.value Pagerduty > services > # GOV.UK Notify P1 outages > AWS Managed Prometheus Events API V2 |
| e | credentials/production/ssm/pagerduty_critical_routing_key.gpg | aws-prometheus/templates/alertmanager_config.yaml data.aws_ssm_parameter.pagerduty_warning_routing_key.value Pagerduty > services > # GOV.UK Notify P1 outages > AWS Managed Prometheus Events API V2 |
| f | credentials/staging/cloudwatch/pagerduty_critical_url.gpg HERE |
Uploaded to param store in scripts/upload-credentials/upload-credentials.sh Accessed via data block in terraform/notify-infra/data.tf data.aws_ssm_parameter.cloudwatch_pagerduty_critical_url Passed into 2 modules: 1. pagerduty-alerts terraform/notify-infra/monitoring.tf 2. Consumed terraform/modules/ecs-proxy/pagerduty.tf as an SNS subscription which is declared in ecs-proxy terraform/notify-infra/cdn.tf called cloudfront_critical_alerts (us-east-1 for some reason. - because its linked with CDN?) Pagerduty > services > # GOV.UK Notify Warnings > Amazon Cloudwatch |
| f | credentials/staging/cloudwatch/pagerduty_warning_url.gpg | This is not used in any SNS topic. Pagerduty > services > # GOV.UK Notify Warnings > Amazon Cloudwatch |
| f | credentials/production/cloudwatch/pagerduty_warning_url.gpg HERE |
This is not used in any SNS topic. Pagerduty > services > # GOV.UK Notify Warnings > Amazon Cloudwatch |
| g | credentials/production/cloudwatch/pagerduty_critical_url.gpg | Uploaded to param store in scripts/upload-credentials/upload-credentials.sh Accessed via data block in terraform/notify-infra/data.tf data.aws_ssm_parameter.cloudwatch_pagerduty_critical_url Used in SNS topic in prod notify-alerts-critical Pagerduty > services > # GOV.UK Notify P1 outages > Amazon Cloudwatch |
| h | credentials/production/logit/elastalert/pagerduty-critical-key.gpg |
Logit prod > Settings > Alerting > 5xxs_production_critical.yaml Logit prod > Settings > Alerting > 5XX-critical-production-resolve.yaml Logit prod > Settings > Alerting > celery_failures_production_critical.yaml |
| i | credentials/production/logit/elastalert/pagerduty-warning-key.gpg | I can't see this in Logit alerting (where youd expect as its same format as above), not used anymore? None of the staging alerts have any creds that would allow them to send PD alerts. |
| j | credentials/sentry.gpg | This is integrated with PD, but only owner / admin / manager can see this. https://govuk-notify-gds.sentry.io/settings/integrations/pagerduty/ |
| Client Repo | https://github.com/alphagov/notifications-java-client/ |
| External Portal | Maven Central |
| Notify-pass login details | notify-pass credentials/maven-central/username |
| Notify-pass credentials to edit | notify-pass edit credentials/maven-central/token-username |
- Login to Maven Central using the credentials above.
- Navigate to Team > View User Tokens.
- You will see the token for Concourse. To rotate this, create a new token. Take note of the token username and token secret and update the notify-pass credential specified above.
- Once the new token has merged into notifications-credentials you must revoke the old token in the Maven Central portal.
| Client Repo | https://github.com/alphagov/notifications-net-client/ |
| External Portal |
Appveyor handles CI Nuget is the package repository |
| Notify-pass login details | notify-pass credentials/appveyor/username |
| Notify-pass credential to edit | notify-pass edit credentials/appveyor/api_key |
-
log into the Appveyor portal.
-
Click on GOV.UK Notify in the top right hand corner > My profile.
-
Navigate to API Keys in the left hand menu.
-
Select GOVUKNotify in the drop down list of accounts to apply to. The current API key will display.
-
Click "generate new API token".
-
Update the token in notifications-credentials at the location above.
-
Once committed, revoke the old API token.
The API key that Appveyor uses to publish to Nuget may also be found in the Appveyor portal.
| Client Repo | https://github.com/alphagov/notifications-ruby-client/ |
| External Portal | rubygems |
| Notify-pass login details | notify-pass credentials/rubygems/email |
| Notify-pass credential to edit | notify-pass credentials/rubygems/api_key |
-
Login to the rubygems portal.
-
Under "My Gems" you will see the notifications-ruby-client.
-
Navigate to the profile icon drop down menu in top right and select "Settings".
-
Scroll down and select "API keys" to show the existing API key. You can create a new API key here.
-
Update notificiations-credentials at the location above.
-
Once the new API key is commited, revoke the old API key in the rubygems portal.
| Client Repo | https://github.com/alphagov/notifications-python-client/ |
| External Portal | PyPi portal |
| Notify-pass login details | notify-pass credentials/pypi/username |
| Notify-pass credential to edit | notify-pass credentials/pypi/python-client-api-token |
-
Log into the PyPi portal. NB. email address is not accepted in the username field.
-
Once logged in navigate to top right corner "govuknotify" drop down list and select "Account Settings".
-
Scroll down to the "API tokens" section. Generate a new API token using the Add API token button.
-
Add the new token to notifications-credentials. Once it has been committed you must revoke the old token in the PyPI portal.
A separate wiki page has been written showing how to rotate the NPM client credentials. In this case the API key only has an expiry of 90 days. There is not currently a way to automate rotation of the API key, so we have chosen to do Just In Time rotation, as we only publish the client periodically.
The PHP client is not published to a remote repository for consumption. Instead users are directed to use a PHP dependency manager which targets our github repository. As such we do not have any API keys that need rotating for this client.