Skip to content

Commit 30408ac

Browse files
committed
NDR-213 Add mTLS api gateway
1 parent 0e8743d commit 30408ac

13 files changed

+295
-22
lines changed

infrastructure/acm_certificate.tf

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
resource "aws_acm_certificate" "mtls_api_gateway_cert" {
2+
domain_name = local.mtls_api_gateway_full_domain_name
3+
validation_method = "DNS"
4+
5+
lifecycle {
6+
create_before_destroy = true
7+
}
8+
}
9+
10+
# Record used by ACM for DNS Validation
11+
resource "aws_route53_record" "validation" {
12+
for_each = {
13+
for dvo in aws_acm_certificate.mtls_api_gateway_cert.domain_validation_options : dvo.domain_name => {
14+
name = dvo.resource_record_name
15+
record = dvo.resource_record_value
16+
type = dvo.resource_record_type
17+
}
18+
}
19+
20+
allow_overwrite = true
21+
name = each.value.name
22+
records = [each.value.record]
23+
ttl = 60
24+
type = each.value.type
25+
zone_id = module.route53_fargate_ui.zone_id #todo change to mtls rute 53
26+
}
27+
28+
29+
resource "aws_acm_certificate_validation" "mtls_api_gateway_cert" {
30+
certificate_arn = aws_acm_certificate.mtls_api_gateway_cert.arn
31+
validation_record_fqdns = [for record in aws_route53_record.validation : record.fqdn]
32+
}

infrastructure/api-key-pdm.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
resource "aws_api_gateway_usage_plan" "api_key_pdm" {
22
name = "${terraform.workspace}_pdm-usage-plan"
33
api_stages {
4-
api_id = aws_api_gateway_rest_api.ndr_doc_store_api.id
5-
stage = aws_api_gateway_stage.ndr_api.stage_name
4+
api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
5+
stage = aws_api_gateway_stage.ndr_api_mtls.stage_name
66
}
77
}
88

infrastructure/api_mtls.tf

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# New API Gateway for mTLS
2+
resource "aws_api_gateway_rest_api" "ndr_doc_store_api_mtls" {
3+
name = "${terraform.workspace}_DocStoreApiMtls"
4+
description = "Document store API with mTLS enabled"
5+
6+
tags = {
7+
Name = "${terraform.workspace}_DocStoreApiMtls"
8+
}
9+
}
10+
11+
resource "aws_api_gateway_domain_name" "custom_api_domain_mtls" {
12+
domain_name = local.mtls_api_gateway_full_domain_name
13+
regional_certificate_arn = aws_acm_certificate_validation.mtls_api_gateway_cert.certificate_arn
14+
security_policy = "TLS_1_2"
15+
16+
endpoint_configuration {
17+
types = ["REGIONAL"]
18+
}
19+
20+
mutual_tls_authentication {
21+
truststore_uri = "s3://${terraform.workspace}-${var.truststore_bucket_name}/${var.ca_pem_filename}"
22+
}
23+
}
24+
25+
resource "aws_api_gateway_base_path_mapping" "api_mapping_mtls" {
26+
api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
27+
stage_name = var.environment
28+
domain_name = aws_api_gateway_domain_name.custom_api_domain_mtls.domain_name
29+
30+
depends_on = [aws_api_gateway_deployment.ndr_api_deploy_mtls]
31+
}
32+
33+
resource "aws_api_gateway_deployment" "ndr_api_deploy_mtls" {
34+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
35+
36+
depends_on = [
37+
aws_api_gateway_rest_api.ndr_doc_store_api_mtls,
38+
aws_api_gateway_resource.get_document_reference_mtls,
39+
module.get-doc-fhir-lambda,
40+
aws_api_gateway_integration.get_doc_fhir_lambda_integration,
41+
aws_lambda_permission.lambda_permission_get_mtls_api,
42+
module.post-document-references-fhir-lambda,
43+
aws_api_gateway_integration.post_doc_fhir_lambda_integration,
44+
aws_lambda_permission.lambda_permission_post_mtls_api,
45+
module.search-document-references-fhir-lambda,
46+
aws_api_gateway_integration.search_doc_fhir_lambda_integration,
47+
aws_lambda_permission.lambda_permission_search_mtls_api,
48+
]
49+
50+
lifecycle {
51+
create_before_destroy = true
52+
}
53+
54+
variables = {
55+
deployed_at = timestamp()
56+
}
57+
}
58+
59+
resource "aws_api_gateway_stage" "ndr_api_mtls" {
60+
deployment_id = aws_api_gateway_deployment.ndr_api_deploy_mtls.id
61+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
62+
stage_name = var.environment
63+
xray_tracing_enabled = var.enable_xray_tracing
64+
}
65+
66+
resource "aws_cloudwatch_log_group" "mtls_api_gateway_stage" {
67+
name = "API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id}/${var.environment}"
68+
retention_in_days = 0
69+
depends_on = [
70+
aws_api_gateway_account.logging
71+
]
72+
}
73+
74+
resource "aws_api_gateway_method_settings" "mtls_api_gateway_stage" {
75+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
76+
stage_name = aws_api_gateway_stage.ndr_api_mtls.stage_name
77+
method_path = "*/*"
78+
79+
settings {
80+
logging_level = "INFO"
81+
metrics_enabled = true
82+
data_trace_enabled = true
83+
}
84+
}
85+
86+
resource "aws_api_gateway_gateway_response" "unauthorised_response_mtls" {
87+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
88+
response_type = "DEFAULT_4XX"
89+
90+
response_templates = {
91+
"application/json" = "{\"message\":$context.error.messageString}"
92+
}
93+
94+
response_parameters = {
95+
"gatewayresponse.header.Access-Control-Allow-Origin" = contains(["prod"], terraform.workspace) ? "'https://${var.domain}'" : "'https://${terraform.workspace}.${var.domain}'"
96+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'*'"
97+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Auth,X-Api-Key,X-Amz-Security-Token,X-Auth-Cookie,Accept'"
98+
"gatewayresponse.header.Access-Control-Allow-Credentials" = "'true'"
99+
}
100+
}
101+
102+
resource "aws_api_gateway_gateway_response" "bad_gateway_response_mtls" {
103+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
104+
response_type = "DEFAULT_5XX"
105+
106+
response_templates = {
107+
"application/json" = "{\"message\":$context.error.messageString}"
108+
}
109+
110+
response_parameters = {
111+
"gatewayresponse.header.Access-Control-Allow-Origin" = contains(["prod"], terraform.workspace) ? "'https://${var.domain}'" : "'https://${terraform.workspace}.${var.domain}'"
112+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'*'"
113+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Auth,X-Api-Key,X-Amz-Security-Token,X-Auth-Cookie,Accept'"
114+
"gatewayresponse.header.Access-Control-Allow-Credentials" = "'true'"
115+
}
116+
}
117+
118+
module "mtls_api_endpoint_url_ssm_parameter" {
119+
source = "./modules/ssm_parameter"
120+
name = "${terraform.workspace}_ApiEndpointMtls"
121+
description = "mTLS api endpoint URL for ${var.environment}"
122+
resource_depends_on = aws_api_gateway_deployment.ndr_api_deploy_mtls
123+
value = "https://${aws_api_gateway_base_path_mapping.api_mapping_mtls.domain_name}"
124+
type = "SecureString"
125+
owner = var.owner
126+
environment = var.environment
127+
}

