Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions infrastructure/stacks/api-layer/cloudtrail.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
resource "aws_cloudtrail" "data_events_trail" {
name = "${var.project_name}-${var.environment}-data-events-trail"
s3_bucket_name = module.s3_cloudtrail_bucket.storage_bucket_name
kms_key_id = aws_kms_key.cloudtrail_kms_key.arn
include_global_service_events = true
is_multi_region_trail = false
enable_log_file_validation = true

cloud_watch_logs_role_arn = aws_iam_role.cloudtrail_cloudwatch_role.arn
cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudtrail_log_group.arn}:*"

event_selector {
read_write_type = "ReadOnly"
include_management_events = false

data_resource {
type = "AWS::DynamoDB::Table"
values = [module.eligibility_status_table.arn]
}
}
}

resource "aws_kms_key" "cloudtrail_kms_key" {
description = "KMS key for CloudTrail log file encryption"
deletion_window_in_days = 14
enable_key_rotation = true

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "EnableRootPermissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "AllowCloudTrailEncryptLogs"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
Action = [
"kms:GenerateDataKey*",
"kms:DescribeKey",
"kms:Encrypt"
]
Resource = "*"
}
]
})

tags = {
environment = var.environment
project_name = var.project_name
stack_name = local.stack_name
workspace = terraform.workspace
}

}

# KMS key alias
resource "aws_kms_alias" "cloudtrail_kms_alias" {
name = "alias/${var.project_name}-${var.environment}-cloudtrail-cmk"
target_key_id = aws_kms_key.cloudtrail_kms_key.key_id
}
7 changes: 7 additions & 0 deletions infrastructure/stacks/api-layer/cloudwatch.tf
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,10 @@ resource "aws_cloudwatch_log_group" "rotation_sfn_logs" {
kms_key_id = module.secrets_manager.rotation_sns_key_arn
retention_in_days = 365
}

# CloudWatch Log Group for CloudTrail
resource "aws_cloudwatch_log_group" "cloudtrail_log_group" {
name = "${terraform.workspace == "default" ? "" : "${terraform.workspace}-"}elid-aws-cloudtrail-logs"
retention_in_days = 365
kms_key_id = aws_kms_key.cloudtrail_kms_key.arn
}
9 changes: 9 additions & 0 deletions infrastructure/stacks/api-layer/cloudwatch_alarms.tf
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ locals {
alarm_description = "Multiple Lambda function changes detected within 10 minutes"
actions_enabled = true
}
DynamoDBTableReadOutsideLambdaRole = {
threshold = 1
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = 1
period = 300
statistic = "Sum"
alarm_description = "DynamoDB table read detected from non-Lambda execution role"
actions_enabled = true
}
}

# API Gateway alarm configuration
Expand Down
6 changes: 6 additions & 0 deletions infrastructure/stacks/api-layer/cloudwatch_metrics.tf
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ locals {
filter = "{($.eventSource=lambda.amazonaws.com) && (($.eventName=CreateFunction20150331) || ($.eventName=DeleteFunction20150331) || ($.eventName=UpdateFunctionCode20150331) || ($.eventName=UpdateFunctionConfiguration20150331))}"
log_group_name = "NHSDAudit_trail_log_group"
},
{
name = "DynamoDBTableReadOutsideLambdaRole"
namespace = "security"
filter = "{($.eventSource=dynamodb.amazonaws.com) && (($.eventName=GetItem) || ($.eventName=Query) || ($.eventName=Scan) || ($.eventName=BatchGetItem) || ($.eventName=BatchWriteItem)) && ($.requestParameters.tableName=\"${module.eligibility_status_table.table_name}\") && ($.userIdentity.sessionContext.sessionIssuer.arn != \"${aws_iam_role.eligibility_lambda_role.arn}\")}"
log_group_name = aws_cloudwatch_log_group.cloudtrail_log_group.name
},
]
}

