Skip to content

Commit

Permalink
AEA-3189 setup mutual TLS (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-nhs authored Jun 15, 2023
1 parent e6d76aa commit e3ee57c
Show file tree
Hide file tree
Showing 17 changed files with 644 additions and 20 deletions.
6 changes: 3 additions & 3 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ RUN git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.11.3; \
echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc; \
echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc;

ENV PATH="$PATH:/home/vscode/.asdf/bin/:/workspaces/prescriptions-api/node_modules/.bin"
ENV PATH="$PATH:/home/vscode/.asdf/bin/:/workspaces/prescriptionsforpatients/node_modules/.bin"


# Install ASDF plugins
Expand All @@ -43,8 +43,8 @@ RUN asdf plugin add python; \
asdf plugin-add direnv;


WORKDIR /workspaces/prescriptions-api
ADD .tool-versions /workspaces/prescriptions-api/.tool-versions
WORKDIR /workspaces/prescriptionsforpatients
ADD .tool-versions /workspaces/prescriptionsforpatients/.tool-versions
ADD .tool-versions /home/vscode/.tool-versions

RUN asdf install; \
Expand Down
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"github.vscode-github-actions"
],
"settings": {
"python.defaultInterpreterPath": "/workspaces/prescriptions-api/.venv/bin/python",
"python.defaultInterpreterPath": "/workspaces/prescriptionsforpatients/.venv/bin/python",
"python.analysis.autoSearchPaths": true,
"python.analysis.extraPaths": [],
"python.testing.unittestEnabled": false,
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@ jobs:
STACK_NAME: PR-${{needs.get_issue_number.outputs.issue_number}}
ARTIFACT_BUCKET_PREFIX: PR-${{needs.get_issue_number.outputs.issue_number}}
TARGET_ENVIRONMENT: dev-pr
ENABLE_MUTUAL_TLS: false
secrets:
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}
5 changes: 5 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ jobs:
ARTIFACT_BUCKET_PREFIX: ${{needs.tag_release.outputs.spec_version}}
STACK_NAME: dev-ci
TARGET_ENVIRONMENT: dev
ENABLE_MUTUAL_TLS: true
secrets:
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.DEV_CLOUD_FORMATION_DEPLOY_ROLE }}

Expand All @@ -84,6 +85,7 @@ jobs:
ARTIFACT_BUCKET_PREFIX: ${{needs.tag_release.outputs.spec_version}}
STACK_NAME: ref-ci
TARGET_ENVIRONMENT: ref
ENABLE_MUTUAL_TLS: true
secrets:
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.REF_CLOUD_FORMATION_DEPLOY_ROLE }}

Expand All @@ -94,6 +96,7 @@ jobs:
ARTIFACT_BUCKET_PREFIX: ${{needs.tag_release.outputs.spec_version}}
STACK_NAME: qa-ci
TARGET_ENVIRONMENT: qa
ENABLE_MUTUAL_TLS: true
secrets:
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.QA_CLOUD_FORMATION_DEPLOY_ROLE }}

Expand All @@ -104,6 +107,7 @@ jobs:
ARTIFACT_BUCKET_PREFIX: ${{needs.tag_release.outputs.spec_version}}
STACK_NAME: int-ci
TARGET_ENVIRONMENT: int
ENABLE_MUTUAL_TLS: true
secrets:
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.INT_CLOUD_FORMATION_DEPLOY_ROLE }}

