Skip to content

Commit 8d83c61

Browse files
authored
feat: add GuardDuty Malware Protection for S3 module (#691)
Add a new module that enables malware scanning for objects uploaded to the specified S3 bucket.
1 parent 529324d commit 8d83c61

File tree

12 files changed

+286
-1
lines changed

12 files changed

+286
-1
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ jobs:
6363
- client_vpn
6464
- codebuild_github_runner
6565
- ecs
66+
- guardduty_malware_s3
6667
- gh_oidc_role
6768
- lambda_schedule
6869
- notify_slack

.modules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
rds S3 S3_log_bucket user_login_alarm vpc lambda gh_oidc_role attach_tf_plan_policy sentinel_forwarder aws_goc_password_policy sns guardduty guardduty_member lambda_response S3_scan_object athena_access_logs resolver_dns sentinel_alert_rule cds_conformance_pack auto_revoke_sg_rules empty_log_group_alarm simple_static_website schedule_shutdown client_vpn rds_activity_stream waf_ip_blocklist lambda_schedule codebuild_github_runner DocumentDB
1+
rds S3 S3_log_bucket user_login_alarm vpc lambda gh_oidc_role attach_tf_plan_policy sentinel_forwarder aws_goc_password_policy sns guardduty guardduty_member lambda_response S3_scan_object athena_access_logs resolver_dns sentinel_alert_rule cds_conformance_pack auto_revoke_sg_rules empty_log_group_alarm simple_static_website schedule_shutdown client_vpn rds_activity_stream waf_ip_blocklist lambda_schedule codebuild_github_runner DocumentDB guardduty_malware_s3

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- [ECS](ecs)
2424
- [Exposed IAM Credentials disabler](exposed_iam_credentials_disabler)
2525
- [GuardDuty](guardduty)
26+
- [GuardDuty Malware Protection for S3](guardduty_malware_s3)
2627
- [GuardDuty member](guardduty_member)
2728
- [Github Open ID Connect](gh_oidc_role)
2829
- [Lambda](lambda)

guardduty_malware_s3/Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.PHONY: fmt docs test
2+
3+
fmt:
4+
@terraform fmt -recursive
5+
6+
docs:
7+
@rm -rf .terraform
8+
@rm -f .terraform.lock.hcl
9+
@terraform-docs markdown -c ../.terraform-docs.yml . > README.md
10+
11+
test:
12+
terraform init && terraform test --var-file=./tests/globals.tfvars

guardduty_malware_s3/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# GuardDuty Malware Protection for S3
2+
3+
Sets up a GuardDuty Malware Protection plan for an S3 bucket. This will asynchronously scan new objects in the specified S3 bucket for malware and tag the objects with a `GuardDutyMalwareScanStatus` scan verdict.
4+
5+
It is expected that GuardDuty is already enabled in the region where this module is deployed.
6+
7+
:warning: You are charged based on the number and size of the objects scanned.
8+
9+
## Requirements
10+
11+
No requirements.
12+
13+
## Providers
14+
15+
| Name | Version |
16+
|------|---------|
17+
| <a name="provider_aws"></a> [aws](#provider\_aws) | n/a |
18+
19+
## Modules
20+
21+
No modules.
22+
23+
## Resources
24+
25+
| Name | Type |
26+
|------|------|
27+
| [aws_guardduty_malware_protection_plan.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/guardduty_malware_protection_plan) | resource |
28+
| [aws_iam_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
29+
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
30+
| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
31+
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
32+
| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
33+
| [aws_iam_policy_document.this_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
34+
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
35+
36+
## Inputs
37+
38+
| Name | Description | Type | Default | Required |
39+
|------|-------------|------|---------|:--------:|
40+
| <a name="input_billing_tag_key"></a> [billing\_tag\_key](#input\_billing\_tag\_key) | (Optional, default 'CostCentre') The name of the billing tag | `string` | `"CostCentre"` | no |
41+
| <a name="input_billing_tag_value"></a> [billing\_tag\_value](#input\_billing\_tag\_value) | (Required) The value of the billing tag | `string` | n/a | yes |
42+
| <a name="input_s3_bucket_name"></a> [s3\_bucket\_name](#input\_s3\_bucket\_name) | (Required) The name of the S3 bucket to scan objects in. | `string` | n/a | yes |
43+
| <a name="input_s3_object_prefixes"></a> [s3\_object\_prefixes](#input\_s3\_object\_prefixes) | (Optional) List of object prefixes to include. If empty, all objects are included. | `list(string)` | `[]` | no |
44+
| <a name="input_tagging_status"></a> [tagging\_status](#input\_tagging\_status) | (Optional, default 'ENABLED') Whether object tagging is enabled for malware findings. | `string` | `"ENABLED"` | no |
45+
46+
## Outputs
47+
48+
No outputs.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
provider "aws" {
2+
region = "ca-central-1"
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
module "guardduty_malware_s3" {
3+
source = "../../"
4+
5+
s3_bucket_name = module.scan_bucket.s3_bucket_id
6+
s3_object_prefixes = ["only-scan-this-folder/"]
7+
tagging_status = "ENABLED"
8+
9+
billing_tag_value = "testing"
10+
}
11+
12+
module "scan_bucket" {
13+
source = "../../../S3"
14+
15+
bucket_name = "a-bucket-that-will-be-scanned-for-malware"
16+
billing_tag_value = "testing"
17+
}

guardduty_malware_s3/input.tf

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
variable "billing_tag_key" {
2+
description = "(Optional, default 'CostCentre') The name of the billing tag"
3+
type = string
4+
default = "CostCentre"
5+
}
6+
7+
variable "billing_tag_value" {
8+
description = "(Required) The value of the billing tag"
9+
type = string
10+
}
11+
12+
variable "s3_bucket_name" {
13+
description = "(Required) The name of the S3 bucket to scan objects in."
14+
type = string
15+
}
16+
17+
variable "s3_object_prefixes" {
18+
description = "(Optional) List of object prefixes to include. If empty, all objects are included."
19+
type = list(string)
20+
default = []
21+
}
22+
23+
variable "tagging_status" {
24+
description = "(Optional, default 'ENABLED') Whether object tagging is enabled for malware findings."
25+
type = string
26+
default = "ENABLED"
27+
}

guardduty_malware_s3/locals.tf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
data "aws_caller_identity" "current" {}
2+
data "aws_region" "current" {}
3+
4+
locals {
5+
s3_bucket_arn = "arn:aws:s3:::${var.s3_bucket_name}"
6+
region = data.aws_region.current.name
7+
account_id = data.aws_caller_identity.current.account_id
8+
common_tags = {
9+
(var.billing_tag_key) = var.billing_tag_value
10+
Terraform = "true"
11+
}
12+
}
13+

guardduty_malware_s3/main.tf

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* # GuardDuty Malware Protection for S3
3+
*
4+
* Sets up a GuardDuty Malware Protection plan for an S3 bucket. This will asynchronously scan new objects in the specified S3 bucket for malware and tag the objects with a `GuardDutyMalwareScanStatus` scan verdict.
5+
*
6+
* It is expected that GuardDuty is already enabled in the region where this module is deployed.
7+
*
8+
* :warning: You are charged based on the number and size of the objects scanned.
9+
*/
10+
11+
resource "aws_guardduty_malware_protection_plan" "this" {
12+
role = aws_iam_role.this.arn
13+
14+
protected_resource {
15+
s3_bucket {
16+
bucket_name = var.s3_bucket_name
17+
object_prefixes = length(var.s3_object_prefixes) > 0 ? var.s3_object_prefixes : null
18+
}
19+
}
20+
21+
actions {
22+
tagging {
23+
status = var.tagging_status
24+
}
25+
}
26+
27+
tags = local.common_tags
28+
}
29+
30+
resource "aws_iam_role" "this" {
31+
name = "MalwareS3-${var.s3_bucket_name}"
32+
assume_role_policy = data.aws_iam_policy_document.this_assume.json
33+
34+
tags = local.common_tags
35+
}
36+
37+
data "aws_iam_policy_document" "this_assume" {
38+
statement {
39+
actions = ["sts:AssumeRole"]
40+
effect = "Allow"
41+
42+
principals {
43+
type = "Service"
44+
identifiers = ["malware-protection-plan.guardduty.amazonaws.com"]
45+
}
46+
}
47+
}
48+
49+
resource "aws_iam_role_policy_attachment" "this" {
50+
role = aws_iam_role.this.name
51+
policy_arn = aws_iam_policy.this.arn
52+
}
53+
54+
resource "aws_iam_policy" "this" {
55+
name = "MalwareS3-${var.s3_bucket_name}"
56+
path = "/"
57+
policy = data.aws_iam_policy_document.this.json
58+
59+
tags = local.common_tags
60+
}
61+
62+
data "aws_iam_policy_document" "this" {
63+
64+
statement {
65+
effect = "Allow"
66+
67+
actions = [
68+
"s3:ListBucket",
69+
"s3:GetObject",
70+
"s3:PutObject",
71+
"s3:GetObjectTagging",
72+
"s3:GetObjectVersion",
73+
"s3:GetObjectVersionTagging",
74+
"s3:PutObjectTagging",
75+
"s3:PutObjectVersionTagging",
76+
"s3:PutBucketNotification",
77+
"s3:GetBucketNotification",
78+
79+
]
80+
81+
resources = [
82+
local.s3_bucket_arn,
83+
"${local.s3_bucket_arn}/*"
84+
]
85+
}
86+
87+
statement {
88+
effect = "Allow"
89+
actions = [
90+
"events:PutRule",
91+
"events:DeleteRule",
92+
"events:PutTargets",
93+
"events:RemoveTargets"
94+
]
95+
resources = [
96+
"arn:aws:events:${local.region}:${local.account_id}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
97+
]
98+
99+
condition {
100+
test = "StringLike"
101+
variable = "events:ManagedBy"
102+
values = ["malware-protection-plan.guardduty.amazonaws.com"]
103+
}
104+
}
105+
106+
statement {
107+
effect = "Allow"
108+
actions = [
109+
"events:DescribeRule",
110+
"events:ListTargetsByRule"
111+
]
112+
resources = [
113+
"arn:aws:events:${local.region}:${local.account_id}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
114+
]
115+
}
116+
}

0 commit comments

Comments
 (0)