Skip to content

Commit a7eb892

Browse files
authored
Import the two CloudTrail trails and their log buckets (#193)
1 parent 1aac2c7 commit a7eb892

2 files changed

Lines changed: 283 additions & 0 deletions

File tree

‎terraform/cloudtrail.tf‎

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// Adopts the account's two CloudTrail trails and the two S3 buckets they deliver to.
2+
// All four predate any Terraform in this repo and were console-configured; audit logging
3+
// is a security control, so it should not live only as console state.
4+
// See hackforla/devops-security#191.
5+
//
6+
// The two trails are complementary, not redundant. With advanced event selectors a trail
7+
// logs only what its selectors name, and devops-security-tf-backend-logs has no Management
8+
// selector at all -- it watches S3 data events on the hfla-ops-terraform-state backend
9+
// bucket and nothing else, while management-events carries the account's management
10+
// events. Neither is an organization trail, so neither covers the payer account
11+
// 504695638931.
12+
//
13+
// This file adopts what is there as-is. Four things are missing from both trails and are
14+
// deliberately NOT added here, because each changes live behaviour and belongs in its own
15+
// ticket: KMS encryption (both use SSE-S3), a CloudWatch Logs destination (so there is no
16+
// alerting path off either trail), a bucket lifecycle configuration (logs accumulate
17+
// indefinitely), and the disagreement over log file validation, which is reproduced below
18+
// rather than resolved.
19+
//
20+
// A bucket is not one resource. Since AWS provider v4 the policy, public access block,
21+
// encryption and ownership controls are each their own resource with their own import --
22+
// the same trap terraform/import.tf in hackforla/incubator documents.
23+
24+
// ---------------------------------------------------------------------------
25+
// devops-security-tf-backend-logs -- S3 data events on the Terraform backend bucket
26+
// ---------------------------------------------------------------------------
27+
28+
resource "aws_s3_bucket" "tf_backend_logs" {
29+
bucket = "aws-cloudtrail-logs-035866691871-4b8654bf"
30+
}
31+
32+
// Reproduced verbatim from the live bucket. These two statements are what let CloudTrail
33+
// deliver to the bucket at all -- if this policy drifts on apply, log delivery stops and
34+
// nothing alerts; the trail simply stops writing.
35+
//
36+
// Note the condition key is spelled "aws:SourceArn" here and "AWS:SourceArn" on the
37+
// management-events bucket below. IAM condition keys are case-insensitive, so the two are
38+
// equivalent to AWS, and both are left exactly as stored. Normalising them would be a
39+
// change to a live policy for no behavioural gain.
40+
resource "aws_s3_bucket_policy" "tf_backend_logs" {
41+
bucket = aws_s3_bucket.tf_backend_logs.id
42+
43+
policy = jsonencode({
44+
Version = "2012-10-17"
45+
Statement = [
46+
{
47+
Sid = "AWSCloudTrailAclCheck20150319"
48+
Effect = "Allow"
49+
Principal = { Service = "cloudtrail.amazonaws.com" }
50+
Action = "s3:GetBucketAcl"
51+
Resource = "arn:aws:s3:::aws-cloudtrail-logs-035866691871-4b8654bf"
52+
Condition = {
53+
StringEquals = {
54+
"aws:SourceArn" = "arn:aws:cloudtrail:us-west-2:035866691871:trail/devops-security-tf-backend-logs"
55+
}
56+
}
57+
},
58+
{
59+
Sid = "AWSCloudTrailWrite20150319"
60+
Effect = "Allow"
61+
Principal = { Service = "cloudtrail.amazonaws.com" }
62+
Action = "s3:PutObject"
63+
Resource = "arn:aws:s3:::aws-cloudtrail-logs-035866691871-4b8654bf/AWSLogs/035866691871/*"
64+
Condition = {
65+
StringEquals = {
66+
"s3:x-amz-acl" = "bucket-owner-full-control"
67+
"aws:SourceArn" = "arn:aws:cloudtrail:us-west-2:035866691871:trail/devops-security-tf-backend-logs"
68+
}
69+
}
70+
}
71+
]
72+
})
73+
}
74+
75+
resource "aws_s3_bucket_public_access_block" "tf_backend_logs" {
76+
bucket = aws_s3_bucket.tf_backend_logs.id
77+
78+
block_public_acls = true
79+
block_public_policy = true
80+
ignore_public_acls = true
81+
restrict_public_buckets = true
82+
}
83+
84+
// blocked_encryption_types is deliberately not set. The provider reads it back as
85+
// ["SSE-C"] on both buckets but it is computed, so leaving it out produces no diff.
86+
resource "aws_s3_bucket_server_side_encryption_configuration" "tf_backend_logs" {
87+
bucket = aws_s3_bucket.tf_backend_logs.id
88+
89+
rule {
90+
apply_server_side_encryption_by_default {
91+
sse_algorithm = "AES256"
92+
}
93+
bucket_key_enabled = false
94+
}
95+
}
96+
97+
// Only this bucket has ownership controls. The management-events bucket has none, and no
98+
// equivalent resource is declared for it below -- declaring one would create it rather
99+
// than import it, which is a live change.
100+
resource "aws_s3_bucket_ownership_controls" "tf_backend_logs" {
101+
bucket = aws_s3_bucket.tf_backend_logs.id
102+
103+
rule {
104+
object_ownership = "BucketOwnerEnforced"
105+
}
106+
}
107+
108+
// The `project` tag already exists on this trail and has to be declared here. Without it
109+
// the provider's default_tags would plan to REMOVE it, which is why this is the one
110+
// resource in this file carrying a tags block.
111+
resource "aws_cloudtrail" "tf_backend_logs" {
112+
name = "devops-security-tf-backend-logs"
113+
s3_bucket_name = aws_s3_bucket.tf_backend_logs.id
114+
include_global_service_events = true
115+
is_multi_region_trail = true
116+
enable_log_file_validation = true
117+
118+
advanced_event_selector {
119+
field_selector {
120+
field = "eventCategory"
121+
equals = ["Data"]
122+
}
123+
field_selector {
124+
field = "resources.ARN"
125+
equals = ["arn:aws:s3:::hfla-ops-terraform-state/"]
126+
}
127+
field_selector {
128+
field = "resources.type"
129+
equals = ["AWS::S3::Object"]
130+
}
131+
}
132+
133+
tags = {
134+
project = "devops-security"
135+
}
136+
}
137+
138+
// ---------------------------------------------------------------------------
139+
// management-events -- the account's management events
140+
// ---------------------------------------------------------------------------
141+
142+
resource "aws_s3_bucket" "management_events" {
143+
bucket = "aws-cloudtrail-logs-035866691871-6539ef03"
144+
}
145+
146+
// Verbatim from the live bucket, including the "AWS:SourceArn" spelling -- see the note on
147+
// the policy above.
148+
resource "aws_s3_bucket_policy" "management_events" {
149+
bucket = aws_s3_bucket.management_events.id
150+
151+
policy = jsonencode({
152+
Version = "2012-10-17"
153+
Statement = [
154+
{
155+
Sid = "AWSCloudTrailAclCheck20150319"
156+
Effect = "Allow"
157+
Principal = { Service = "cloudtrail.amazonaws.com" }
158+
Action = "s3:GetBucketAcl"
159+
Resource = "arn:aws:s3:::aws-cloudtrail-logs-035866691871-6539ef03"
160+
Condition = {
161+
StringEquals = {
162+
"AWS:SourceArn" = "arn:aws:cloudtrail:us-west-2:035866691871:trail/management-events"
163+
}
164+
}
165+
},
166+
{
167+
Sid = "AWSCloudTrailWrite20150319"
168+
Effect = "Allow"
169+
Principal = { Service = "cloudtrail.amazonaws.com" }
170+
Action = "s3:PutObject"
171+
Resource = "arn:aws:s3:::aws-cloudtrail-logs-035866691871-6539ef03/AWSLogs/035866691871/*"
172+
Condition = {
173+
StringEquals = {
174+
"AWS:SourceArn" = "arn:aws:cloudtrail:us-west-2:035866691871:trail/management-events"
175+
"s3:x-amz-acl" = "bucket-owner-full-control"
176+
}
177+
}
178+
}
179+
]
180+
})
181+
}
182+
183+
resource "aws_s3_bucket_public_access_block" "management_events" {
184+
bucket = aws_s3_bucket.management_events.id
185+
186+
block_public_acls = true
187+
block_public_policy = true
188+
ignore_public_acls = true
189+
restrict_public_buckets = true
190+
}
191+
192+
resource "aws_s3_bucket_server_side_encryption_configuration" "management_events" {
193+
bucket = aws_s3_bucket.management_events.id
194+
195+
rule {
196+
apply_server_side_encryption_by_default {
197+
sse_algorithm = "AES256"
198+
}
199+
bucket_key_enabled = false
200+
}
201+
}
202+
203+
// Log file validation is off on this trail and on for the other. That inconsistency is
204+
// reproduced rather than resolved -- turning it on is a live change and its own ticket.
205+
resource "aws_cloudtrail" "management_events" {
206+
name = "management-events"
207+
s3_bucket_name = aws_s3_bucket.management_events.id
208+
include_global_service_events = true
209+
is_multi_region_trail = true
210+
enable_log_file_validation = false
211+
212+
advanced_event_selector {
213+
name = "Management events selector"
214+
215+
field_selector {
216+
field = "eventCategory"
217+
equals = ["Management"]
218+
}
219+
}
220+
}

‎terraform/imports.tf‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,66 @@ import {
1111
to = aws_iam_group_policy_attachment.manageAccessKeys
1212
id = "ops-leads/arn:aws:iam::035866691871:policy/ManageAccessKeys"
1313
}
14+
15+
// Adopts the two CloudTrail trails and their two log buckets. See
16+
// hackforla/devops-security#191 and the commentary in cloudtrail.tf.
17+
//
18+
// A trail imports by its full ARN, NOT by its name. The name fails against the pinned
19+
// provider with `could not parse import ID "management-events" as ARN: arn: invalid
20+
// prefix`. Every S3 resource below imports by bucket name, including the policy, public
21+
// access block, encryption and ownership controls, each of which is its own resource.
22+
23+
import {
24+
to = aws_cloudtrail.tf_backend_logs
25+
id = "arn:aws:cloudtrail:us-west-2:035866691871:trail/devops-security-tf-backend-logs"
26+
}
27+
28+
import {
29+
to = aws_s3_bucket.tf_backend_logs
30+
id = "aws-cloudtrail-logs-035866691871-4b8654bf"
31+
}
32+
33+
import {
34+
to = aws_s3_bucket_policy.tf_backend_logs
35+
id = "aws-cloudtrail-logs-035866691871-4b8654bf"
36+
}
37+
38+
import {
39+
to = aws_s3_bucket_public_access_block.tf_backend_logs
40+
id = "aws-cloudtrail-logs-035866691871-4b8654bf"
41+
}
42+
43+
import {
44+
to = aws_s3_bucket_server_side_encryption_configuration.tf_backend_logs
45+
id = "aws-cloudtrail-logs-035866691871-4b8654bf"
46+
}
47+
48+
import {
49+
to = aws_s3_bucket_ownership_controls.tf_backend_logs
50+
id = "aws-cloudtrail-logs-035866691871-4b8654bf"
51+
}
52+
53+
import {
54+
to = aws_cloudtrail.management_events
55+
id = "arn:aws:cloudtrail:us-west-2:035866691871:trail/management-events"
56+
}
57+
58+
import {
59+
to = aws_s3_bucket.management_events
60+
id = "aws-cloudtrail-logs-035866691871-6539ef03"
61+
}
62+
63+
import {
64+
to = aws_s3_bucket_policy.management_events
65+
id = "aws-cloudtrail-logs-035866691871-6539ef03"
66+
}
67+
68+
import {
69+
to = aws_s3_bucket_public_access_block.management_events
70+
id = "aws-cloudtrail-logs-035866691871-6539ef03"
71+
}
72+
73+
import {
74+
to = aws_s3_bucket_server_side_encryption_configuration.management_events
75+
id = "aws-cloudtrail-logs-035866691871-6539ef03"
76+
}

0 commit comments

Comments
 (0)