infrastructure/dev.tfvars

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
environment = "dev"
2-
owner = "nhse/ndr-team"
3-
domain = "access-request-fulfilment.patient-deductions.nhs.uk"
4-
certificate_domain = "access-request-fulfilment.patient-deductions.nhs.uk"
5-
certificate_subdomain_name_prefix = "api-"
1+
environment = "dev"
2+
owner = "nhse/ndr-team"
3+
domain = "access-request-fulfilment.patient-deductions.nhs.uk"
4+
certificate_domain = "access-request-fulfilment.patient-deductions.nhs.uk"
5+
certificate_subdomain_name_prefix = "api-"
6+
certificate_subdomain_name_prefix_mtls = "mtls-"
67

78
standalone_vpc_tag = "ndr-dev"
89
standalone_vpc_ig_tag = "ndr-dev"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module "fhir_document_reference_mtls_gateway" {
2+
source = "./modules/gateway"
3+
api_gateway_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
4+
parent_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.root_resource_id
5+
http_methods = ["POST", "GET"]
6+
authorization = "NONE"
7+
api_key_required = true
8+
gateway_path = "DocumentReference"
9+
require_credentials = true
10+
}

infrastructure/lambda-get-document-fhir.tf

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ resource "aws_api_gateway_resource" "get_document_reference" {
44
path_part = "{id}"
55
}
66

7+
resource "aws_api_gateway_resource" "get_document_reference_mtls" {
8+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
9+
parent_id = module.fhir_document_reference_mtls_gateway.gateway_resource_id
10+
path_part = "{id}"
11+
}
12+
713
resource "aws_api_gateway_method" "get_document_reference" {
814
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api.id
915
resource_id = aws_api_gateway_resource.get_document_reference.id
@@ -15,6 +21,17 @@ resource "aws_api_gateway_method" "get_document_reference" {
1521
}
1622
}
1723

24+
resource "aws_api_gateway_method" "get_document_reference_mtls" {
25+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
26+
resource_id = aws_api_gateway_resource.get_document_reference_mtls.id
27+
http_method = "GET"
28+
authorization = "NONE"
29+
api_key_required = true
30+
request_parameters = {
31+
"method.request.path.id" = true
32+
}
33+
}
34+
1835

