Skip to content

Commit 3117d1a

Browse files
committed
feat: Configure malware proteciton using GuardDuty.
1 parent 9d5e9b1 commit 3117d1a

6 files changed

Lines changed: 167 additions & 4 deletions

File tree

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AWS S3 Uploads Bucket Module
22

3-
[![Main Checks][badge-checks]][code-checks] [![GitHub Release][badge-release]][latest-release]
3+
[![GitHub Release][badge-release]][latest-release]
44

55
This module creates an S3 bucket for file uploads. The bucket is configured with
66
logging, encryption, verisioning and a lifecycle configuration.
@@ -43,6 +43,7 @@ tofu init -upgrade
4343
| project | Project that these resources are supporting. This is used in the prefix to all resource names. | `string` | n/a | yes |
4444
| abort_incomplete_multipart_upload_days | Number of days to abort incomplete multipart uploads. | `number` | `7` | no |
4545
| allowed_principals | List of AWS principal ARNs to allow to use the KMS key. This is used to grant access to other resources that need to use the key, such as ECS task roles. | `list(string)` | `[]` | no |
46+
| enable_malware_protection | Whether to enable malware protection for the bucket using GuardDuty. This will create a new IAM role and GuardDuty malware protection plan. | `bool` | `true` | no |
4647
| encryption_key_arn | ARN of the KMS key to use for S3 bucket encryption. If not provided, a new KMS key will be created. | `string` | `null` | no |
4748
| environment | The environment for the deployment. This is used in the prefix to all resource names. | `string` | `"development"` | no |
4849
| force_delete | Whether to force delete the bucket and its contents. Must be set to `true` _and_ applied before the bucket can be deleted. | `bool` | `false` | no |
@@ -85,9 +86,7 @@ different storage classes, see the [Amazon S3 documentation][storage-class].
8586
Follow the [contributing guidelines][contributing] to contribute to this
8687
repository.
8788

88-
[badge-checks]: https://github.com/codeforamerica/tofu-modules-aws-s3-uploads-bucket/actions/workflows/main.yaml/badge.svg
8989
[badge-release]: https://img.shields.io/github/v/release/codeforamerica/tofu-modules-aws-s3-uploads-bucket?logo=github&label=Latest%20Release
90-
[code-checks]: https://github.com/codeforamerica/tofu-modules-aws-s3-uploads-bucket/actions/workflows/main.yaml
9190
[contributing]: CONTRIBUTING.md
9291
[latest-release]: https://github.com/codeforamerica/tofu-modules-aws-s3-uploads-bucket/releases/latest
9392
[storage-class]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/storage-class-intro.html

locals.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ locals {
22
bucket_name = join("-", [var.project, var.environment, var.name])
33
kms_key_arn = var.encryption_key_arn != null ? var.encryption_key_arn : aws_kms_key.bucket["this"].arn
44
logs_path = "/AWSLogs/${data.aws_caller_identity.identity.account_id}"
5-
tags = merge({ use : "file-uploads" }, var.tags)
5+
tags = merge({ use : "file-uploads" }, var.tags)
66
}

