Skip to content

Commit 1854a2d

Browse files
[NDR-55] Virus Scanning Terraform Implementation (#300)
1 parent 0537817 commit 1854a2d

File tree

10 files changed

+170
-5
lines changed

10 files changed

+170
-5
lines changed

infrastructure/README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
| Name | Version |
44
|------|---------|
55
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
6+
| <a name="requirement_awscc"></a> [awscc](#requirement\_awscc) | ~> 1.0 |
67

78
## Providers
89

910
| Name | Version |
1011
|------|---------|
11-
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.84.0 |
12+
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.97.0 |
1213

1314
## Modules
1415

@@ -40,6 +41,7 @@
4041
| <a name="module_bulk-upload-report-lambda"></a> [bulk-upload-report-lambda](#module\_bulk-upload-report-lambda) | ./modules/lambda | n/a |
4142
| <a name="module_bulk_upload_metadata_preprocessor_lambda"></a> [bulk\_upload\_metadata\_preprocessor\_lambda](#module\_bulk\_upload\_metadata\_preprocessor\_lambda) | ./modules/lambda | n/a |
4243
| <a name="module_bulk_upload_report_dynamodb_table"></a> [bulk\_upload\_report\_dynamodb\_table](#module\_bulk\_upload\_report\_dynamodb\_table) | ./modules/dynamo_db | n/a |
44+
| <a name="module_cloud_storage_security"></a> [cloud\_storage\_security](#module\_cloud\_storage\_security) | cloudstoragesec/cloud-storage-security/aws | 1.7.1+css8.07.002 |
4345
| <a name="module_cloudfront-distribution-lg"></a> [cloudfront-distribution-lg](#module\_cloudfront-distribution-lg) | ./modules/cloudfront | n/a |
4446
| <a name="module_cloudfront_edge_dynamodb_table"></a> [cloudfront\_edge\_dynamodb\_table](#module\_cloudfront\_edge\_dynamodb\_table) | ./modules/dynamo_db | n/a |
4547
| <a name="module_cloudfront_firewall_waf_v2"></a> [cloudfront\_firewall\_waf\_v2](#module\_cloudfront\_firewall\_waf\_v2) | ./modules/firewall_waf_v2 | n/a |
@@ -306,6 +308,9 @@
306308
| [aws_lambda_permission.data_collection_schedule_permission](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
307309
| [aws_lambda_permission.nhs_oauth_token_generator_schedule](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
308310
| [aws_lambda_permission.statistical_report_schedule_permission](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
311+
| [aws_route_table.virus_scanning_route_table](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
312+
| [aws_route_table_association.virus_scanning_subnet1_route_table_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
313+
| [aws_route_table_association.virus_scanning_subnet2_route_table_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
309314
| [aws_rum_app_monitor.ndr](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rum_app_monitor) | resource |
310315
| [aws_s3_bucket.access_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
311316
| [aws_s3_bucket.logs_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
@@ -322,7 +327,11 @@
322327
| [aws_scheduler_schedule.data_collection_ecs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/scheduler_schedule) | resource |
323328
| [aws_sns_topic.alarm_notifications_topic](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
324329
| [aws_sns_topic_subscription.alarm_notifications_sns_topic_subscription](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
330+
| [aws_sns_topic_subscription.proactive_notifications_sns_topic_subscription](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
325331
| [aws_sqs_queue_policy.mns_sqs_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource |
332+
| [aws_ssm_parameter.virus_scan_notifications_sns_topic_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
333+
| [aws_subnet.virus_scanning_subnet1](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
334+
| [aws_subnet.virus_scanning_subnet2](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
326335
| [aws_wafv2_web_acl_association.web_acl_association](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_association) | resource |
327336
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
328337
| [aws_elb_service_account.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/elb_service_account) | data source |
@@ -338,11 +347,13 @@
338347
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
339348
| [aws_ssm_parameter.apim_url](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
340349
| [aws_ssm_parameter.backup_target_account](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
350+
| [aws_ssm_parameter.cloud_security_admin_email](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
341351
| [aws_ssm_parameter.cloud_security_notification_email_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
342352
| [aws_ssm_parameter.end_user_ods_code](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
343353
| [aws_ssm_parameter.mns_lambda_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
344354
| [aws_ssm_parameter.splunk_trusted_principal](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
345355
| [aws_ssm_parameter.target_backup_vault_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
356+
| [aws_ssm_parameter.virus_scanning_subnet_cidr_range](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
346357

347358
## Inputs
348359

@@ -357,6 +368,9 @@
357368
| <a name="input_certificate_domain"></a> [certificate\_domain](#input\_certificate\_domain) | n/a | `string` | n/a | yes |
358369
| <a name="input_certificate_subdomain_name_prefix"></a> [certificate\_subdomain\_name\_prefix](#input\_certificate\_subdomain\_name\_prefix) | Prefix to add to subdomains on certification configurations, dev envs use api-{env}, prod envs use api.{env} | `string` | `"api-"` | no |
359370
| <a name="input_cloud_only_service_instances"></a> [cloud\_only\_service\_instances](#input\_cloud\_only\_service\_instances) | n/a | `number` | `1` | no |
371+
| <a name="input_cloud_security_console_black_hole_address"></a> [cloud\_security\_console\_black\_hole\_address](#input\_cloud\_security\_console\_black\_hole\_address) | Using reserved address that does not lead anywhere to make sure CloudStorageSecurity console is not available | `string` | `"198.51.100.0/24"` | no |
372+
| <a name="input_cloud_security_console_public_address"></a> [cloud\_security\_console\_public\_address](#input\_cloud\_security\_console\_public\_address) | Using public address to make sure CloudStorageSecurity console is available | `string` | `"0.0.0.0/0"` | no |
373+
| <a name="input_cloud_security_email_param_environment"></a> [cloud\_security\_email\_param\_environment](#input\_cloud\_security\_email\_param\_environment) | This is the environment reference in cloud security email param store key | `string` | n/a | yes |
360374
| <a name="input_cloudfront_edge_table_name"></a> [cloudfront\_edge\_table\_name](#input\_cloudfront\_edge\_table\_name) | The name of the dynamodb table to store the presigned url reference of CloudFront requests | `string` | `"CloudFrontEdgeReference"` | no |
361375
| <a name="input_cloudwatch_alarm_evaluation_periods"></a> [cloudwatch\_alarm\_evaluation\_periods](#input\_cloudwatch\_alarm\_evaluation\_periods) | n/a | `any` | n/a | yes |
362376
| <a name="input_docstore_bucket_name"></a> [docstore\_bucket\_name](#input\_docstore\_bucket\_name) | The name of the S3 bucket to store ARF documents | `string` | `"ndr-document-store"` | no |

infrastructure/dev.tfvars

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ poll_frequency = "3600"
1010
standalone_vpc_tag = "ndr-dev"
1111
standalone_vpc_ig_tag = "ndr-dev"
1212

13-
apim_environment = "internal-dev."
13+
cloud_security_email_param_environment = "dev"
14+
15+
apim_environment = "internal-dev."

infrastructure/main.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ terraform {
66
source = "hashicorp/aws"
77
version = "~> 5.0"
88
}
9+
awscc = {
10+
source = "hashicorp/awscc"
11+
version = "~> 1.0"
12+
}
913
}
1014
backend "s3" {
1115
dynamodb_table = "ndr-terraform-locks"
@@ -18,6 +22,10 @@ provider "aws" {
1822
region = "eu-west-2"
1923
}
2024

25+
provider "awscc" {
26+
region = "eu-west-2"
27+
}
28+
2129
provider "aws" {
2230
alias = "us_east_1"
2331
region = "us-east-1"

infrastructure/modules/vpc/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ No modules.
6262

6363
| Name | Description |
6464
|------|-------------|
65+
| <a name="output_internet_gateway_id"></a> [internet\_gateway\_id](#output\_internet\_gateway\_id) | n/a |
6566
| <a name="output_private_subnets"></a> [private\_subnets](#output\_private\_subnets) | n/a |
6667
| <a name="output_public_subnets"></a> [public\_subnets](#output\_public\_subnets) | n/a |
6768
| <a name="output_vpc_id"></a> [vpc\_id](#output\_vpc\_id) | n/a |

infrastructure/modules/vpc/output.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ output "vpc_id" {
22
value = local.is_production ? aws_vpc.vpc[0].id : data.aws_vpc.vpc[0].id
33
}
44

5+
output "internet_gateway_id" {
6+
value = local.is_production ? aws_internet_gateway.ig[0].id : data.aws_internet_gateway.ig[0].id
7+
}
8+
59
output "public_subnets" {
610
value = local.is_sandbox ? data.aws_subnet.public_subnets.*.id : aws_subnet.public_subnets.*.id
711
}

infrastructure/preprod.tfvars

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ poll_frequency = "60"
1010
standalone_vpc_tag = "ndr-pre-prod"
1111
standalone_vpc_ig_tag = "ndr-pre-prod"
1212

13-
apim_environment = "int."
13+
cloud_security_email_param_environment = "pre-prod"
14+
15+
apim_environment = "int."

infrastructure/prod.tfvars

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ poll_frequency = "60"
1010
standalone_vpc_tag = "ndr-prod"
1111
standalone_vpc_ig_tag = "ndr-prod"
1212

13-
apim_environment = ""
13+
cloud_security_email_param_environment = "prod"
14+
15+
apim_environment = ""

infrastructure/test.tfvars

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ poll_frequency = "10"
1010
standalone_vpc_tag = "ndr-test"
1111
standalone_vpc_ig_tag = "ndr-test"
1212

13-
apim_environment = "internal-qa."
13+
cloud_security_email_param_environment = "ndr-test"
14+
15+
apim_environment = "internal-qa."

infrastructure/variable.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,23 @@ locals {
198198

199199
variable "nrl_api_endpoint_suffix" {
200200
default = "api.service.nhs.uk/record-locator/producer/FHIR/R4/DocumentReference"
201+
}
202+
203+
# Virus scanner variables
204+
205+
variable "cloud_security_email_param_environment" {
206+
type = string
207+
description = "This is the environment reference in cloud security email param store key"
208+
}
209+
210+
variable "cloud_security_console_black_hole_address" {
211+
type = string
212+
default = "198.51.100.0/24"
213+
description = "Using reserved address that does not lead anywhere to make sure CloudStorageSecurity console is not available"
214+
}
215+
216+
variable "cloud_security_console_public_address" {
217+
type = string
218+
default = "0.0.0.0/0"
219+
description = "Using public address to make sure CloudStorageSecurity console is available"
201220
}

infrastructure/virusscanner.tf

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
locals {
2+
subnet_1_cidr_block = split(",", data.aws_ssm_parameter.virus_scanning_subnet_cidr_range.value)[0]
3+
subnet_2_cidr_block = split(",", data.aws_ssm_parameter.virus_scanning_subnet_cidr_range.value)[1]
4+
}
5+
6+
data "aws_ssm_parameter" "cloud_security_admin_email" {
7+
name = "/prs/${var.cloud_security_email_param_environment}/user-input/cloud-security-admin-email"
8+
}
9+
10+
data "aws_ssm_parameter" "virus_scanning_subnet_cidr_range" {
11+
name = "/prs/virus-scanner/subnet-cidr-range"
12+
}
13+
14+
resource "aws_subnet" "virus_scanning_a" {
15+
count = local.is_production ? 1 : 0
16+
17+
availability_zone = "eu-west-2a"
18+
vpc_id = module.ndr-vpc-ui.vpc_id
19+
cidr_block = local.subnet_1_cidr_block
20+
21+
tags = {
22+
Name = "Virus scanning subnet for eu-west-2a"
23+
Environment = var.environment
24+
Owner = var.owner
25+
}
26+
}
27+
28+
resource "aws_subnet" "virus_scanning_b" {
29+
count = local.is_production ? 1 : 0
30+
31+
availability_zone = "eu-west-2b"
32+
vpc_id = module.ndr-vpc-ui.vpc_id
33+
cidr_block = local.subnet_2_cidr_block
34+
35+
tags = {
36+
Name = "Virus scanning subnet for eu-west-2b"
37+
Environment = var.environment
38+
Owner = var.owner
39+
}
40+
}
41+
42+
resource "aws_route_table" "virus_scanning" {
43+
count = local.is_production ? 1 : 0
44+
45+
vpc_id = module.ndr-vpc-ui.vpc_id
46+
47+
route {
48+
cidr_block = "0.0.0.0/0"
49+
gateway_id = module.ndr-vpc-ui.internet_gateway_id
50+
}
51+
52+
tags = {
53+
Name = "Virus scanning route table"
54+
Environment = var.environment
55+
Owner = var.owner
56+
}
57+
}
58+
59+
resource "aws_route_table_association" "virus_scanning_a" {
60+
count = local.is_production ? 1 : 0
61+
62+
subnet_id = aws_subnet.virus_scanning_a[0].id
63+
route_table_id = aws_route_table.virus_scanning[0].id
64+
}
65+
66+
resource "aws_route_table_association" "virus_scanning_b" {
67+
count = local.is_production ? 1 : 0
68+
69+
subnet_id = aws_subnet.virus_scanning_b[0].id
70+
route_table_id = aws_route_table.virus_scanning[0].id
71+
}
72+
73+
module "cloud_storage_security" {
74+
count = local.is_production ? 1 : 0
75+
76+
source = "cloudstoragesec/cloud-storage-security/aws"
77+
version = "1.7.1+css8.07.002"
78+
cidr = [var.cloud_security_console_black_hole_address] # This is a reserved address that does not lead anywhere to make sure CloudStorageSecurity console is not available
79+
email = data.aws_ssm_parameter.cloud_security_admin_email.value
80+
subnet_a_id = aws_subnet.virus_scanning_a[0].id
81+
subnet_b_id = aws_subnet.virus_scanning_b[0].id
82+
vpc = module.ndr-vpc-ui.vpc_id
83+
min_running_agents = 0
84+
allow_access_to_all_kms_keys = false
85+
# num_messages_in_queue_scaling_threshold = 1 # Currently not exposed in the module, will need to be set in the SSM after the terraform has been run.
86+
# only_scan_when_queue_threshold_exceeded = true # Currently not exposed in the module, will need to be set in the SSM after the terraform has been run.
87+
custom_resource_tags = {
88+
Name = "Virus scanner for Repository"
89+
Environment = var.environment
90+
Owner = var.owner
91+
}
92+
}
93+
94+
resource "aws_ssm_parameter" "virus_scanning_notifications_sns_topic_arn" {
95+
count = local.is_production ? 1 : 0
96+
97+
name = "/prs/${var.environment}/virus-scan-notifications-sns-topic-arn"
98+
type = "String"
99+
value = module.cloud_storage_security[0].proactive_notifications_topic_arn
100+
}
101+
102+
resource "aws_sns_topic_subscription" "proactive_virus_scanning_notifications" {
103+
for_each = local.is_production ? toset(nonsensitive(split(",", data.aws_ssm_parameter.cloud_security_notification_email_list.value))) : []
104+
endpoint = each.value
105+
protocol = "email"
106+
topic_arn = module.cloud_storage_security[0].proactive_notifications_topic_arn
107+
filter_policy = jsonencode({
108+
"notificationType" : ["scanResult"],
109+
"scanResult" : ["Infected", "Error", "Unscannable", "Suspicious"]
110+
})
111+
}

0 commit comments

Comments
 (0)