Skip to content

Commit 08e93c3

Browse files
committed
feat: FTRS-3063 Add cloudtrail for s3 data events
1 parent a4035e1 commit 08e93c3

File tree

8 files changed

+165
-0
lines changed

8 files changed

+165
-0
lines changed

infrastructure/account_wide.tfvars

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ hec_acknowledgment_timeout = 300
4848
hec_endpoint_type = "Raw"
4949
retry_duration = 300
5050
s3_backup_mode = "FailedEventsOnly"
51+
52+
cloudtrail_log_retention_days = 30

infrastructure/environments/int/account_wide.tfvars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ single_nat_gateway = true
2424
one_nat_gateway_per_az = false
2525

2626
regional_waf_log_group_retention_days = 30
27+
cloudtrail_log_retention_days = 90

infrastructure/environments/prod/account_wide.tfvars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ single_nat_gateway = false
2525
one_nat_gateway_per_az = true
2626

2727
regional_waf_log_group_retention_days = 90
28+
cloudtrail_log_retention_days = 365

infrastructure/environments/ref/account_wide.tfvars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ single_nat_gateway = false
2424
one_nat_gateway_per_az = true
2525

2626
regional_waf_log_group_retention_days = 90
27+
cloudtrail_log_retention_days = 180
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# CloudTrail trail logging S3 object-level data events
2+
resource "aws_cloudtrail" "s3_data_events" {
3+
# checkov:skip=CKV_AWS_252: Justification: No CMK required by design; default SSE-S3 encryption is sufficient.
4+
# checkov:skip=CKV2_AWS_10: Justification: CloudWatch Logs integration is not required for this S3 data events trail.
5+
name = "${local.resource_prefix}-${var.cloudtrail_trail_name}"
6+
7+
s3_bucket_name = module.cloudtrail_s3_bucket.s3_bucket_id
8+
9+
# Regional, non-aggregated trail (same region delivery, no multi-region)
10+
is_multi_region_trail = false
11+
include_global_service_events = false
12+
13+
# Requirement: log file validation
14+
enable_log_file_validation = true
15+
16+
# S3 object-level write events — satisfies [S3.22]
17+
advanced_event_selector {
18+
name = "Log S3 object-level write events (S3.22)"
19+
20+
field_selector {
21+
field = "eventCategory"
22+
equals = ["Data"]
23+
}
24+
25+
field_selector {
26+
field = "resources.type"
27+
equals = ["AWS::S3::Object"]
28+
}
29+
30+
field_selector {
31+
field = "readOnly"
32+
equals = ["false"]
33+
}
34+
35+
# Exclude the CloudTrail delivery bucket itself to prevent a feedback loop
36+
field_selector {
37+
field = "resources.ARN"
38+
not_starts_with = ["${module.cloudtrail_s3_bucket.s3_bucket_arn}/"]
39+
}
40+
}
41+
42+
# S3 object-level read events — satisfies [S3.23]
43+
advanced_event_selector {
44+
name = "Log S3 object-level read events (S3.23)"
45+
46+
field_selector {
47+
field = "eventCategory"
48+
equals = ["Data"]
49+
}
50+
51+
field_selector {
52+
field = "resources.type"
53+
equals = ["AWS::S3::Object"]
54+
}
55+
56+
field_selector {
57+
field = "readOnly"
58+
equals = ["true"]
59+
}
60+
61+
# Exclude the CloudTrail delivery bucket itself to prevent a feedback loop
62+
field_selector {
63+
field = "resources.ARN"
64+
not_starts_with = ["${module.cloudtrail_s3_bucket.s3_bucket_arn}/"]
65+
}
66+
}
67+
}

infrastructure/stacks/account_wide/data.tf

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,47 @@ data "aws_iam_policy_document" "regional_waf_log_group_policy_document" {
6666
data "aws_prefix_list" "s3" {
6767
name = "com.amazonaws.${var.aws_region}.s3"
6868
}
69+
70+
data "aws_iam_policy_document" "cloudtrail_s3_bucket_policy" {
71+
statement {
72+
sid = "AWSCloudTrailAclCheck"
73+
74+
principals {
75+
type = "Service"
76+
identifiers = ["cloudtrail.amazonaws.com"]
77+
}
78+
79+
actions = ["s3:GetBucketAcl"]
80+
resources = ["_S3_BUCKET_ARN_"]
81+
82+
condition {
83+
test = "StringEquals"
84+
variable = "aws:SourceArn"
85+
values = ["arn:aws:cloudtrail:${var.aws_region}:${local.account_id}:trail/${local.resource_prefix}-${var.cloudtrail_trail_name}"]
86+
}
87+
}
88+
89+
statement {
90+
sid = "AWSCloudTrailWrite"
91+
92+
principals {
93+
type = "Service"
94+
identifiers = ["cloudtrail.amazonaws.com"]
95+
}
96+
97+
actions = ["s3:PutObject"]
98+
resources = ["_S3_BUCKET_ARN_/AWSLogs/${local.account_id}/*"]
99+
100+
condition {
101+
test = "StringEquals"
102+
variable = "s3:x-amz-acl"
103+
values = ["bucket-owner-full-control"]
104+
}
105+
106+
condition {
107+
test = "StringEquals"
108+
variable = "aws:SourceArn"
109+
values = ["arn:aws:cloudtrail:${var.aws_region}:${local.account_id}:trail/${local.resource_prefix}-${var.cloudtrail_trail_name}"]
110+
}
111+
}
112+
}

infrastructure/stacks/account_wide/s3.tf

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,28 @@ module "firehose_backup_s3" {
142142
enable_kms_encryption = var.enable_firehose_s3_kms_encryption
143143
s3_logging_bucket = local.s3_logging_bucket
144144
}
145+
146+
# S3 bucket to receive CloudTrail log deliveries
147+
module "cloudtrail_s3_bucket" {
148+
source = "../../modules/s3"
149+
bucket_name = "${local.resource_prefix}-${var.cloudtrail_bucket_name}"
150+
force_destroy = var.cloudtrail_bucket_force_destroy
151+
152+
s3_logging_bucket = local.s3_logging_bucket
153+
154+
attach_policy = true
155+
policy = data.aws_iam_policy_document.cloudtrail_s3_bucket_policy.json
156+
157+
lifecycle_rule_inputs = [
158+
{
159+
id = "delete_logs_older_than_${var.cloudtrail_log_retention_days}_days"
160+
enabled = true
161+
filter = {
162+
prefix = ""
163+
}
164+
expiration = {
165+
days = var.cloudtrail_log_retention_days
166+
}
167+
}
168+
]
169+
}

infrastructure/stacks/account_wide/variables.tf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,3 +327,27 @@ variable "performance_ec2_log_group_retention_days" {
327327
type = number
328328
default = 30
329329
}
330+
331+
variable "cloudtrail_trail_name" {
332+
description = "The name suffix for the S3 data events CloudTrail trail"
333+
type = string
334+
default = "s3-data-events"
335+
}
336+
337+
variable "cloudtrail_bucket_name" {
338+
description = "The name suffix for the S3 bucket used to store CloudTrail logs"
339+
type = string
340+
default = "cloudtrail-logs"
341+
}
342+
343+
variable "cloudtrail_log_retention_days" {
344+
description = "Number of days to retain CloudTrail logs in S3 before expiry"
345+
type = number
346+
default = 365
347+
}
348+
349+
variable "cloudtrail_bucket_force_destroy" {
350+
description = "Whether to allow force-destroying the CloudTrail S3 bucket on stack deletion"
351+
type = bool
352+
default = false
353+
}

0 commit comments

Comments
 (0)