malware.tf

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
resource "aws_iam_role" "malware" {
2+
for_each = var.enable_malware_protection ? toset(["this"]) : toset([])
3+
4+
name = "${local.bucket_name}-malware"
5+
tags = local.tags
6+
7+
assume_role_policy = jsonencode(yamldecode(templatefile("${path.module}/templates/malware-trust-policy.yaml.tftpl", {
8+
account : data.aws_caller_identity.identity.account_id
9+
partition : data.aws_partition.current.partition
10+
region : data.aws_region.current.region
11+
})))
12+
13+
inline_policy {
14+
name = "${local.bucket_name}-malware"
15+
policy = jsonencode(yamldecode(templatefile("${path.module}/templates/malware-policy.yaml.tftpl", {
16+
account : data.aws_caller_identity.identity.account_id
17+
bucket_arn : module.this.arn
18+
kms_key_arn : local.kms_key_arn
19+
partition : data.aws_partition.current.partition
20+
region : data.aws_region.current.region
21+
})))
22+
}
23+
24+
}
25+
26+
resource "aws_guardduty_malware_protection_plan" "this" {
27+
for_each = var.enable_malware_protection ? toset(["this"]) : toset([])
28+
29+
role = aws_iam_role.malware["this"].arn
30+
tags = merge({ Name : local.bucket_name }, local.tags)
31+
32+
protected_resource {
33+
s3_bucket {
34+
bucket_name = module.this.bucket_name
35+
}
36+
}
37+
38+
actions {
39+
tagging {
40+
status = "ENABLED"
41+
}
42+
}
43+
44+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
Version: "2012-10-17"
2+
Statement:
3+
- Sid: AllowManagedRuleToSendS3EventsToGuardDuty
4+
Effect: Allow
5+
Action:
6+
- events:PutRule
7+
Resource:
8+
- arn:${partition}:events:${region}:${account}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*
9+
Condition:
10+
StringEquals:
11+
events:ManagedBy: malware-protection-plan.guardduty.amazonaws.com
12+
ForAllValues:StringEquals:
13+
events:source: aws.s3
14+
events:detail-type:
15+
- Object Created
16+
- AWS API Call via CloudTrail
17+
'Null':
18+
events:source: 'false'
19+
events:detail-type: 'false'
20+
- Sid: AllowUpdateTargetAndDeleteManagedRule
21+
Effect: Allow
22+
Action:
23+
- events:DeleteRule
24+
- events:PutTargets
25+
- events:RemoveTargets
26+
Resource:
27+
- arn:${partition}:events:${region}:${account}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*
28+
Condition:
29+
StringEquals:
30+
events:ManagedBy: malware-protection-plan.guardduty.amazonaws.com
31+
- Sid: AllowGuardDutyToMonitorEventBridgeManagedRule
32+
Effect: Allow
33+
Action:
34+
- events:DescribeRule
35+
- events:ListTargetsByRule
36+
Resource:
37+
- arn:${partition}:events:${region}:${account}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*
38+
- Sid: AllowEnableS3EventBridgeEvents
39+
Effect: Allow
40+
Action:
41+
- s3:PutBucketNotification
42+
- s3:GetBucketNotification
43+
Resource:
44+
- "${bucket_arn}"
45+
Condition:
46+
StringEquals:
47+
aws:ResourceAccount: "${account}"
48+
- Sid: AllowPostScanTag
49+
Effect: Allow
50+
Action:
51+
- s3:GetObjectTagging
52+
- s3:GetObjectVersionTagging
53+
- s3:PutObjectTagging
54+
- s3:PutObjectVersionTagging
55+
Resource:
56+
- "${bucket_arn}/*
57+
Condition:
58+
StringEquals:
59+
aws:ResourceAccount: "${account}"
60+
- Sid: AllowPutValidationObject
61+
Effect: Allow
62+
Action:
63+
- s3:PutObject
64+
Resource:
65+
- "${bucket_arn}/malware-protection-resource-validation-object"
66+
Condition:
67+
StringEquals:
68+
aws:ResourceAccount: "${account}"
69+
- Sid: AllowCheckBucketOwnership
70+
Effect: Allow
71+
Action:
72+
- s3:ListBucket
73+
Resource:
74+
- "${bucket_arn}"
75+
Condition:
76+
StringEquals:
77+
aws:ResourceAccount: "${account}"
78+
- Sid: AllowMalwareScan
79+
Effect: Allow
80+
Action:
81+
- s3:GetObject
82+
- s3:GetObjectVersion
83+
Resource:
84+
- "${bucket_arn}/*"
85+
Condition:
86+
StringEquals:
87+
aws:ResourceAccount: "${account}"
88+
- Sid: AllowDecryptForMalwareScan
89+
Effect: Allow
90+
Action:
91+
- kms:GenerateDataKey
92+
- kms:Decrypt
93+
Resource: ${kms_key_arn}
94+
Condition:
95+
StringEquals:
96+
kms:CallerAccount: "${account}"
97+
kms:ViaService: s3.${region}.amazonaws.com
98+
StringLike:
99+
kms:EncryptionContext:aws:s3:arn: "${bucket_arn}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Version: "2012-10-17"
2+
Statement:
3+
- Sid: GuardDutyMalwareProtectionForS3
4+
Effect: Allow
5+
Principal:
6+
Service: malware-protection-plan.guardduty.amazonaws.com
7+
Action: sts:AssumeRole
8+
Condition:
9+
StringEquals:
10+
aws:SourceAccount: "${account}"
11+
ArnLike:
12+
aws:SourceArn: arn:${partition}:guardduty:${region}:${account}:malware-protection-plan/*

variables.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ variable "allowed_principals" {
1919
default = []
2020
}
2121

22+
variable "enable_malware_protection" {
23+
type = bool
24+
description = <<-EOT
25+
Whether to enable malware protection for the bucket using GuardDuty. This
26+
will create a new IAM role and GuardDuty malware protection plan.
27+
EOT
28+
default = true
29+
}
30+
2231
variable "encryption_key_arn" {
2332
type = string
2433
description = <<-EOT

0 commit comments

Comments
 (0)