Expand All @@ -114,5 +118,6 @@ jobs:
ARTIFACT_BUCKET_PREFIX: ${{needs.tag_release.outputs.spec_version}}
STACK_NAME: prod-ci
TARGET_ENVIRONMENT: prod
ENABLE_MUTUAL_TLS: true
secrets:
CLOUD_FORMATION_DEPLOY_ROLE: ${{ secrets.PROD_CLOUD_FORMATION_DEPLOY_ROLE }}
12 changes: 12 additions & 0 deletions .github/workflows/sam_release_code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
TARGET_ENVIRONMENT:
required: true
type: string
ENABLE_MUTUAL_TLS:
required: true
type: string
secrets:
CLOUD_FORMATION_DEPLOY_ROLE:
required: true
Expand Down Expand Up @@ -47,4 +50,13 @@ jobs:
export stack_name=${{ inputs.STACK_NAME }}
export cloud_formation_execution_role=$(aws cloudformation list-exports --output json | \
jq -r '.Exports[] | select(.Name == "ci-resources:CloudFormationExecutionRole") | .Value' )
TRUSTSTORE_BUCKET_ARN=$(aws cloudformation describe-stacks \
--stack-name ci-resources \
--query 'Stacks[0].Outputs[?OutputKey==`TrustStoreBucket`].OutputValue' --output text)
TRUSTSTORE_BUCKET_NAME=$(echo ${TRUSTSTORE_BUCKET_ARN} | cut -d ":" -f 6)
export LATEST_TRUSTSTORE_VERSION=$(aws s3api list-object-versions \
--bucket ${TRUSTSTORE_BUCKET_NAME} \
--prefix truststore.pem \
--query 'Versions[?IsLatest].[VersionId]' --output text)
export enable_mutual_tls=${{ inputs.ENABLE_MUTUAL_TLS }}
make sam-deploy-package
3 changes: 2 additions & 1 deletion .vscode/prescriptions-api.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
],
"settings": {
"jest.disabledWorkspaceFolders": [
"prescriptions-api-monorepo"
"prescriptions-api-monorepo",
"packages/specification"
],
"files.exclude": {
"packages/": true,
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ sam-list-outputs: guard-AWS_DEFAULT_PROFILE guard-stack_name
sam-validate:
sam validate

sam-deploy-package: guard-artifact_bucket guard-artifact_bucket_prefix guard-stack_name guard-template_file guard-cloud_formation_execution_role
sam-deploy-package: guard-artifact_bucket guard-artifact_bucket_prefix guard-stack_name guard-template_file guard-cloud_formation_execution_role guard-LATEST_TRUSTSTORE_VERSION guard-enable_mutual_tls
sam deploy \
--template-file $$template_file \
--stack-name $$stack_name \
Expand All @@ -56,7 +56,8 @@ sam-deploy-package: guard-artifact_bucket guard-artifact_bucket_prefix guard-sta
--no-fail-on-empty-changeset \
--role-arn $$cloud_formation_execution_role \
--no-confirm-changeset \
--force-upload
--force-upload \
--parameter-overrides ParameterKey=TruststoreVersion,ParameterValue=$$LATEST_TRUSTSTORE_VERSION ParameterKey=EnableMutualTLS,ParameterValue=$$enable_mutual_tls

lint:
npm run lint --workspace packages/authz
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ This is an API for accessing prescription information.
- `packages/authz/` Deals with authorisation to the API.
- `packages/getMyPrescriptions/` Get prescription details
- `scripts/` Utilities helpful to developers of this specification.
- `cloudformation/` Contains a cloudformation file used to create resources for CI builds and deployments
- `cloudformation/` Contains cloudformation files used to create resources for CI builds and deployments
- `privateCA/` Contains script to create self signed CA certificate and a client certificate used for mutual TLS
- `.github` Contains github workflows that are used for building and deploying from pull requests and releases

Consumers of the API will find developer documentation on the [NHS Digital Developer Hub](https://digital.nhs.uk/developer/api-catalogue).
Expand Down
62 changes: 59 additions & 3 deletions cloudformation/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
This folder contains a cloudformation definition that is used to create the resources in an AWS account for CI processes to work.
This folder contains cloudformation definitions for 'manually' created resources that are only created once per environment. These need to manually applied as they are not created as part of a CI or pull request build

# CI Resources

ci_resources.yml contains resources that are needed for the CI pipeline to work. This should be applied to each environment.
It creates the following resources

- OIDC provider allowing github to assume a role in the account
- Cloudformation deploy role - github runners assume this role
- Cloudformation execution role - cloudformation uses this role when applying a changeset. This has minimum permissions so if a new resource type is added, the permissions will need modifying
- Artifact bucket and KMS key - resources used by CI build are uploaded to this bucket
- Trust store bucket and KMS key - public CA certs used for mutual TLS are uploaded to this bucket
- Secrets and KMS key - there are various secrets created for storing keys used in mutual TLS. These have a default value set, but the values are modified when creating new keys.
- - CAKeySecret - used to store the private CA key
- - CACertSecret - used to store the public CA cert
- - ClientKeySecret - used to store the private client key
- - ClientCertSecret - used to store the public client cert

The stack deployed in each environment must be called `ci-resources` as the deployment pipeline gets the bucket and cloudformation execution role from the stack as part of its processing.

Expand All @@ -15,7 +25,7 @@ export AWS_PROFILE=<name of AWS profile defined in ~/.aws/config>
aws sso login --sso-session sso-session
aws cloudformation deploy \
--template-file cloudformation/ci_resources.yaml \
--template-file cloudformation/ci_resources.yml \
--stack-name ci-resources \
--region eu-west-2 \
--capabilities CAPABILITY_IAM
Expand All @@ -24,7 +34,53 @@ aws cloudformation deploy \
Once this is deployed, you should get the ARN for the role `ci-resources:CloudFormationDeployRole` using this command

```
aws cloudformation list-exports
aws cloudformation list-exports \
--query 'Exports[?Name==`ci-resources:CloudFormationDeployRole`].Value' --output text
```

This value should then be stored in the github project as a repository secret called `<ENVIRONMENT>_CLOUD_FORMATION_DEPLOY_ROLE`

# Route 53 resources - management account

management_route53.yml contains route 53 resources created in the management account. This should only be applied to the management account.
It creates the following resources

- route 53 hosted zone for prescriptionsforpatients.national.nhs.uk
- NS records for {dev, int, ref, qa, prod}.prescriptionsforpatients.national.nhs.uk pointing to route 53 hosted zones in each account

To deploy the stack, use the following

```
export AWS_PROFILE=prescription-management
aws sso login --sso-session sso-session
aws cloudformation deploy \
--template-file cloudformation/management_route53.yml \
--stack-name route53-resources \
--region eu-west-2
```

# Route 53 resources - environment accounts

environment_route53.yml contains route 53 resources created in each environment account.
It creates the following resources

- route 53 hosted zone for {environment}.prescriptionsforpatients.national.nhs.uk

It outputs the following as exports as they are used in SAM deployments

- route53-resources:ZoneID - zoneID of zone created
- route53-resources:domain - domain name of the hosted zone

To deploy the stack, use the following

```
export AWS_PROFILE=<name of AWS profile defined in ~/.aws/config>
aws sso login --sso-session sso-session
aws cloudformation deploy \
--template-file cloudformation/environment_route53.yml \
--stack-name route53-resources \
--region eu-west-2 \
--parameter-overrides ParameterKey=environment,ParameterValue=<ENVIRONENT NAME>
```
Loading

0 comments on commit e3ee57c

Please sign in to comment.