Skip to content

Commit

Permalink
Added finding aggregator (#38)
Browse files Browse the repository at this point in the history
Added finding aggregator and also added enable_default_standards so default standards can be disabled
  • Loading branch information
zdmytriv authored May 31, 2023
1 parent 644d2fa commit ce8cab4
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 19 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Available targets:

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2 |

## Providers
Expand All @@ -176,6 +176,7 @@ Available targets:
| [aws_cloudwatch_event_rule.imported_findings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
| [aws_cloudwatch_event_target.imported_findings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
| [aws_securityhub_account.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource |
| [aws_securityhub_finding_aggregator.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_finding_aggregator) | resource |
| [aws_securityhub_standards_subscription.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_standards_subscription) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.sns_kms_key_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
Expand All @@ -193,9 +194,13 @@ Available targets:
| <a name="input_create_sns_topic"></a> [create\_sns\_topic](#input\_create\_sns\_topic) | Flag to indicate whether an SNS topic should be created for notifications<br><br>If you want to send findings to a new SNS topic, set this to true and provide a valid configuration for subscribers | `bool` | `false` | no |
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| <a name="input_descriptor_formats"></a> [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.<br>Map of maps. Keys are names of descriptors. Values are maps of the form<br>`{<br> format = string<br> labels = list(string)<br>}`<br>(Type is `any` so the map values can later be enhanced to provide additional options.)<br>`format` is a Terraform format string to be passed to the `format()` function.<br>`labels` is a list of labels, in order, to pass to `format()` function.<br>Label values will be normalized before being passed to `format()` so they will be<br>identical to how they appear in `id`.<br>Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no |
| <a name="input_enable_default_standards"></a> [enable\_default\_standards](#input\_enable\_default\_standards) | Flag to indicate whether default standards should be enabled | `bool` | `true` | no |
| <a name="input_enabled"></a> [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| <a name="input_enabled_standards"></a> [enabled\_standards](#input\_enabled\_standards) | A list of standards/rulesets to enable<br><br>See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_standards_subscription#argument-reference<br><br>The possible values are:<br><br> - standards/aws-foundational-security-best-practices/v/1.0.0<br> - ruleset/cis-aws-foundations-benchmark/v/1.2.0<br> - standards/pci-dss/v/3.2.1 | `list(any)` | `[]` | no |
| <a name="input_environment"></a> [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
| <a name="input_finding_aggregator_enabled"></a> [finding\_aggregator\_enabled](#input\_finding\_aggregator\_enabled) | Flag to indicate whether a finding aggregator should be created<br><br>If you want to aggregate findings from one region, set this to `true`.<br><br>For more information, see:<br>https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_finding_aggregator | `bool` | `false` | no |
| <a name="input_finding_aggregator_linking_mode"></a> [finding\_aggregator\_linking\_mode](#input\_finding\_aggregator\_linking\_mode) | Linking mode to use for the finding aggregator. <br><br>The possible values are: <br> - `ALL_REGIONS` - Aggregate from all regions<br> - `ALL_REGIONS_EXCEPT_SPECIFIED` - Aggregate from all regions except those specified in `var.finding_aggregator_regions`<br> - `SPECIFIED_REGIONS` - Aggregate from regions specified in `finding_aggregator_enabled` | `string` | `"ALL_REGIONS"` | no |
| <a name="input_finding_aggregator_regions"></a> [finding\_aggregator\_regions](#input\_finding\_aggregator\_regions) | A list of regions to aggregate findings from. <br><br>This is only used if `finding_aggregator_enabled` is `true`. | `list(string)` | `[]` | no |
| <a name="input_id_length_limit"></a> [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).<br>Set to `0` for unlimited length.<br>Set to `null` for keep the existing setting, which defaults to `0`.<br>Does not affect `id_full`. | `number` | `null` | no |
| <a name="input_imported_findings_notification_arn"></a> [imported\_findings\_notification\_arn](#input\_imported\_findings\_notification\_arn) | The ARN for an SNS topic to send findings notifications to. This is only used if create\_sns\_topic is false.<br><br>If you want to send findings to an existing SNS topic, set the value of this to the ARN of the existing topic and set <br>create\_sns\_topic to false. | `string` | `null` | no |
| <a name="input_label_key_case"></a> [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.<br>Does not affect keys of tags passed in via the `tags` input.<br>Possible values: `lower`, `title`, `upper`.<br>Default value: `title`. | `string` | `null` | no |
Expand Down
7 changes: 6 additions & 1 deletion docs/terraform.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 2 |

## Providers
Expand All @@ -29,6 +29,7 @@
| [aws_cloudwatch_event_rule.imported_findings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource |
| [aws_cloudwatch_event_target.imported_findings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_target) | resource |
| [aws_securityhub_account.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource |
| [aws_securityhub_finding_aggregator.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_finding_aggregator) | resource |
| [aws_securityhub_standards_subscription.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_standards_subscription) | resource |
| [aws_caller_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.sns_kms_key_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
Expand All @@ -46,9 +47,13 @@
| <a name="input_create_sns_topic"></a> [create\_sns\_topic](#input\_create\_sns\_topic) | Flag to indicate whether an SNS topic should be created for notifications<br><br>If you want to send findings to a new SNS topic, set this to true and provide a valid configuration for subscribers | `bool` | `false` | no |
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
| <a name="input_descriptor_formats"></a> [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.<br>Map of maps. Keys are names of descriptors. Values are maps of the form<br>`{<br> format = string<br> labels = list(string)<br>}`<br>(Type is `any` so the map values can later be enhanced to provide additional options.)<br>`format` is a Terraform format string to be passed to the `format()` function.<br>`labels` is a list of labels, in order, to pass to `format()` function.<br>Label values will be normalized before being passed to `format()` so they will be<br>identical to how they appear in `id`.<br>Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no |
| <a name="input_enable_default_standards"></a> [enable\_default\_standards](#input\_enable\_default\_standards) | Flag to indicate whether default standards should be enabled | `bool` | `true` | no |
| <a name="input_enabled"></a> [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
| <a name="input_enabled_standards"></a> [enabled\_standards](#input\_enabled\_standards) | A list of standards/rulesets to enable<br><br>See https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_standards_subscription#argument-reference<br><br>The possible values are:<br><br> - standards/aws-foundational-security-best-practices/v/1.0.0<br> - ruleset/cis-aws-foundations-benchmark/v/1.2.0<br> - standards/pci-dss/v/3.2.1 | `list(any)` | `[]` | no |
| <a name="input_environment"></a> [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
| <a name="input_finding_aggregator_enabled"></a> [finding\_aggregator\_enabled](#input\_finding\_aggregator\_enabled) | Flag to indicate whether a finding aggregator should be created<br><br>If you want to aggregate findings from one region, set this to `true`.<br><br>For more information, see:<br>https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_finding_aggregator | `bool` | `false` | no |
| <a name="input_finding_aggregator_linking_mode"></a> [finding\_aggregator\_linking\_mode](#input\_finding\_aggregator\_linking\_mode) | Linking mode to use for the finding aggregator. <br><br>The possible values are: <br> - `ALL_REGIONS` - Aggregate from all regions<br> - `ALL_REGIONS_EXCEPT_SPECIFIED` - Aggregate from all regions except those specified in `var.finding_aggregator_regions`<br> - `SPECIFIED_REGIONS` - Aggregate from regions specified in `finding_aggregator_enabled` | `string` | `"ALL_REGIONS"` | no |
| <a name="input_finding_aggregator_regions"></a> [finding\_aggregator\_regions](#input\_finding\_aggregator\_regions) | A list of regions to aggregate findings from. <br><br>This is only used if `finding_aggregator_enabled` is `true`. | `list(string)` | `[]` | no |
| <a name="input_id_length_limit"></a> [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).<br>Set to `0` for unlimited length.<br>Set to `null` for keep the existing setting, which defaults to `0`.<br>Does not affect `id_full`. | `number` | `null` | no |
| <a name="input_imported_findings_notification_arn"></a> [imported\_findings\_notification\_arn](#input\_imported\_findings\_notification\_arn) | The ARN for an SNS topic to send findings notifications to. This is only used if create\_sns\_topic is false.<br><br>If you want to send findings to an existing SNS topic, set the value of this to the ARN of the existing topic and set <br>create\_sns\_topic to false. | `string` | `null` | no |
| <a name="input_label_key_case"></a> [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.<br>Does not affect keys of tags passed in via the `tags` input.<br>Possible values: `lower`, `title`, `upper`.<br>Default value: `title`. | `string` | `null` | no |
Expand Down
6 changes: 3 additions & 3 deletions eventbridge.tf
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ data "aws_iam_policy_document" "sns_kms_key_policy" {

principals {
type = "AWS"
identifiers = ["arn:aws:iam::${data.aws_caller_identity.this.account_id}:root"]
identifiers = ["arn:aws:iam::${one(data.aws_caller_identity.this[*].account_id)}:root"]
}
}

Expand Down Expand Up @@ -85,7 +85,7 @@ module "imported_findings_label" {
}

resource "aws_cloudwatch_event_rule" "imported_findings" {
count = local.enable_notifications == true ? 1 : 0
count = local.enable_notifications ? 1 : 0
name = module.imported_findings_label.id
description = "SecurityHubEvent - Imported Findings"
tags = module.this.tags
Expand All @@ -103,7 +103,7 @@ resource "aws_cloudwatch_event_rule" "imported_findings" {
}

resource "aws_cloudwatch_event_target" "imported_findings" {
count = local.enable_notifications == true ? 1 : 0
count = local.enable_notifications ? 1 : 0
rule = aws_cloudwatch_event_rule.imported_findings[0].name
arn = local.imported_findings_notification_arn
}
2 changes: 1 addition & 1 deletion examples/complete/versions.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 0.13.0"
required_version = ">= 1.0"

required_providers {
aws = {
Expand Down
42 changes: 33 additions & 9 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
#-----------------------------------------------------------------------------------------------------------------------
# Subscribe the Acccount to Security Hub
# Subscribe the Account to Security Hub
#-----------------------------------------------------------------------------------------------------------------------
resource "aws_securityhub_account" "this" {
count = module.this.enabled ? 1 : 0
count = local.enabled ? 1 : 0

enable_default_standards = var.enable_default_standards
}

#-----------------------------------------------------------------------------------------------------------------------
# Optionally subscribe to Security Hub Standards
# https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards.html
#-----------------------------------------------------------------------------------------------------------------------
resource "aws_securityhub_standards_subscription" "this" {
for_each = local.enabled_standards_arns
for_each = local.enabled ? local.enabled_standards_arns : []
depends_on = [aws_securityhub_account.this]
standards_arn = each.key
}

#-----------------------------------------------------------------------------------------------------------------------
# Optionally setup finding aggregator
# https://docs.aws.amazon.com/securityhub/latest/userguide/finding-aggregation.html
#-----------------------------------------------------------------------------------------------------------------------
resource "aws_securityhub_finding_aggregator" "this" {
count = local.enabled && var.finding_aggregator_enabled ? 1 : 0

linking_mode = var.finding_aggregator_linking_mode
specified_regions = var.finding_aggregator_linking_mode == "ALL_REGIONS" ? null : var.finding_aggregator_regions

depends_on = [aws_securityhub_account.this]
}

#-----------------------------------------------------------------------------------------------------------------------
# Locals and Data References
#-----------------------------------------------------------------------------------------------------------------------
locals {
enable_notifications = module.this.enabled && (var.create_sns_topic || var.imported_findings_notification_arn != null)
create_sns_topic = module.this.enabled && var.create_sns_topic
enabled = module.this.enabled
enable_notifications = local.enabled && (var.create_sns_topic || var.imported_findings_notification_arn != null)
create_sns_topic = local.enabled && var.create_sns_topic
imported_findings_notification_arn = local.enable_notifications ? (var.imported_findings_notification_arn != null ? var.imported_findings_notification_arn : module.sns_topic[0].sns_topic.arn) : null
enabled_standards_arns = toset([
for standard in var.enabled_standards :
format("arn:%s:securityhub:%s::%s", data.aws_partition.this.partition, length(regexall("ruleset", standard)) == 0 ? data.aws_region.this.name : "", standard)
format("arn:%s:securityhub:%s::%s", one(data.aws_partition.this[*].partition), length(regexall("ruleset", standard)) == 0 ? one(data.aws_region.this[*].name) : "", standard)
])
}

data "aws_caller_identity" "this" {}
data "aws_partition" "this" {}
data "aws_region" "this" {}
data "aws_caller_identity" "this" {
count = local.enabled ? 1 : 0
}

data "aws_partition" "this" {
count = local.enabled ? 1 : 0
}

data "aws_region" "this" {
count = local.enabled ? 1 : 0
}
2 changes: 1 addition & 1 deletion modules/control-disablements/versions.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 0.13.0"
required_version = ">= 1.0"

required_providers {
aws = {
Expand Down
4 changes: 2 additions & 2 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
output "enabled_subscriptions" {
description = "A list of subscriptions that have been enabled"
value = [
value = local.enabled ? [
for standard in aws_securityhub_standards_subscription.this :
standard.id
]
] : []
}

output "sns_topic" {
Expand Down
43 changes: 43 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
# These variables have defaults set that can be overridden for desired behavior.
#-----------------------------------------------------------------------------------------------------------------------

variable "enable_default_standards" {
description = "Flag to indicate whether default standards should be enabled"
type = bool
default = true
}

variable "enabled_standards" {
description = <<-DOC
A list of standards/rulesets to enable
Expand Down Expand Up @@ -66,3 +72,40 @@ variable "cloudwatch_event_rule_pattern_detail_type" {
type = string
default = "Security Hub Findings - Imported"
}

variable "finding_aggregator_enabled" {
description = <<-DOC
Flag to indicate whether a finding aggregator should be created
If you want to aggregate findings from one region, set this to `true`.
For more information, see:
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_finding_aggregator
DOC

type = bool
default = false
}

variable "finding_aggregator_linking_mode" {
description = <<-DOC
Linking mode to use for the finding aggregator.
The possible values are:
- `ALL_REGIONS` - Aggregate from all regions
- `ALL_REGIONS_EXCEPT_SPECIFIED` - Aggregate from all regions except those specified in `var.finding_aggregator_regions`
- `SPECIFIED_REGIONS` - Aggregate from regions specified in `finding_aggregator_enabled`
DOC
type = string
default = "ALL_REGIONS"
}

variable "finding_aggregator_regions" {
description = <<-DOC
A list of regions to aggregate findings from.
This is only used if `finding_aggregator_enabled` is `true`.
DOC
type = list(string)
default = []
}
2 changes: 1 addition & 1 deletion versions.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
terraform {
required_version = ">= 0.13.0"
required_version = ">= 1.0"

required_providers {
aws = {
Expand Down

0 comments on commit ce8cab4

Please # to comment.