1936
module "get-doc-fhir-lambda" {
2037
source = "./modules/lambda"
@@ -46,3 +63,21 @@ module "get-doc-fhir-lambda" {
4663
depends_on = [aws_api_gateway_method.get_document_reference]
4764
}
4865

66+
resource "aws_api_gateway_integration" "get_doc_fhir_lambda_integration" {
67+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
68+
resource_id = aws_api_gateway_resource.get_document_reference_mtls.id
69+
http_method = "GET"
70+
integration_http_method = "POST"
71+
type = "AWS_PROXY"
72+
uri = module.get-doc-fhir-lambda[0].invoke_arn
73+
}
74+
75+
resource "aws_lambda_permission" "lambda_permission_get_mtls_api" {
76+
statement_id = "AllowAPImTLSGatewayInvoke"
77+
action = "lambda:InvokeFunction"
78+
function_name = module.get-doc-fhir-lambda[0].lambda_arn
79+
principal = "apigateway.amazonaws.com"
80+
# The "/*/*" portion grants access from any method on any resource
81+
# within the API Gateway REST API.
82+
source_arn = "${aws_api_gateway_rest_api.ndr_doc_store_api_mtls.execution_arn}/*/*"
83+
}

infrastructure/lambda-post-document-fhir.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,22 @@ module "post-document-references-fhir-lambda" {
2727
PRESIGNED_ASSUME_ROLE = aws_iam_role.create_post_presign_url_role.arn
2828
}
2929
}
30+
31+
resource "aws_api_gateway_integration" "post_doc_fhir_lambda_integration" {
32+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
33+
resource_id = module.fhir_document_reference_mtls_gateway.gateway_resource_id
34+
http_method = "POST"
35+
integration_http_method = "POST"
36+
type = "AWS_PROXY"
37+
uri = module.post-document-references-fhir-lambda[0].invoke_arn
38+
}
39+
40+
resource "aws_lambda_permission" "lambda_permission_post_mtls_api" {
41+
statement_id = "AllowAPImTLSGatewayInvoke"
42+
action = "lambda:InvokeFunction"
43+
function_name = module.post-document-references-fhir-lambda[0].lambda_arn
44+
principal = "apigateway.amazonaws.com"
45+
# The "/*/*" portion grants access from any method on any resource
46+
# within the API Gateway REST API.
47+
source_arn = "${aws_api_gateway_rest_api.ndr_doc_store_api_mtls.execution_arn}/*/*"
48+
}

infrastructure/lambda-search-document-references-fhir.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,22 @@ module "search-document-references-fhir-lambda" {
3030
module.ndr-app-config
3131
]
3232
}
33+
34+
resource "aws_api_gateway_integration" "search_doc_fhir_lambda_integration" {
35+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api_mtls.id
36+
resource_id = module.fhir_document_reference_mtls_gateway.gateway_resource_id
37+
http_method = "GET"
38+
integration_http_method = "POST"
39+
type = "AWS_PROXY"
40+
uri = module.search-document-references-fhir-lambda[0].invoke_arn
41+
}
42+
43+
resource "aws_lambda_permission" "lambda_permission_search_mtls_api" {
44+
statement_id = "AllowMtlsApiGatewayInvoke"
45+
action = "lambda:InvokeFunction"
46+
function_name = module.search-document-references-fhir-lambda[0].lambda_arn
47+
principal = "apigateway.amazonaws.com"
48+
# The "/*/*" portion grants access from any method on any resource
49+
# within the API Gateway REST API.
50+
source_arn = "${aws_api_gateway_rest_api.ndr_doc_store_api_mtls.execution_arn}/*/*"
51+
}

infrastructure/preprod.tfvars

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
environment = "pre-prod"
2-
owner = "nhse/ndr-team"
3-
domain = "national-document-repository.nhs.uk"
4-
certificate_domain = "pre-prod.national-document-repository.nhs.uk"
5-
certificate_subdomain_name_prefix = "api."
1+
environment = "pre-prod"
2+
owner = "nhse/ndr-team"
3+
domain = "national-document-repository.nhs.uk"
4+
certificate_domain = "pre-prod.national-document-repository.nhs.uk"
5+
certificate_subdomain_name_prefix = "api."
6+
certificate_subdomain_name_prefix_mtls = "mtls."
67

78
standalone_vpc_tag = "ndr-pre-prod"
89
standalone_vpc_ig_tag = "ndr-pre-prod"

infrastructure/prod.tfvars

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
environment = "prod"
2-
owner = "nhse/ndr-team"
3-
domain = "national-document-repository.nhs.uk"
4-
certificate_domain = "national-document-repository.nhs.uk"
5-
certificate_subdomain_name_prefix = "api."
1+
environment = "prod"
2+
owner = "nhse/ndr-team"
3+
domain = "national-document-repository.nhs.uk"
4+
certificate_domain = "national-document-repository.nhs.uk"
5+
certificate_subdomain_name_prefix = "api."
6+
certificate_subdomain_name_prefix_mtls = "mtls."
67

78
standalone_vpc_tag = "ndr-prod"
89
standalone_vpc_ig_tag = "ndr-prod"

0 commit comments

Comments
 (0)