Expand Down
82 changes: 82 additions & 0 deletions infrastructure/stacks/api-layer/iam_policies.tf
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,85 @@ resource "aws_iam_role_policy" "external_s3_kms_access_policy" {
role = aws_iam_role.write_access_role[count.index].id
policy = data.aws_iam_policy_document.s3_dq_kms_access_policy.json
}


##################################
# Cloudtrail Bucket & KMS Policies
##################################

# S3 Cloudtrail bucket policy
data "aws_iam_policy_document" "s3_cloudtrail_bucket_policy" {
statement {
sid = "AllowSSLRequestsOnly"
actions = [
"s3:*"
]
effect = "Deny"
resources = [
module.s3_cloudtrail_bucket.storage_bucket_arn,
"${module.s3_cloudtrail_bucket.storage_bucket_arn}/*",
]
principals {
type = "*"
identifiers = ["*"]
}
condition {
test = "Bool"
values = ["false"]
variable = "aws:SecureTransport"
}
}
}

# Attach s3 Cloudtrail bucket policy to Cloudtrail role
resource "aws_s3_bucket_policy" "s3_cloudtrail_bucket_policy" {
bucket = module.s3_cloudtrail_bucket.storage_bucket_id
policy = data.aws_iam_policy_document.s3_cloudtrail_bucket_policy.json
}

# S3 Cloudtrail bucket KMS access policy
data "aws_iam_policy_document" "s3_cloudtrail_kms_access_policy" {
statement {
actions = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
]
resources = [
module.s3_cloudtrail_bucket.storage_bucket_kms_key_arn
]
}
}

# Attach S3 Cloudtrail bucket KMS policy to Cloudtrail role
resource "aws_iam_role_policy" "s3_cloudtrail_kms_access_policy" {
name = "S3CloudTrailKMSAccess"
role = aws_iam_role.cloudtrail_cloudwatch_role.id
policy = data.aws_iam_policy_document.s3_cloudtrail_kms_access_policy.json
}

# CloudWatch Logs permissions policy for CloudTrail
data "aws_iam_policy_document" "cloudtrail_cloudwatch_policy" {
statement {
effect = "Allow"
actions = [
"logs:PutLogEvents",
"logs:CreateLogGroup",
"logs:CreateLogStream"
]
resources = [
aws_cloudwatch_log_group.cloudtrail_log_group.arn,
"${aws_cloudwatch_log_group.cloudtrail_log_group.arn}:*"
]

}
}

# Attach CloudTrail CloudWatch Logs policy to CloudTrail role
resource "aws_iam_role_policy" "cloudtrail_cloudwatch_policy" {
name = "CloudTrailCloudWatchLogsAccess"
role = aws_iam_role.cloudtrail_cloudwatch_role.id
policy = data.aws_iam_policy_document.cloudtrail_cloudwatch_policy.json
}
20 changes: 20 additions & 0 deletions infrastructure/stacks/api-layer/iam_roles.tf
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,23 @@ resource "aws_iam_role_policy_attachment" "rotation_vpc_access" {
role = aws_iam_role.rotation_lambda_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}

# IAM role for CloudTrail to write to CloudWatch Logs
resource "aws_iam_role" "cloudtrail_cloudwatch_role" {
name = "cloudtrail-cloudwatch-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "cloudtrail.amazonaws.com"
}
}
]
})
permissions_boundary = aws_iam_policy.assumed_role_permissions_boundary.arn
}

9 changes: 9 additions & 0 deletions infrastructure/stacks/api-layer/s3_buckets.tf
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,12 @@ module "s3_dq_metrics_bucket" {
stack_name = local.stack_name
workspace = terraform.workspace
}

module "s3_cloudtrail_bucket" {
source = "../../modules/s3"
bucket_name = "eli-cloudwatch-logs"
environment = var.environment
project_name = var.project_name
stack_name = local.stack_name
workspace = terraform.workspace
}