Skip to content

Commit fd4cd6f

Browse files
authored
[NDR-213] Add mTLS api gateway (#401)
* NDR-213 Add mTLS api gateway * NDR-213 Fix indexing * NDR-213 Add depends on for methods * NDR-213 Fix indexing * NDR-213 Remove indexing * NDR-213 Remove unessecary prefix var * NDR-213 tf fmt * NDR-213 Index truststore bucket * NDR-213 tf fmt * NDR-213 typo * NDR-213 Fix reference * NDR-213 Fix reference * NDR-213 Refactor conditional into locals * NDR-213 Disable default endpoint * NDR-213 Set Api Gateway endpoint type to Regional
1 parent 7b029b5 commit fd4cd6f

File tree

9 files changed

+252
-2
lines changed

9 files changed

+252
-2
lines changed

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

infrastructure/buckets.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ module "ndr-truststore" {
128128
force_destroy = local.is_force_destroy
129129
}
130130

131+
data "aws_s3_object" "truststore_ext_cert" {
132+
bucket = local.truststore_bucket_id
133+
key = var.ca_pem_filename
134+
}
135+
131136
# Lifecycle Rules
132137
resource "aws_s3_bucket_lifecycle_configuration" "lg-lifecycle-rules" {
133138
bucket = module.ndr-lloyd-george-store.bucket_id
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: 38 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,24 @@ 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.invoke_arn
73+
74+
depends_on = [aws_api_gateway_method.get_document_reference_mtls]
75+
76+
}
77+
78+
resource "aws_lambda_permission" "lambda_permission_get_mtls_api" {
79+
statement_id = "AllowAPImTLSGatewayInvoke"
80+
action = "lambda:InvokeFunction"
81+
function_name = module.get-doc-fhir-lambda.lambda_arn
82+
principal = "apigateway.amazonaws.com"
83+
# The "/*/*" portion grants access from any method on any resource
84+
# within the API Gateway REST API.
85+
source_arn = "${aws_api_gateway_rest_api.ndr_doc_store_api_mtls.execution_arn}/*/*"
86+
}

infrastructure/lambda-post-document-fhir.tf

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,25 @@ 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.invoke_arn
38+
39+
depends_on = [module.fhir_document_reference_mtls_gateway]
40+
41+
}
42+
43+
resource "aws_lambda_permission" "lambda_permission_post_mtls_api" {
44+
statement_id = "AllowAPImTLSGatewayInvoke"
45+
action = "lambda:InvokeFunction"
46+
function_name = module.post-document-references-fhir-lambda.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/lambda-search-document-references-fhir.tf

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,24 @@ 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.invoke_arn
41+
42+
depends_on = [module.fhir_document_reference_mtls_gateway]
43+
}
44+
45+
resource "aws_lambda_permission" "lambda_permission_search_mtls_api" {
46+
statement_id = "AllowMtlsApiGatewayInvoke"
47+
action = "lambda:InvokeFunction"
48+
function_name = module.search-document-references-fhir-lambda.lambda_arn
49+
principal = "apigateway.amazonaws.com"
50+
# The "/*/*" portion grants access from any method on any resource
51+
# within the API Gateway REST API.
52+
source_arn = "${aws_api_gateway_rest_api.ndr_doc_store_api_mtls.execution_arn}/*/*"
53+
}

infrastructure/route53.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,15 @@ module "route53_fargate_ui" {
1010
api_gateway_full_domain_name = aws_api_gateway_domain_name.custom_api_domain.regional_domain_name
1111
api_gateway_zone_id = aws_api_gateway_domain_name.custom_api_domain.regional_zone_id
1212
}
13+
14+
resource "aws_route53_record" "ndr_mtls_api_record" {
15+
name = aws_api_gateway_domain_name.custom_api_domain_mtls.domain_name
16+
type = "A"
17+
zone_id = module.route53_fargate_ui.zone_id
18+
19+
alias {
20+
name = aws_api_gateway_domain_name.custom_api_domain_mtls.regional_domain_name
21+
zone_id = aws_api_gateway_domain_name.custom_api_domain_mtls.regional_zone_id
22+
evaluate_target_health = true
23+
}
24+
}

infrastructure/variable.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ variable "truststore_bucket_name" {
6969
default = "ndr-truststore"
7070
}
7171

72+
variable "ca_pem_filename" {
73+
type = string
74+
description = "Filename of the CA Truststore pem file stored in the core Truststore s3 bucket"
75+
default = "ndr-truststore.pem"
76+
}
77+
7278
# DynamoDB Table Variables
7379

7480
variable "pdm_dynamodb_table_name" {
@@ -232,6 +238,9 @@ locals {
232238
current_account_id = data.aws_caller_identity.current.account_id
233239

234240
apim_api_url = "https://${var.apim_environment}api.service.nhs.uk/national-document-repository"
241+
242+
truststore_bucket_id = local.is_sandbox ? "ndr-dev-${var.truststore_bucket_name}" : module.ndr-truststore[0].bucket_id
243+
truststore_uri = "s3://${local.truststore_bucket_id}/${var.ca_pem_filename}"
235244
}
236245

237246
variable "nrl_api_endpoint_suffix" {

0 commit comments

Comments
 (0)