From 60db1091d4eecac40a77f012a49fec6511032a53 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Fri, 13 Dec 2024 10:39:51 +0100 Subject: [PATCH 01/28] feature: support central Security Hub configuration --- security_hub.tf | 23 ++++++++++++++++++++--- variables.tf | 34 ++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/security_hub.tf b/security_hub.tf index 5bbeb76..6392198 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -1,4 +1,10 @@ // AWS Security Hub - Management account configuration and enrollment +locals { + security_configuration_type = ( + var.aws_security_hub.organization_configuration_type == "CENTRAL" ? "NONE" : + (var.aws_security_hub.auto_enable_default_standards ? "DEFAULT" : "NONE") + ) +} resource "aws_securityhub_organization_admin_account" "default" { admin_account_id = data.aws_caller_identity.audit.account_id @@ -41,10 +47,14 @@ resource "aws_securityhub_account" "default" { resource "aws_securityhub_organization_configuration" "default" { provider = aws.audit - auto_enable = var.aws_security_hub.auto_enable_new_accounts - auto_enable_standards = var.aws_security_hub.auto_enable_default_standards ? "DEFAULT" : "NONE" + auto_enable = var.aws_security_hub.organization_configuration_type == "CENTRAL" ? false : var.aws_security_hub.auto_enable_new_accounts + auto_enable_standards = local.security_configuration_type + + organization_configuration { + configuration_type = var.aws_security_hub.organization_configuration_type + } - depends_on = [aws_securityhub_organization_admin_account.default] + depends_on = [aws_securityhub_organization_admin_account.default, aws_securityhub_finding_aggregator.default] } resource "aws_securityhub_product_subscription" "default" { @@ -133,3 +143,10 @@ resource "aws_securityhub_standards_subscription" "logging" { standards_arn = each.value depends_on = [aws_securityhub_account.default] } + +resource "aws_securityhub_finding_aggregator" "default" { + linking_mode = var.aws_security_hub.linking_mode + specified_regions = var.aws_security_hub.specified_regions + + depends_on = [aws_securityhub_account.default] +} diff --git a/variables.tf b/variables.tf index 9bd2c79..595d504 100644 --- a/variables.tf +++ b/variables.tf @@ -151,22 +151,28 @@ variable "aws_required_tags" { variable "aws_security_hub" { type = object({ - auto_enable_controls = optional(bool, true) - auto_enable_default_standards = optional(bool, false) - auto_enable_new_accounts = optional(bool, true) - control_finding_generator = optional(string, "SECURITY_CONTROL") - create_cis_metric_filters = optional(bool, true) - product_arns = optional(list(string), []) - standards_arns = optional(list(string), null) + aggregator_linking_mode = optional(string, "ALL_REGIONS") + aggregator_specified_regions = optional(list(string), []) + auto_enable_controls = optional(bool, true) + auto_enable_default_standards = optional(bool, false) + auto_enable_new_accounts = optional(bool, true) + control_finding_generator = optional(string, "SECURITY_CONTROL") + create_cis_metric_filters = optional(bool, true) + organization_configuration_type = optional(string, "LOCAL") + product_arns = optional(list(string), []) + standards_arns = optional(list(string), null) }) default = { - auto_enable_controls = true - auto_enable_default_standards = false - auto_enable_new_accounts = true - control_finding_generator = "SECURITY_CONTROL" - create_cis_metric_filters = true - product_arns = [] - standards_arns = null + aggregator_linking_mode = "ALL_REGIONS" + aggregator_specified_regions = null + auto_enable_controls = true + auto_enable_default_standards = false + auto_enable_new_accounts = true + control_finding_generator = "SECURITY_CONTROL" + create_cis_metric_filters = true + organization_configuration_type = "LOCAL" + product_arns = [] + standards_arns = null } description = "AWS Security Hub settings" From 7020061a67ac4b7f4070c86b48cffc260bfd52e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Dec 2024 10:09:52 +0000 Subject: [PATCH 02/28] docs(readme): update module usage --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 938501f..99105cf 100644 --- a/README.md +++ b/README.md @@ -504,6 +504,7 @@ module "landing_zone" { | [aws_s3_account_public_access_block.master](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_account_public_access_block) | resource | | [aws_securityhub_account.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource | | [aws_securityhub_account.management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource | +| [aws_securityhub_finding_aggregator.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_finding_aggregator) | resource | | [aws_securityhub_member.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_member) | resource | | [aws_securityhub_member.management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_member) | resource | | [aws_securityhub_organization_admin_account.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_organization_admin_account) | resource | @@ -549,7 +550,7 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
|
{
"auto_enable_controls": true,
"auto_enable_default_standards": false,
"auto_enable_new_accounts": true,
"control_finding_generator": "SECURITY_CONTROL",
"create_cis_metric_filters": true,
"product_arns": [],
"standards_arns": null
}
| no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), [])
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
|
{
"aggregator_linking_mode": "ALL_REGIONS",
"aggregator_specified_regions": null,
"auto_enable_controls": true,
"auto_enable_default_standards": false,
"auto_enable_new_accounts": true,
"control_finding_generator": "SECURITY_CONTROL",
"create_cis_metric_filters": true,
"organization_configuration_type": "LOCAL",
"product_arns": [],
"standards_arns": null
}
| no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | From 9ca7e78f5d23160c549e672b94761505df76cbf9 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Fri, 13 Dec 2024 11:14:58 +0100 Subject: [PATCH 03/28] rely on defaults of optionals --- variables.tf | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/variables.tf b/variables.tf index 595d504..06d075f 100644 --- a/variables.tf +++ b/variables.tf @@ -162,18 +162,7 @@ variable "aws_security_hub" { product_arns = optional(list(string), []) standards_arns = optional(list(string), null) }) - default = { - aggregator_linking_mode = "ALL_REGIONS" - aggregator_specified_regions = null - auto_enable_controls = true - auto_enable_default_standards = false - auto_enable_new_accounts = true - control_finding_generator = "SECURITY_CONTROL" - create_cis_metric_filters = true - organization_configuration_type = "LOCAL" - product_arns = [] - standards_arns = null - } + default = {} description = "AWS Security Hub settings" validation { From b71a6a59a83287d97923b6b41763e6bd8d7a3676 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Dec 2024 14:01:48 +0000 Subject: [PATCH 04/28] docs(readme): update module usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99105cf..0b60809 100644 --- a/README.md +++ b/README.md @@ -550,7 +550,7 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), [])
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
|
{
"aggregator_linking_mode": "ALL_REGIONS",
"aggregator_specified_regions": null,
"auto_enable_controls": true,
"auto_enable_default_standards": false,
"auto_enable_new_accounts": true,
"control_finding_generator": "SECURITY_CONTROL",
"create_cis_metric_filters": true,
"organization_configuration_type": "LOCAL",
"product_arns": [],
"standards_arns": null
}
| no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), [])
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | From d39d1f08217fba7b285d5f2a9d0588f7d6f0178c Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Fri, 13 Dec 2024 16:42:26 +0100 Subject: [PATCH 05/28] do not use key if no value --- variables.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variables.tf b/variables.tf index 06d075f..49e38cf 100644 --- a/variables.tf +++ b/variables.tf @@ -152,7 +152,7 @@ variable "aws_required_tags" { variable "aws_security_hub" { type = object({ aggregator_linking_mode = optional(string, "ALL_REGIONS") - aggregator_specified_regions = optional(list(string), []) + aggregator_specified_regions = optional(list(string), null) auto_enable_controls = optional(bool, true) auto_enable_default_standards = optional(bool, false) auto_enable_new_accounts = optional(bool, true) From 83290a5b75c39d6bf7e0b83fa0f24cdf9793e7bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 13 Dec 2024 15:42:54 +0000 Subject: [PATCH 06/28] docs(readme): update module usage --- README.md | 2 +- variables.tf | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b60809..225170f 100644 --- a/README.md +++ b/README.md @@ -550,7 +550,7 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), [])
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | diff --git a/variables.tf b/variables.tf index 49e38cf..53e626e 100644 --- a/variables.tf +++ b/variables.tf @@ -162,7 +162,6 @@ variable "aws_security_hub" { product_arns = optional(list(string), []) standards_arns = optional(list(string), null) }) - default = {} description = "AWS Security Hub settings" validation { From c6ccfc779681e0716be5737304714bd3ea7f5e98 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 14 Dec 2024 08:27:42 +0000 Subject: [PATCH 07/28] docs(readme): update module usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 225170f..c56c8fd 100644 --- a/README.md +++ b/README.md @@ -539,6 +539,7 @@ module "landing_zone" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| n/a | yes | | [control\_tower\_account\_ids](#input\_control\_tower\_account\_ids) | Control Tower core account IDs |
object({
audit = string
logging = string
})
| n/a | yes | | [tags](#input\_tags) | Map of tags | `map(string)` | n/a | yes | | [additional\_auditing\_trail](#input\_additional\_auditing\_trail) | CloudTrail configuration for additional auditing trail |
object({
name = string
bucket = string
kms_key_id = string

event_selector = optional(object({
data_resource = optional(object({
type = string
values = list(string)
}))
exclude_management_event_sources = optional(set(string), null)
include_management_events = optional(bool, true)
read_write_type = optional(string, "All")
}))
})
| `null` | no | @@ -550,7 +551,6 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | From 09235946704252bb64220fcba9a9d065802960ae Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 17 Dec 2024 10:14:18 +0100 Subject: [PATCH 08/28] feat: Add validation rules to security_hub configuration --- security_hub.tf | 4 ++-- variables.tf | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/security_hub.tf b/security_hub.tf index 010703f..ac1f512 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -47,8 +47,8 @@ resource "aws_securityhub_account" "default" { resource "aws_securityhub_organization_configuration" "default" { provider = aws.audit - auto_enable = var.aws_security_hub.organization_configuration_type == "CENTRAL" ? false : var.aws_security_hub.auto_enable_new_accounts - auto_enable_standards = local.security_configuration_type + auto_enable = var.aws_security_hub.auto_enable_new_accounts + auto_enable_standards = var.aws_security_hub.auto_enable_default_standards organization_configuration { configuration_type = var.aws_security_hub.organization_configuration_type diff --git a/variables.tf b/variables.tf index 53e626e..e86cc12 100644 --- a/variables.tf +++ b/variables.tf @@ -162,12 +162,23 @@ variable "aws_security_hub" { product_arns = optional(list(string), []) standards_arns = optional(list(string), null) }) + default = {} description = "AWS Security Hub settings" validation { condition = contains(["SECURITY_CONTROL", "STANDARD_CONTROL"], var.aws_security_hub.control_finding_generator) error_message = "The \"control_finding_generator\" variable must be set to either \"SECURITY_CONTROL\" or \"STANDARD_CONTROL\"." } + + validation { + condition = contains(["LOCAL", "CENTRAL"], var.security_hub.organization_configuration_type) + error_message = "Invalid var.security_hub.organization_configuration_type: Must be one of \"LOCAL\" or \"CENTRAL\"." + } + + validation { + condition = var.security_hub.organization_configuration_type == "LOCAL" || (var.security_hub.auto_enable_new_accounts == false && var.security_hub.auto_enable_default_standards == "NONE") + error_message = "If var.security_hub.organization_configuration_type is \"CENTRAL\", var.security_hub.auto_enable_new_accounts` must be \"False\" and var.security_hub.auto_enable_default_standards must be \"NONE\"." + } } variable "aws_security_hub_sns_subscription" { From 750cdc13065e5b63d6364a364b30ed7eed082557 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 17 Dec 2024 09:20:10 +0000 Subject: [PATCH 09/28] docs(readme): update module usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c56c8fd..225170f 100644 --- a/README.md +++ b/README.md @@ -539,7 +539,6 @@ module "landing_zone" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| n/a | yes | | [control\_tower\_account\_ids](#input\_control\_tower\_account\_ids) | Control Tower core account IDs |
object({
audit = string
logging = string
})
| n/a | yes | | [tags](#input\_tags) | Map of tags | `map(string)` | n/a | yes | | [additional\_auditing\_trail](#input\_additional\_auditing\_trail) | CloudTrail configuration for additional auditing trail |
object({
name = string
bucket = string
kms_key_id = string

event_selector = optional(object({
data_resource = optional(object({
type = string
values = list(string)
}))
exclude_management_event_sources = optional(set(string), null)
include_management_events = optional(bool, true)
read_write_type = optional(string, "All")
}))
})
| `null` | no | @@ -551,6 +550,7 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | From ac42a576490b751d0b6aa40cda2d6b8af8d17bdc Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 17 Dec 2024 10:27:26 +0100 Subject: [PATCH 10/28] Set provider for aws_securityhub_finding_aggregator --- security_hub.tf | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/security_hub.tf b/security_hub.tf index ac1f512..f935bd3 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -1,10 +1,4 @@ // AWS Security Hub - Management account configuration and enrollment -locals { - security_configuration_type = ( - var.aws_security_hub.organization_configuration_type == "CENTRAL" ? "NONE" : - (var.aws_security_hub.auto_enable_default_standards ? "DEFAULT" : "NONE") - ) -} resource "aws_securityhub_organization_admin_account" "default" { admin_account_id = data.aws_caller_identity.audit.account_id @@ -145,6 +139,8 @@ resource "aws_securityhub_standards_subscription" "logging" { } resource "aws_securityhub_finding_aggregator" "default" { + provider = aws.audit + linking_mode = var.aws_security_hub.aggregator_linking_mode specified_regions = var.aws_security_hub.aggregator_specified_regions From 3f1d890cea49ab04b53efb839bb5481a09a1890c Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 17 Dec 2024 12:26:15 +0100 Subject: [PATCH 11/28] Move Security Hub Configuration to Central --- UPGRADING.md | 9 ++++++++ security_hub.tf | 59 ++++++++++++++++++++----------------------------- variables.tf | 27 ++++++---------------- 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 79fa1f8..74325bd 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -2,6 +2,15 @@ This document captures required refactoring on your part when upgrading to a module version that contains breaking changes. +## Upgrading to v4.1.0 + +### Behaviour + +This version changes the detault [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). You can change this behaviour by setting `var.aws_security_hub.organization_configuration_type` to `LOCAL`. + +This version enables Security Hub Findings Aggregation for all regions. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` or `SPECIFIED_REGIONS` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions` + + ## Upgrading to v4.0.0 > [!WARNING] diff --git a/security_hub.tf b/security_hub.tf index 023927d..5b92370 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -23,14 +23,6 @@ resource "aws_securityhub_member" "management" { } } -resource "aws_securityhub_standards_subscription" "management" { - for_each = toset(local.security_hub_standards_arns) - - standards_arn = each.value - - depends_on = [aws_securityhub_account.default] -} - // AWS Security Hub - Audit account configuration and enrollment resource "aws_securityhub_account" "default" { provider = aws.audit @@ -41,33 +33,16 @@ resource "aws_securityhub_account" "default" { resource "aws_securityhub_organization_configuration" "default" { provider = aws.audit - auto_enable = var.aws_security_hub.auto_enable_new_accounts - auto_enable_standards = var.aws_security_hub.auto_enable_default_standards ? "DEFAULT" : "NONE" + auto_enable = false + auto_enable_standards = "NONE" organization_configuration { - configuration_type = var.aws_security_hub.organization_configuration_type + configuration_type = "CENTRAL" } depends_on = [aws_securityhub_organization_admin_account.default, aws_securityhub_finding_aggregator.default] } -resource "aws_securityhub_product_subscription" "default" { - for_each = toset(var.aws_security_hub.product_arns) - provider = aws.audit - - product_arn = each.value - - depends_on = [aws_securityhub_account.default] -} - -resource "aws_securityhub_standards_subscription" "default" { - for_each = toset(local.security_hub_standards_arns) - provider = aws.audit - - standards_arn = each.value - - depends_on = [aws_securityhub_account.default] -} resource "aws_cloudwatch_event_rule" "security_hub_findings" { provider = aws.audit @@ -130,13 +105,6 @@ resource "aws_securityhub_member" "logging" { depends_on = [aws_securityhub_organization_configuration.default] } -resource "aws_securityhub_standards_subscription" "logging" { - for_each = toset(local.security_hub_standards_arns) - provider = aws.logging - - standards_arn = each.value - depends_on = [aws_securityhub_account.default] -} resource "aws_securityhub_finding_aggregator" "default" { provider = aws.audit @@ -146,3 +114,24 @@ resource "aws_securityhub_finding_aggregator" "default" { depends_on = [aws_securityhub_account.default] } + +resource "aws_securityhub_configuration_policy" "default" { + name = "mcaf-lz" + description = "MCAF Landing Zone default configuration policy" + + configuration_policy { + service_enabled = true + enabled_standard_arns = local.security_hub_standards_arns + + security_controls_configuration { + disabled_control_identifiers = [] + } + } + + depends_on = [aws_securityhub_organization_configuration.default] +} + +resource "aws_securityhub_configuration_policy_association" "root" { + target_id = data.aws_organizations_organization.default.id + policy_id = aws_securityhub_configuration_policy.default.id +} diff --git a/variables.tf b/variables.tf index 3ce407c..eb41d90 100644 --- a/variables.tf +++ b/variables.tf @@ -151,16 +151,13 @@ variable "aws_required_tags" { variable "aws_security_hub" { type = object({ - aggregator_linking_mode = optional(string, "ALL_REGIONS") - aggregator_specified_regions = optional(list(string), null) - auto_enable_controls = optional(bool, true) - auto_enable_default_standards = optional(bool, false) - auto_enable_new_accounts = optional(bool, true) - control_finding_generator = optional(string, "SECURITY_CONTROL") - create_cis_metric_filters = optional(bool, true) - organization_configuration_type = optional(string, "LOCAL") - product_arns = optional(list(string), []) - standards_arns = optional(list(string), null) + aggregator_linking_mode = optional(string, "ALL_REGIONS") + aggregator_specified_regions = optional(list(string), null) + auto_enable_controls = optional(bool, true) + control_finding_generator = optional(string, "SECURITY_CONTROL") + create_cis_metric_filters = optional(bool, true) + product_arns = optional(list(string), []) + standards_arns = optional(list(string), null) }) default = {} description = "AWS Security Hub settings" @@ -169,16 +166,6 @@ variable "aws_security_hub" { condition = contains(["SECURITY_CONTROL", "STANDARD_CONTROL"], var.aws_security_hub.control_finding_generator) error_message = "The \"control_finding_generator\" variable must be set to either \"SECURITY_CONTROL\" or \"STANDARD_CONTROL\"." } - - validation { - condition = contains(["LOCAL", "CENTRAL"], var.aws_security_hub.organization_configuration_type) - error_message = "Invalid var.aws_security_hub.organization_configuration_type: Must be one of \"LOCAL\" or \"CENTRAL\"." - } - - validation { - condition = var.aws_security_hub.organization_configuration_type == "LOCAL" || (var.aws_security_hub.auto_enable_new_accounts == false && var.aws_security_hub.auto_enable_default_standards == false) - error_message = "If var.aws_security_hub.organization_configuration_type is \"CENTRAL\", var.aws_security_hub.auto_enable_new_accounts` must be \"False\" and var.aws_security_hub.auto_enable_default_standards must be \"False\"." - } } variable "aws_security_hub_sns_subscription" { From d623e1027ead6c8b62b1e24c8b17e574008e50f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 17 Dec 2024 11:31:26 +0000 Subject: [PATCH 12/28] docs(readme): update module usage --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 225170f..46683d6 100644 --- a/README.md +++ b/README.md @@ -504,15 +504,13 @@ module "landing_zone" { | [aws_s3_account_public_access_block.master](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_account_public_access_block) | resource | | [aws_securityhub_account.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource | | [aws_securityhub_account.management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_account) | resource | +| [aws_securityhub_configuration_policy.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_configuration_policy) | resource | +| [aws_securityhub_configuration_policy_association.root](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_configuration_policy_association) | resource | | [aws_securityhub_finding_aggregator.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_finding_aggregator) | resource | | [aws_securityhub_member.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_member) | resource | | [aws_securityhub_member.management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_member) | resource | | [aws_securityhub_organization_admin_account.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_organization_admin_account) | resource | | [aws_securityhub_organization_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_organization_configuration) | resource | -| [aws_securityhub_product_subscription.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_product_subscription) | resource | -| [aws_securityhub_standards_subscription.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_standards_subscription) | resource | -| [aws_securityhub_standards_subscription.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_standards_subscription) | resource | -| [aws_securityhub_standards_subscription.management](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/securityhub_standards_subscription) | resource | | [aws_sns_topic.iam_activity](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | | [aws_sns_topic.security_hub_findings](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | | [aws_sns_topic_policy.iam_activity](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | @@ -550,7 +548,7 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
auto_enable_default_standards = optional(bool, false)
auto_enable_new_accounts = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
organization_configuration_type = optional(string, "LOCAL")
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | From 5afe35788cebd427505535f8d61ef0c7faece673 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 17 Dec 2024 13:21:25 +0100 Subject: [PATCH 13/28] Use correct org root id --- UPGRADING.md | 3 ++- security_hub.tf | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 74325bd..c762184 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -6,7 +6,8 @@ This document captures required refactoring on your part when upgrading to a mod ### Behaviour -This version changes the detault [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). You can change this behaviour by setting `var.aws_security_hub.organization_configuration_type` to `LOCAL`. +> [!IMPORTANT] +> **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html).** This version enables Security Hub Findings Aggregation for all regions. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` or `SPECIFIED_REGIONS` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions` diff --git a/security_hub.tf b/security_hub.tf index 5b92370..8e88190 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -132,6 +132,6 @@ resource "aws_securityhub_configuration_policy" "default" { } resource "aws_securityhub_configuration_policy_association" "root" { - target_id = data.aws_organizations_organization.default.id + target_id = data.aws_organizations_organization.default.roots[0].id policy_id = aws_securityhub_configuration_policy.default.id } From f4b3571a725d8a81de50959f4808a04e6894758e Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 17 Dec 2024 14:25:44 +0100 Subject: [PATCH 14/28] Use central allowed_region for all region bound configurations --- UPGRADING.md | 6 +++++- config.tf | 4 ++-- organizations_policy.tf | 6 +++--- security_hub.tf | 2 +- variables.tf | 25 ++++++++++++++++--------- 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index c762184..23ac501 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -2,7 +2,7 @@ This document captures required refactoring on your part when upgrading to a module version that contains breaking changes. -## Upgrading to v4.1.0 +## Upgrading to v5.0.0 ### Behaviour @@ -11,6 +11,10 @@ This document captures required refactoring on your part when upgrading to a mod This version enables Security Hub Findings Aggregation for all regions. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` or `SPECIFIED_REGIONS` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions` +The following variables have been replaced: +* `aws_service_control_policies.allowed_regions` -> `allowed_regions` +* `aws_config.aggregator_regions` -> `allowed_regions` + ## Upgrading to v4.0.0 diff --git a/config.tf b/config.tf index 17f8171..74079f2 100644 --- a/config.tf +++ b/config.tf @@ -1,7 +1,7 @@ locals { aws_config_aggregators = flatten([ for account in toset(try(var.aws_config.aggregator_account_ids, [])) : [ - for region in toset(try(var.aws_config.aggregator_regions, [])) : { + for region in toset(try(var.allowed_regions, [])) : { account_id = account region = region } @@ -32,7 +32,7 @@ resource "aws_config_aggregate_authorization" "master" { } resource "aws_config_aggregate_authorization" "master_to_audit" { - for_each = toset(coalescelist(var.aws_config.aggregator_regions, [data.aws_region.current.name])) + for_each = toset(coalescelist(var.allowed_regions, [data.aws_region.current.name])) account_id = var.control_tower_account_ids.audit region = each.value diff --git a/organizations_policy.tf b/organizations_policy.tf index 3f2d3d3..f515965 100644 --- a/organizations_policy.tf +++ b/organizations_policy.tf @@ -1,9 +1,9 @@ locals { enabled_root_policies = { allowed_regions = { - enable = var.aws_service_control_policies.allowed_regions != null ? true : false - policy = var.aws_service_control_policies.allowed_regions != null ? templatefile("${path.module}/files/organizations/allowed_regions.json.tpl", { - allowed = var.aws_service_control_policies.allowed_regions != null ? var.aws_service_control_policies.allowed_regions : [] + enable = var.allowed_regions != null ? true : false + policy = var.allowed_regions != null ? templatefile("${path.module}/files/organizations/allowed_regions.json.tpl", { + allowed = var.allowed_regions != null ? var.allowed_regions : [] exceptions = local.aws_service_control_policies_principal_exceptions }) : null } diff --git a/security_hub.tf b/security_hub.tf index 35358fb..bcd85e8 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -110,7 +110,7 @@ resource "aws_securityhub_finding_aggregator" "default" { provider = aws.audit linking_mode = var.aws_security_hub.aggregator_linking_mode - specified_regions = var.aws_security_hub.aggregator_specified_regions + specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? var.allowed_regions : null depends_on = [aws_securityhub_account.default] } diff --git a/variables.tf b/variables.tf index eb41d90..56f60f3 100644 --- a/variables.tf +++ b/variables.tf @@ -18,6 +18,11 @@ variable "additional_auditing_trail" { description = "CloudTrail configuration for additional auditing trail" } +variable "allowed_regions" { + type = list(string) + description = "List of AWS regions where operations are allowed and for which central services like Security Hub and AWS Config are configured." +} + variable "aws_account_password_policy" { type = object({ allow_users_to_change = bool @@ -57,7 +62,6 @@ variable "aws_auditmanager" { variable "aws_config" { type = object({ aggregator_account_ids = optional(list(string), []) - aggregator_regions = optional(list(string), []) delivery_channel_s3_bucket_name = optional(string, null) delivery_channel_s3_key_prefix = optional(string, null) delivery_frequency = optional(string, "TwentyFour_Hours") @@ -65,7 +69,6 @@ variable "aws_config" { }) default = { aggregator_account_ids = [] - aggregator_regions = [] delivery_channel_s3_bucket_name = null delivery_channel_s3_key_prefix = null delivery_frequency = "TwentyFour_Hours" @@ -151,13 +154,12 @@ variable "aws_required_tags" { variable "aws_security_hub" { type = object({ - aggregator_linking_mode = optional(string, "ALL_REGIONS") - aggregator_specified_regions = optional(list(string), null) - auto_enable_controls = optional(bool, true) - control_finding_generator = optional(string, "SECURITY_CONTROL") - create_cis_metric_filters = optional(bool, true) - product_arns = optional(list(string), []) - standards_arns = optional(list(string), null) + aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS") + auto_enable_controls = optional(bool, true) + control_finding_generator = optional(string, "SECURITY_CONTROL") + create_cis_metric_filters = optional(bool, true) + product_arns = optional(list(string), []) + standards_arns = optional(list(string), null) }) default = {} description = "AWS Security Hub settings" @@ -166,6 +168,11 @@ variable "aws_security_hub" { condition = contains(["SECURITY_CONTROL", "STANDARD_CONTROL"], var.aws_security_hub.control_finding_generator) error_message = "The \"control_finding_generator\" variable must be set to either \"SECURITY_CONTROL\" or \"STANDARD_CONTROL\"." } + + validation { + condition = var.aws_security_hub.aggregator_linking_mode != "ALL_REGIONS" + error_message = "Security Hub Linking mode cannot be set to \"ALL_REGIONS\" since AWS Config needs to be configured in all regions individually." + } } variable "aws_security_hub_sns_subscription" { From 5ece85bea47b2c80f7c59f44203a73b0197dfe9a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 17 Dec 2024 13:26:09 +0000 Subject: [PATCH 15/28] docs(readme): update module usage --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 46683d6..26d2e83 100644 --- a/README.md +++ b/README.md @@ -537,18 +537,19 @@ module "landing_zone" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [allowed\_regions](#input\_allowed\_regions) | List of AWS regions where operations are allowed and for which central services like Security Hub and AWS Config are configured. | `list(string)` | n/a | yes | | [control\_tower\_account\_ids](#input\_control\_tower\_account\_ids) | Control Tower core account IDs |
object({
audit = string
logging = string
})
| n/a | yes | | [tags](#input\_tags) | Map of tags | `map(string)` | n/a | yes | | [additional\_auditing\_trail](#input\_additional\_auditing\_trail) | CloudTrail configuration for additional auditing trail |
object({
name = string
bucket = string
kms_key_id = string

event_selector = optional(object({
data_resource = optional(object({
type = string
values = list(string)
}))
exclude_management_event_sources = optional(set(string), null)
include_management_events = optional(bool, true)
read_write_type = optional(string, "All")
}))
})
| `null` | no | | [aws\_account\_password\_policy](#input\_aws\_account\_password\_policy) | AWS account password policy parameters for the audit, logging and master account |
object({
allow_users_to_change = bool
max_age = number
minimum_length = number
require_lowercase_characters = bool
require_numbers = bool
require_symbols = bool
require_uppercase_characters = bool
reuse_prevention_history = number
})
|
{
"allow_users_to_change": true,
"max_age": 90,
"minimum_length": 14,
"require_lowercase_characters": true,
"require_numbers": true,
"require_symbols": true,
"require_uppercase_characters": true,
"reuse_prevention_history": 24
}
| no | | [aws\_auditmanager](#input\_aws\_auditmanager) | AWS Audit Manager config settings |
object({
enabled = bool
reports_bucket_prefix = string
})
|
{
"enabled": true,
"reports_bucket_prefix": "audit-manager-reports"
}
| no | -| [aws\_config](#input\_aws\_config) | AWS Config settings |
object({
aggregator_account_ids = optional(list(string), [])
aggregator_regions = optional(list(string), [])
delivery_channel_s3_bucket_name = optional(string, null)
delivery_channel_s3_key_prefix = optional(string, null)
delivery_frequency = optional(string, "TwentyFour_Hours")
rule_identifiers = optional(list(string), [])
})
|
{
"aggregator_account_ids": [],
"aggregator_regions": [],
"delivery_channel_s3_bucket_name": null,
"delivery_channel_s3_key_prefix": null,
"delivery_frequency": "TwentyFour_Hours",
"rule_identifiers": []
}
| no | +| [aws\_config](#input\_aws\_config) | AWS Config settings |
object({
aggregator_account_ids = optional(list(string), [])
delivery_channel_s3_bucket_name = optional(string, null)
delivery_channel_s3_key_prefix = optional(string, null)
delivery_frequency = optional(string, "TwentyFour_Hours")
rule_identifiers = optional(list(string), [])
})
|
{
"aggregator_account_ids": [],
"delivery_channel_s3_bucket_name": null,
"delivery_channel_s3_key_prefix": null,
"delivery_frequency": "TwentyFour_Hours",
"rule_identifiers": []
}
| no | | [aws\_config\_sns\_subscription](#input\_aws\_config\_sns\_subscription) | Subscription options for the aws-controltower-AggregateSecurityNotifications (AWS Config) SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_ebs\_encryption\_by\_default](#input\_aws\_ebs\_encryption\_by\_default) | Set to true to enable AWS Elastic Block Store encryption by default | `bool` | `true` | no | | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "ALL_REGIONS")
aggregator_specified_regions = optional(list(string), null)
auto_enable_controls = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS")
auto_enable_controls = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | From 3e36ef609eb400381791d06132ed25bccc628807 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 17 Dec 2024 15:02:10 +0100 Subject: [PATCH 16/28] Ensure home region is not used for findings aggregator --- security_hub.tf | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/security_hub.tf b/security_hub.tf index bcd85e8..a6bcac8 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -1,3 +1,7 @@ +locals { + all_regions_except_home_region = setsubtract(var.allowed_regions, (data.aws_region.current.name)) +} + // AWS Security Hub - Management account configuration and enrollment resource "aws_securityhub_organization_admin_account" "default" { admin_account_id = data.aws_caller_identity.audit.account_id @@ -107,10 +111,11 @@ resource "aws_securityhub_member" "logging" { resource "aws_securityhub_finding_aggregator" "default" { + count = length(local.all_regions_except_home_region) == 0 ? 0 : 1 provider = aws.audit linking_mode = var.aws_security_hub.aggregator_linking_mode - specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? var.allowed_regions : null + specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? local.all_regions_except_home_region : null depends_on = [aws_securityhub_account.default] } From 341311f5d09a8e8ad42b69507175d7b55227dba3 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 17 Dec 2024 16:07:50 +0100 Subject: [PATCH 17/28] Ensure allowed_regions contains at least one region. --- config.tf | 4 ++-- locals.tf | 4 ++++ organizations_policy.tf | 4 ++-- security_hub.tf | 8 ++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/config.tf b/config.tf index 74079f2..203705e 100644 --- a/config.tf +++ b/config.tf @@ -1,7 +1,7 @@ locals { aws_config_aggregators = flatten([ for account in toset(try(var.aws_config.aggregator_account_ids, [])) : [ - for region in toset(try(var.allowed_regions, [])) : { + for region in toset(try(local.allowed_regions_with_us_east, [])) : { account_id = account region = region } @@ -32,7 +32,7 @@ resource "aws_config_aggregate_authorization" "master" { } resource "aws_config_aggregate_authorization" "master_to_audit" { - for_each = toset(coalescelist(var.allowed_regions, [data.aws_region.current.name])) + for_each = local.allowed_regions_with_us_east account_id = var.control_tower_account_ids.audit region = each.value diff --git a/locals.tf b/locals.tf index 0727e22..d6d9659 100644 --- a/locals.tf +++ b/locals.tf @@ -34,4 +34,8 @@ locals { security_hub_has_cis_aws_foundations_enabled = length(regexall( "cis-aws-foundations-benchmark/v", join(",", local.security_hub_standards_arns) )) > 0 ? true : false + + allowed_regions = toset(distinct(concat(var.allowed_regions, [data.aws_region.current.name]))) + allowed_regions_with_us_east = toset(distinct(concat(var.allowed_regions, [data.aws_region.current.name], ["us-east-1"]))) + allowed_regions_except_home_region = setsubtract(local.allowed_regions_with_us_east, [data.aws_region.current.name]) } diff --git a/organizations_policy.tf b/organizations_policy.tf index f515965..8803971 100644 --- a/organizations_policy.tf +++ b/organizations_policy.tf @@ -2,8 +2,8 @@ locals { enabled_root_policies = { allowed_regions = { enable = var.allowed_regions != null ? true : false - policy = var.allowed_regions != null ? templatefile("${path.module}/files/organizations/allowed_regions.json.tpl", { - allowed = var.allowed_regions != null ? var.allowed_regions : [] + policy = local.allowed_regions != null ? templatefile("${path.module}/files/organizations/allowed_regions.json.tpl", { + allowed = var.allowed_regions != null ? local.allowed_regions : [] exceptions = local.aws_service_control_policies_principal_exceptions }) : null } diff --git a/security_hub.tf b/security_hub.tf index a6bcac8..04eec56 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -1,7 +1,3 @@ -locals { - all_regions_except_home_region = setsubtract(var.allowed_regions, (data.aws_region.current.name)) -} - // AWS Security Hub - Management account configuration and enrollment resource "aws_securityhub_organization_admin_account" "default" { admin_account_id = data.aws_caller_identity.audit.account_id @@ -111,11 +107,11 @@ resource "aws_securityhub_member" "logging" { resource "aws_securityhub_finding_aggregator" "default" { - count = length(local.all_regions_except_home_region) == 0 ? 0 : 1 + count = length(local.allowed_regions_except_home_region) == 0 ? 0 : 1 provider = aws.audit linking_mode = var.aws_security_hub.aggregator_linking_mode - specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? local.all_regions_except_home_region : null + specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? local.allowed_regions_except_home_region : null depends_on = [aws_securityhub_account.default] } From f63139ed79570caff0bd6e52331ed426f090bcb1 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Wed, 18 Dec 2024 09:05:05 +0100 Subject: [PATCH 18/28] Allow disabling of securityhub controls by ID --- UPGRADING.md | 7 ++++++- security_hub.tf | 2 +- variables.tf | 13 +++++++------ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 23ac501..8b0eb76 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -9,12 +9,17 @@ This document captures required refactoring on your part when upgrading to a mod > [!IMPORTANT] > **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html).** -This version enables Security Hub Findings Aggregation for all regions. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` or `SPECIFIED_REGIONS` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions` +This version enables Security Hub Findings Aggregation for all regions specfied in `allowed_regions`. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions`. + + +### Variables The following variables have been replaced: * `aws_service_control_policies.allowed_regions` -> `allowed_regions` * `aws_config.aggregator_regions` -> `allowed_regions` +The following variable is added: +* `aws_security_hub.disabled_control_identifiers`. List of Security Hub control IDs that are disabled in the organisation. ## Upgrading to v4.0.0 diff --git a/security_hub.tf b/security_hub.tf index 04eec56..083837a 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -127,7 +127,7 @@ resource "aws_securityhub_configuration_policy" "default" { enabled_standard_arns = local.security_hub_standards_arns security_controls_configuration { - disabled_control_identifiers = [] + disabled_control_identifiers = var.aws_security_hub.disabled_control_identifiers } } diff --git a/variables.tf b/variables.tf index 56f60f3..05266d0 100644 --- a/variables.tf +++ b/variables.tf @@ -154,12 +154,13 @@ variable "aws_required_tags" { variable "aws_security_hub" { type = object({ - aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS") - auto_enable_controls = optional(bool, true) - control_finding_generator = optional(string, "SECURITY_CONTROL") - create_cis_metric_filters = optional(bool, true) - product_arns = optional(list(string), []) - standards_arns = optional(list(string), null) + aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS") + auto_enable_controls = optional(bool, true) + control_finding_generator = optional(string, "SECURITY_CONTROL") + create_cis_metric_filters = optional(bool, true) + product_arns = optional(list(string), []) + standards_arns = optional(list(string), null) + disabled_control_identifiers = optional(list(string), []) }) default = {} description = "AWS Security Hub settings" From c3cc4b9e52a26e6368c593adec5d190b76ea1177 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 18 Dec 2024 08:05:25 +0000 Subject: [PATCH 19/28] docs(readme): update module usage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26d2e83..95a80e9 100644 --- a/README.md +++ b/README.md @@ -549,7 +549,7 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS")
auto_enable_controls = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS")
auto_enable_controls = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
disabled_control_identifiers = optional(list(string), [])
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | From 5dc95f8afd476511d1d3d472c80a926489b82b42 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Wed, 18 Dec 2024 14:56:51 +0100 Subject: [PATCH 20/28] Allow enabling of securityhub controls by ID --- README.md | 10 ++++++++-- UPGRADING.md | 22 ++++++++++++++++++++++ config.tf | 6 ++++++ examples/basic/main.tf | 8 +++++++- security_hub.tf | 1 + variables.tf | 8 ++++---- versions.tf | 2 +- 7 files changed, 49 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 95a80e9..183b34a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,11 @@ locals { provider "aws" {} +provider "aws" { + alias = "us-east-1" + region = "us-east-1" +} + provider "aws" { alias = "audit" @@ -49,12 +54,13 @@ provider "mcaf" { } module "landing_zone" { - providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging } + providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging, aws.us-east-1 = aws.us-east-1 } source = "github.com/schubergphilis/terraform-aws-mcaf-landing-zone?ref=VERSION" control_tower_account_ids = local.control_tower_account_ids - tags = { Terraform = true } + allowed_regions = ["eu-central-1", "eu-west-1"] + tags = { Terraform = true } } ``` diff --git a/UPGRADING.md b/UPGRADING.md index 8b0eb76..17bd7d1 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -11,6 +11,27 @@ This document captures required refactoring on your part when upgrading to a mod This version enables Security Hub Findings Aggregation for all regions specfied in `allowed_regions`. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions`. +### Providers + +Since global AWS services have their origin in the `us-east-1` region, this version requires the following provider to be set and passed to the module: +```hcl +provider "aws" { + alias = "us-east-1" + region = "us-east-1" +} + +[...] + +module "landing_zone" { + providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging, aws.us-east-1 = aws.us-east-1 } + + source = "github.com/schubergphilis/terraform-aws-mcaf-landing-zone?ref=VERSION" + + control_tower_account_ids = local.control_tower_account_ids + allowed_regions = ["eu-central-1", "eu-west-1"] + tags = { Terraform = true } +} +``` ### Variables @@ -20,6 +41,7 @@ The following variables have been replaced: The following variable is added: * `aws_security_hub.disabled_control_identifiers`. List of Security Hub control IDs that are disabled in the organisation. +* `aws_security_hub.enabled_control_identifiers`. List of Security Hub control IDs that are enabled in the organisation. ## Upgrading to v4.0.0 diff --git a/config.tf b/config.tf index 203705e..00f6243 100644 --- a/config.tf +++ b/config.tf @@ -43,6 +43,12 @@ resource "aws_iam_service_linked_role" "config" { aws_service_name = "config.amazonaws.com" } +resource "aws_iam_service_linked_role" "config_us" { + provider = aws.us-east-1 + + aws_service_name = "config.amazonaws.com" +} + resource "aws_config_configuration_recorder" "default" { name = "default" role_arn = aws_iam_service_linked_role.config.arn diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 3f33bca..e8b42a6 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -9,6 +9,11 @@ provider "aws" { region = "eu-west-1" } +provider "aws" { + alias = "us-east-1" + region = "us-east-1" +} + provider "aws" { alias = "audit" region = "eu-west-1" @@ -36,10 +41,11 @@ provider "mcaf" { } module "landing_zone" { - providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging } + providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging, aws.us-east-1 = aws.us-east-1 } source = "../../" control_tower_account_ids = local.control_tower_account_ids + allowed_regions = ["eu-central-1", "eu-west-1"] tags = { Terraform = true } } diff --git a/security_hub.tf b/security_hub.tf index 083837a..20f7576 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -128,6 +128,7 @@ resource "aws_securityhub_configuration_policy" "default" { security_controls_configuration { disabled_control_identifiers = var.aws_security_hub.disabled_control_identifiers + enabled_control_identifiers = var.aws_security_hub.enabled_control_identifiers } } diff --git a/variables.tf b/variables.tf index 05266d0..9637ab5 100644 --- a/variables.tf +++ b/variables.tf @@ -158,9 +158,10 @@ variable "aws_security_hub" { auto_enable_controls = optional(bool, true) control_finding_generator = optional(string, "SECURITY_CONTROL") create_cis_metric_filters = optional(bool, true) + disabled_control_identifiers = optional(list(string), null) + enabled_control_identifiers = optional(list(string), null) product_arns = optional(list(string), []) standards_arns = optional(list(string), null) - disabled_control_identifiers = optional(list(string), []) }) default = {} description = "AWS Security Hub settings" @@ -171,8 +172,8 @@ variable "aws_security_hub" { } validation { - condition = var.aws_security_hub.aggregator_linking_mode != "ALL_REGIONS" - error_message = "Security Hub Linking mode cannot be set to \"ALL_REGIONS\" since AWS Config needs to be configured in all regions individually." + condition = length(var.aws_security_hub.enabled_control_identifiers) == 0 || length(var.aws_security_hub.disabled_control_identifiers) == 0 + error_message = "Only one of \"enabled_control_identifiers\" or \"disabled_control_identifiers\" can be set." } } @@ -187,7 +188,6 @@ variable "aws_security_hub_sns_subscription" { variable "aws_service_control_policies" { type = object({ - allowed_regions = optional(list(string), []) aws_deny_disabling_security_hub = optional(bool, true) aws_deny_leaving_org = optional(bool, true) aws_deny_root_user_ous = optional(list(string), []) diff --git a/versions.tf b/versions.tf index ff18295..032025c 100644 --- a/versions.tf +++ b/versions.tf @@ -3,7 +3,7 @@ terraform { aws = { source = "hashicorp/aws" version = ">= 5.54.0" - configuration_aliases = [aws.audit, aws.logging] + configuration_aliases = [aws.audit, aws.logging, aws.us-east-1] } datadog = { source = "datadog/datadog" From 200076f63d12f95e62ba5a491d6957eec886feec Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Wed, 18 Dec 2024 15:00:42 +0100 Subject: [PATCH 21/28] Assume AWS Config is enabled externally for linked regions --- README.md | 7 +------ UPGRADING.md | 24 +----------------------- config.tf | 6 ------ examples/basic/main.tf | 7 +------ versions.tf | 2 +- 5 files changed, 4 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 183b34a..a952747 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,6 @@ locals { provider "aws" {} -provider "aws" { - alias = "us-east-1" - region = "us-east-1" -} - provider "aws" { alias = "audit" @@ -54,7 +49,7 @@ provider "mcaf" { } module "landing_zone" { - providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging, aws.us-east-1 = aws.us-east-1 } + providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging } source = "github.com/schubergphilis/terraform-aws-mcaf-landing-zone?ref=VERSION" diff --git a/UPGRADING.md b/UPGRADING.md index 17bd7d1..eb1594c 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -7,32 +7,10 @@ This document captures required refactoring on your part when upgrading to a mod ### Behaviour > [!IMPORTANT] -> **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html).** +> **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html) and always enabled Security Hub in the us-east-1 region.** This version enables Security Hub Findings Aggregation for all regions specfied in `allowed_regions`. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions`. -### Providers - -Since global AWS services have their origin in the `us-east-1` region, this version requires the following provider to be set and passed to the module: -```hcl -provider "aws" { - alias = "us-east-1" - region = "us-east-1" -} - -[...] - -module "landing_zone" { - providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging, aws.us-east-1 = aws.us-east-1 } - - source = "github.com/schubergphilis/terraform-aws-mcaf-landing-zone?ref=VERSION" - - control_tower_account_ids = local.control_tower_account_ids - allowed_regions = ["eu-central-1", "eu-west-1"] - tags = { Terraform = true } -} -``` - ### Variables The following variables have been replaced: diff --git a/config.tf b/config.tf index 00f6243..203705e 100644 --- a/config.tf +++ b/config.tf @@ -43,12 +43,6 @@ resource "aws_iam_service_linked_role" "config" { aws_service_name = "config.amazonaws.com" } -resource "aws_iam_service_linked_role" "config_us" { - provider = aws.us-east-1 - - aws_service_name = "config.amazonaws.com" -} - resource "aws_config_configuration_recorder" "default" { name = "default" role_arn = aws_iam_service_linked_role.config.arn diff --git a/examples/basic/main.tf b/examples/basic/main.tf index e8b42a6..465f23c 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -9,11 +9,6 @@ provider "aws" { region = "eu-west-1" } -provider "aws" { - alias = "us-east-1" - region = "us-east-1" -} - provider "aws" { alias = "audit" region = "eu-west-1" @@ -41,7 +36,7 @@ provider "mcaf" { } module "landing_zone" { - providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging, aws.us-east-1 = aws.us-east-1 } + providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging } source = "../../" diff --git a/versions.tf b/versions.tf index 032025c..ff18295 100644 --- a/versions.tf +++ b/versions.tf @@ -3,7 +3,7 @@ terraform { aws = { source = "hashicorp/aws" version = ">= 5.54.0" - configuration_aliases = [aws.audit, aws.logging, aws.us-east-1] + configuration_aliases = [aws.audit, aws.logging] } datadog = { source = "datadog/datadog" From 0319956b967f7e7e6417699df8289074b78301ac Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Thu, 19 Dec 2024 11:23:11 +0100 Subject: [PATCH 22/28] Split regions --- config.tf | 4 ++-- locals.tf | 5 +---- organizations_policy.tf | 6 +++--- security_hub.tf | 4 ++-- variables.tf | 12 ++++++++---- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/config.tf b/config.tf index 203705e..dd9366c 100644 --- a/config.tf +++ b/config.tf @@ -1,7 +1,7 @@ locals { aws_config_aggregators = flatten([ for account in toset(try(var.aws_config.aggregator_account_ids, [])) : [ - for region in toset(try(local.allowed_regions_with_us_east, [])) : { + for region in toset(try(local.all_organisation_regions, [])) : { account_id = account region = region } @@ -32,7 +32,7 @@ resource "aws_config_aggregate_authorization" "master" { } resource "aws_config_aggregate_authorization" "master_to_audit" { - for_each = local.allowed_regions_with_us_east + for_each = local.all_organisation_regions account_id = var.control_tower_account_ids.audit region = each.value diff --git a/locals.tf b/locals.tf index d6d9659..08b6cd1 100644 --- a/locals.tf +++ b/locals.tf @@ -34,8 +34,5 @@ locals { security_hub_has_cis_aws_foundations_enabled = length(regexall( "cis-aws-foundations-benchmark/v", join(",", local.security_hub_standards_arns) )) > 0 ? true : false - - allowed_regions = toset(distinct(concat(var.allowed_regions, [data.aws_region.current.name]))) - allowed_regions_with_us_east = toset(distinct(concat(var.allowed_regions, [data.aws_region.current.name], ["us-east-1"]))) - allowed_regions_except_home_region = setsubtract(local.allowed_regions_with_us_east, [data.aws_region.current.name]) + all_organisation_regions = toset(distinct(concat([var.regions.home_region], var.regions.linked_regions, var.regions.allowed_regions, [data.aws_region.current.name]))) } diff --git a/organizations_policy.tf b/organizations_policy.tf index 8803971..92a0bba 100644 --- a/organizations_policy.tf +++ b/organizations_policy.tf @@ -1,9 +1,9 @@ locals { enabled_root_policies = { allowed_regions = { - enable = var.allowed_regions != null ? true : false - policy = local.allowed_regions != null ? templatefile("${path.module}/files/organizations/allowed_regions.json.tpl", { - allowed = var.allowed_regions != null ? local.allowed_regions : [] + enable = var.regions.allowed_regions != null ? true : false + policy = var.regions.allowed_regions != null ? templatefile("${path.module}/files/organizations/allowed_regions.json.tpl", { + allowed = var.regions.allowed_regions != null ? var.regions.allowed_regions : [] exceptions = local.aws_service_control_policies_principal_exceptions }) : null } diff --git a/security_hub.tf b/security_hub.tf index 20f7576..cdfd934 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -107,11 +107,11 @@ resource "aws_securityhub_member" "logging" { resource "aws_securityhub_finding_aggregator" "default" { - count = length(local.allowed_regions_except_home_region) == 0 ? 0 : 1 + count = length(var.regions.linked_regions) == 0 ? 0 : 1 provider = aws.audit linking_mode = var.aws_security_hub.aggregator_linking_mode - specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? local.allowed_regions_except_home_region : null + specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? var.regions.linked_regions : null depends_on = [aws_securityhub_account.default] } diff --git a/variables.tf b/variables.tf index 9637ab5..8884770 100644 --- a/variables.tf +++ b/variables.tf @@ -18,9 +18,13 @@ variable "additional_auditing_trail" { description = "CloudTrail configuration for additional auditing trail" } -variable "allowed_regions" { - type = list(string) - description = "List of AWS regions where operations are allowed and for which central services like Security Hub and AWS Config are configured." +variable "regions" { + type = object({ + allowed_regions = list(string) + home_region = string + linked_regions = optional(list(string), ["us-east-1"]) + }) + description = "Regions for your AWS organisation. More information: https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html" } variable "aws_account_password_policy" { @@ -172,7 +176,7 @@ variable "aws_security_hub" { } validation { - condition = length(var.aws_security_hub.enabled_control_identifiers) == 0 || length(var.aws_security_hub.disabled_control_identifiers) == 0 + condition = try(length(var.aws_security_hub.enabled_control_identifiers), 0) == 0 || try(length(var.aws_security_hub.disabled_control_identifiers), 0) == 0 error_message = "Only one of \"enabled_control_identifiers\" or \"disabled_control_identifiers\" can be set." } } From 2b50aef688925f97d9a9499c799844d8bde3773c Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Thu, 19 Dec 2024 13:53:33 +0100 Subject: [PATCH 23/28] [skip ci] update readme --- UPGRADING.md | 8 ++++---- examples/basic/main.tf | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index eb1594c..0ee3bd6 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -9,15 +9,15 @@ This document captures required refactoring on your part when upgrading to a mod > [!IMPORTANT] > **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html) and always enabled Security Hub in the us-east-1 region.** -This version enables Security Hub Findings Aggregation for all regions specfied in `allowed_regions`. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions`. +This version enables Security Hub Findings Aggregation for all regions specfied in `regions.home_region` and `regions.linked_regions`. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions`. More information on this in the [AWS Security Hub Documentation](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). ### Variables The following variables have been replaced: -* `aws_service_control_policies.allowed_regions` -> `allowed_regions` -* `aws_config.aggregator_regions` -> `allowed_regions` +* `aws_service_control_policies.allowed_regions` -> `regions.allowed_regions` +* `aws_config.aggregator_regions` -> the union of `regions.home_region` and `regions.linked_regions` -The following variable is added: +The following variables are added: * `aws_security_hub.disabled_control_identifiers`. List of Security Hub control IDs that are disabled in the organisation. * `aws_security_hub.enabled_control_identifiers`. List of Security Hub control IDs that are enabled in the organisation. diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 465f23c..1d28a7c 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -41,6 +41,10 @@ module "landing_zone" { source = "../../" control_tower_account_ids = local.control_tower_account_ids - allowed_regions = ["eu-central-1", "eu-west-1"] - tags = { Terraform = true } + regions = { + allowed_regions = ["eu-central-1", "eu-west-1"] + home_region = "eu-central-1" + linked_regions = ["eu-west-1"] + } + tags = { Terraform = true } } From dc00853339884eaf1f030bf0d119ac5014710293 Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Thu, 19 Dec 2024 14:42:01 +0100 Subject: [PATCH 24/28] [skip ci] update readme --- UPGRADING.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/UPGRADING.md b/UPGRADING.md index 0ee3bd6..9020221 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -7,10 +7,31 @@ This document captures required refactoring on your part when upgrading to a mod ### Behaviour > [!IMPORTANT] -> **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html) and always enabled Security Hub in the us-east-1 region.** +> **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). This version enables Security Hub Findings Aggregation for all regions specfied in `regions.home_region` and `regions.linked_regions`. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions`. More information on this in the [AWS Security Hub Documentation](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). +### Removing local Security Hub Standards in logging account + +Since the state of version < 5.0.0 contains an unknown number of instances of `aws_securityhub_standards_subscription` resources for the Logging account, Terraform wants to remove them. Since the configuration of this is moved to the central `aws_securityhub_configuration_policy` resource, we don't want to disable them, but just 'forget' about them. + +Since there is an unknown number of instances (a for_each on `local.security_hub_standards_arns`) and [Terraform does not support `for_each` on `removed` statements yet](https://github.com/hashicorp/terraform/issues/34439), we need to remove the resources manually from the state. + +The following shell snippet generates the removal statements: + +```shell +terraform init +for local_standard in $(terraform state list | grep "module.landing_zone.aws_securityhub_standards_subscription.logging"); do + echo "terraform state rm '$local_standard'" +done +``` + +Evaluate the output and run the commands. The statements should look something like this: + +```shell +terraform state rm 'module.landing_zone.aws_securityhub_standards_subscription.logging["arn:aws:securityhub:eu-central-1::standards/pci-dss/v/3.2.1"]' +``` + ### Variables The following variables have been replaced: From 45c794f9901b1a65f1ea63537b8db52b39b93a8e Mon Sep 17 00:00:00 2001 From: Marwin Baumann Date: Mon, 23 Dec 2024 19:38:00 +0100 Subject: [PATCH 25/28] improve documentation, formatting and code placement --- README.md | 113 ++++++++++++++++++++++++----------------- UPGRADING.md | 99 +++++++++++++++++++++++++++++------- config.tf | 2 + examples/basic/main.tf | 11 ++-- security_hub.tf | 73 +++++++++++++------------- variables.tf | 32 ++++++++---- 6 files changed, 211 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index a952747..8b06924 100644 --- a/README.md +++ b/README.md @@ -8,56 +8,82 @@ Overview of Landing Zone tools & services: The SBP AWS Landing Zone consists of 3 repositories: -- [MCAF Landing Zone module (current repository)](https://github.com/schubergphilis/terraform-aws-mcaf-landing-zone): the foundation of the Landing Zone and manages the 3 core accounts: audit, logging, master +- [MCAF Landing Zone module (current repository)](https://github.com/schubergphilis/terraform-aws-mcaf-landing-zone): the foundation of the Landing Zone and manages the 3 core accounts: audit, logging, management - [MCAF Account Vending Machine (AVM) module](https://github.com/schubergphilis/terraform-aws-mcaf-avm): providing an AWS AVM. This module sets up an AWS account with one or more Terraform Cloud/Enterprise (TFE) workspace(s) backed by a VCS project - [MCAF Account Baseline module](https://github.com/schubergphilis/terraform-aws-mcaf-account-baseline): optional module providing baseline configuration for AWS accounts + +## Pre-Requisites + +> [!IMPORTANT] +> Before deploying this module, ensure the following pre-requisites are met: +> - AWS Control Tower is deployed in the `core-management` account. +> - AWS Control Tower governed regions include at least `us-east-1` (and your designated home region). + ## Basic configuration -```hcl -locals { - control_tower_account_ids = { - audit = "012345678902" - logging = "012345678903" - } -} +Refer to [examples/basic](examples/basic/main.tf) for an example of minimal setup. -provider "aws" {} +### Specifying the correct regions -provider "aws" { - alias = "audit" +**Home Region** - assume_role { - role_arn = "arn:aws:iam::${local.control_tower_account_ids.audit}:role/AWSControlTowerExecution" - } -} +The mandatory `regions.home_region` variable specifies the AWS Control Tower home region. This must match the region defined in your AWS provider that deploys this module. -provider "aws" { - alias = "logging" +To find your home region: +1. Log in to the **core-management account**. +2. Navigate to **AWS Control Tower** → **Landing Zone Settings**. +3. The home region is listed under **Home Region**. - assume_role { - role_arn = "arn:aws:iam::${local.control_tower_account_ids.logging}:role/AWSControlTowerExecution" - } -} +**Linked Regions** -provider "datadog" { - validate = false -} +The optional `regions.linked_regions` variable defines the AWS Control Tower governed regions. This module ensures proper configuration of AWS Security Hub and AWS Config for all specified linked regions to collect data from them. -provider "mcaf" { - aws {} -} +To find your linked regions: +1. Log in to the **core-management account**. +2. Navigate to **AWS Control Tower** → **Landing Zone Settings**. +3. Linked regions are listed under **Landing Zone Regions**. -module "landing_zone" { - providers = { aws = aws, aws.audit = aws.audit, aws.logging = aws.logging } +*Note:* By default, `us-east-1` is included as a linked region to ensure data collection from global services. To restrict deployment of non-global resources in this region, use the `allowed_regions` functionality described in the section below. + +> [!IMPORTANT] +> All specified linked regions need to be an AWS Control Tower governed region. This ensures that an AWS Config recorder is enabled by AWS Control Tower in all governed regions. AWS Security Hub will only function correctly if an AWS Config recorder exists in all linked regions. + +**Allowed Regions** + +The optional `regions.allowed_regions` variable defines the allowed regions within your AWS Organization. This triggers the deployment of a [Service Control Policy (SCP)](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps_examples.html#example-scp-deny-region), which is attached to the root of your AWS Organization. - source = "github.com/schubergphilis/terraform-aws-mcaf-landing-zone?ref=VERSION" +#### Configuration Scenarios - control_tower_account_ids = local.control_tower_account_ids - allowed_regions = ["eu-central-1", "eu-west-1"] - tags = { Terraform = true } +**Scenario 1: Home region only (no deployment in other regions)** + +- **Home region:** `eu-central-1` +- **Requirement:** Prevent deployment in all other regions. + +You need to configure the `regions` variable as follows: + +```hcl +regions = { + allowed_regions = ["eu-central-1"] + home_region = "eu-central-1" } +``` + +*Note:* Ensure that `us-east-1` is included as a governed region in AWS Control Tower since the `linked_region` variable defaults to this value. + +**Scenario 2: Home region with additional governed regions** + +- **Home region:** `eu-central-1` +- **Requirement:** Also allow deploying resources in `eu-west-1`. + +You need to configure the `regions` variable as follows: +```hcl +regions = { + allowed_regions = ["eu-central-1", "eu-west-1] + home_region = "eu-central-1" + linked_regions = ["eu-west-1", "us-east-1"] +} ``` ## Detailed configuration @@ -102,9 +128,9 @@ additional_auditing_trail = { ### AWS Config Rules -This module provisions by default a set of basic AWS Config Rules. In order to add extra rules, a list of [rule identifiers](https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html) can be passed via the variable `aws_config` using the attribute `rule_identifiers`. +This module provisions by default a set of basic AWS Config Rules. In order to add extra rules, a list of [rule identifiers](https://docs.aws.amazon.com/config/latest/developerguide/managed-rules-by-aws-config.html) can be passed via the variable `aws_config.rule_identifiers`. -If you would like to authorize other accounts to aggregate AWS Config data, the account IDs and regions can also be passed via the variable `aws_config` using the attributes `aggregator_account_ids` and `aggregator_regions` respectively. +If you would like to authorize other accounts to aggregate AWS Config data, the account IDs can also be passed via the variable `aws_config.aggregator_account_ids`. NOTE: This module already authorizes the `audit` account to aggregate Config data from all other accounts in the organization, so there is no need to specify the `audit` account ID in the `aggregator_account_ids` list. @@ -113,7 +139,6 @@ Example: ```hcl aws_config = { aggregator_account_ids = ["123456789012"] - aggregator_regions = ["eu-west-1"] rule_identifiers = ["ACCESS_KEYS_ROTATED", "ALB_WAF_ENABLED"] } ``` @@ -122,13 +147,13 @@ aws_config = { This module supports enabling GuardDuty at the organization level which means that all new accounts that are created in, or added to, the organization are added as member accounts to the `audit` account GuardDuty detector. -The feature can be controlled via the `aws_guardduty` variable and is enabled by default. The finding publishing frequency has been reduced from 6 hours to every 15 minutes, and the Malware Protection, Kubernetes and S3 Logs data sources are enabled out of the box. +The feature can be controlled via the `aws_guardduty` variable and is enabled by default. -Note: In case you are migrating an existing AWS organization to this module, all existing accounts except for the `master` and `logging` accounts have to be enabled like explained [here](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_organizations.html#guardduty_add_orgs_accounts). +Note: In case you are migrating an existing AWS organization to this module, all existing accounts except for the `management` and `logging` accounts have to be enabled like explained [here](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_organizations.html#guardduty_add_orgs_accounts). ## AWS KMS -The module creates 3 AWS KMS keys, one for the master account, one for the audit account, and one for the log archive account. We recommend to further scope down the AWS KMS key policy in the master account by providing a secure policy using `kms_key_policy`. The default policy "Base Permissions" can be overwritten and should be limited to the root account only, for example by using the statement below: +The module creates 3 AWS KMS keys, one for the management account, one for the audit account, and one for the log archive account. We recommend to further scope down the AWS KMS key policy in the management account by providing a secure policy using `kms_key_policy`. The default policy "Base Permissions" can be overwritten and should be limited to the root account only, for example by using the statement below: ```hcl statement { @@ -321,15 +346,7 @@ aws_service_control_policies = { #### SCP: Restricting AWS Regions -If you would like to define which AWS Regions can be used in your AWS Organization, you can pass a list of region names to the variable `aws_service_control_policies` using the `allowed_regions` attribute. This will trigger this module to deploy a [Service Control Policy (SCP) designed by AWS](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps_examples.html#example-scp-deny-region) and attach it to the root of your AWS Organization. - -Example: - -```hcl -aws_service_control_policies = { - allowed_regions = ["eu-west-1"] -} -``` +See the section `Specifying the correct regions`. #### SCP: Restricting Root User Access diff --git a/UPGRADING.md b/UPGRADING.md index 9020221..d9f49aa 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -4,43 +4,108 @@ This document captures required refactoring on your part when upgrading to a mod ## Upgrading to v5.0.0 -### Behaviour +### Key Changes -> [!IMPORTANT] -> **This version changes the [Security Hub configuration to Central](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). +#### Transition to Centralized Security Hub Configuration + +This version transitions Security Hub configuration from **Local** to **Central**. Learn more in the [AWS Security Hub Documentation](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). + +**Default Behavior:** + +- Security Hub Findings Aggregation is enabled for regions defined in: + - `regions.home_region` + - `regions.linked_regions`. `us-east-1` is automatically included for global services. -This version enables Security Hub Findings Aggregation for all regions specfied in `regions.home_region` and `regions.linked_regions`. You can change this behauviour by setting `var.aws_security_hub.aggregator_linking_mode` to `ALL_REGIONS_EXCEPT_SPECIFIED` and providing the list of regions via `var.aws_security_hub.aggregator_specified_regions`. More information on this in the [AWS Security Hub Documentation](https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html). +#### Dropping Support for Local Configuration -### Removing local Security Hub Standards in logging account +**Local configurations are no longer supported.** Centralized configuration aligns with AWS best practices and reduces complexity. -Since the state of version < 5.0.0 contains an unknown number of instances of `aws_securityhub_standards_subscription` resources for the Logging account, Terraform wants to remove them. Since the configuration of this is moved to the central `aws_securityhub_configuration_policy` resource, we don't want to disable them, but just 'forget' about them. +### Variables + +The following variables have been replaced: +* `aws_service_control_policies.allowed_regions` → `regions.allowed_regions` +* `aws_config.aggregator_regions` → the union of `regions.home_region` and `regions.linked_regions` -Since there is an unknown number of instances (a for_each on `local.security_hub_standards_arns`) and [Terraform does not support `for_each` on `removed` statements yet](https://github.com/hashicorp/terraform/issues/34439), we need to remove the resources manually from the state. +The following variables have been introduced: +* `aws_security_hub.aggregator_linking_mode`. Indicates whether to aggregate findings from all of the available Regions or from a specified list. +* `aws_security_hub.disabled_control_identifiers`. List of Security Hub control IDs that are disabled in the organisation. +* `aws_security_hub.enabled_control_identifiers`. List of Security Hub control IDs that are enabled in the organisation. + +The following variables have been removed: +* `aws_security_hub.auto_enable_new_accounts`. This variable is not configurable anymore using security hub central configuration. + +### How to upgrade. + +1. Verify Control Tower Governed Regions. + +Ensure your AWS Control Tower Landing Zone regions includes `us-east-1`. + +To check: +1. Log in to the **core-management account**. +2. Navigate to **AWS Control Tower** → **Landing Zone Settings**. +3. Confirm `us-east-1` is listed under **Landing Zone Regions**. + +If `us-east-1` is missing, update your AWS Control Tower settings **before upgrading**. + +> [!NOTE] +> For more details on the `regions` variable, refer to the [Specifying the correct regions section in the readme](README.md). -The following shell snippet generates the removal statements: +2. Update the variables according to the variables section above. + +3. Manually Removing Local Security Hub Standards + +Previous versions managed `aws_securityhub_standards_subscription` resources locally in core accounts. These are now centrally configured using `aws_securityhub_configuration_policy`. **Terraform will attempt to remove these resources from the state**. To prevent disabling them, the resources must be manually removed from the Terraform state. + +*Steps to Remove Resources:* + +a. Generate Removal Commands. Run the following shell snippet: ```shell terraform init -for local_standard in $(terraform state list | grep "module.landing_zone.aws_securityhub_standards_subscription.logging"); do +for local_standard in $(terraform state list | grep "module.landing_zone.aws_securityhub_standards_subscription"); do echo "terraform state rm '$local_standard'" done ``` -Evaluate the output and run the commands. The statements should look something like this: +b. Execute Commands: Evaluate and run the generated statements. They will look like: ```shell terraform state rm 'module.landing_zone.aws_securityhub_standards_subscription.logging["arn:aws:securityhub:eu-central-1::standards/pci-dss/v/3.2.1"]' +... ``` -### Variables +*Why Manual Removal is Required* -The following variables have been replaced: -* `aws_service_control_policies.allowed_regions` -> `regions.allowed_regions` -* `aws_config.aggregator_regions` -> the union of `regions.home_region` and `regions.linked_regions` +Terraform cannot handle `for_each` loops in `removed` statements ([HashiCorp Issue #34439](https://github.com/hashicorp/terraform/issues/34439)). Therefore the resources created with a `for_each` loop on `local.security_hub_standards_arns` must be manually removed from the Terraform state to prevent unintended deletions. + +4. Upgrade your mcaf-landing-zone module to v5.x.x. + +### Troubleshooting + +#### Issue: AWS Security Hub control `AWS Config should be enabled and use the service-linked role for resource recording` fails for multiple accounts after upgrade + +#### Resolution Steps + +1. **Verify `regions.linked_regions`:** + - Ensure that `regions.linked_regions` matches the AWS Control Tower Landing Zone regions. + - For guidance, refer to the [Specifying the correct regions section in the README](README.md). + +2. **Check Organizational Units (OUs):** + - Log in to the **core-management account**. + - Navigate to **AWS Control Tower** → **Organization**. + - Confirm all OUs have the **Baseline state** set to `Succeeded`. + +3. **Check Account Baseline States:** + - In **AWS Control Tower** → **Organization**, verify that all accounts show a **Baseline state** of `Succeeded`. + - If any accounts display `Update available`: + - Select the account. + - Go to **Actions** → **Update**. + +4. **Allow Time for Changes to Propagate:** + - Wait up to **24 hours** for updates to propagate and resolve the Security Hub findings. + +If all steps are completed and the issue persists, review AWS Control Tower settings and logs for additional troubleshooting. -The following variables are added: -* `aws_security_hub.disabled_control_identifiers`. List of Security Hub control IDs that are disabled in the organisation. -* `aws_security_hub.enabled_control_identifiers`. List of Security Hub control IDs that are enabled in the organisation. ## Upgrading to v4.0.0 diff --git a/config.tf b/config.tf index dd9366c..c808f55 100644 --- a/config.tf +++ b/config.tf @@ -7,6 +7,7 @@ locals { } ] ]) + aws_config_rules = setunion( try(var.aws_config.rule_identifiers, []), [ @@ -16,6 +17,7 @@ locals { "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED" ] ) + aws_config_s3_name = coalesce( var.aws_config.delivery_channel_s3_bucket_name, "aws-config-configuration-history-${var.control_tower_account_ids.logging}-${data.aws_region.current.name}" diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 1d28a7c..6d9b7f4 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -6,12 +6,12 @@ locals { } provider "aws" { - region = "eu-west-1" + region = "eu-central-1" } provider "aws" { alias = "audit" - region = "eu-west-1" + region = "eu-central-1" assume_role { role_arn = "arn:aws:iam::${local.control_tower_account_ids.audit}:role/AWSControlTowerExecution" @@ -20,7 +20,7 @@ provider "aws" { provider "aws" { alias = "logging" - region = "eu-west-1" + region = "eu-central-1" assume_role { role_arn = "arn:aws:iam::${local.control_tower_account_ids.logging}:role/AWSControlTowerExecution" @@ -41,10 +41,9 @@ module "landing_zone" { source = "../../" control_tower_account_ids = local.control_tower_account_ids + regions = { - allowed_regions = ["eu-central-1", "eu-west-1"] + allowed_regions = ["eu-central-1"] home_region = "eu-central-1" - linked_regions = ["eu-west-1"] } - tags = { Terraform = true } } diff --git a/security_hub.tf b/security_hub.tf index cdfd934..720c9e2 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -30,11 +30,20 @@ resource "aws_securityhub_account" "default" { control_finding_generator = var.aws_security_hub.control_finding_generator } +resource "aws_securityhub_finding_aggregator" "default" { + provider = aws.audit + + linking_mode = var.aws_security_hub.aggregator_linking_mode + specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? var.regions.linked_regions : null + + depends_on = [aws_securityhub_account.default] +} + resource "aws_securityhub_organization_configuration" "default" { provider = aws.audit auto_enable = false - auto_enable_standards = "NONE" + auto_enable_standards = var.aws_security_hub.auto_enable_default_standards ? "DEFAULT" : "NONE" organization_configuration { configuration_type = "CENTRAL" @@ -43,6 +52,31 @@ resource "aws_securityhub_organization_configuration" "default" { depends_on = [aws_securityhub_organization_admin_account.default, aws_securityhub_finding_aggregator.default] } +resource "aws_securityhub_configuration_policy" "default" { + provider = aws.audit + + name = "mcaf-lz" + description = "MCAF Landing Zone default configuration policy" + + configuration_policy { + service_enabled = true + enabled_standard_arns = local.security_hub_standards_arns + + security_controls_configuration { + disabled_control_identifiers = var.aws_security_hub.disabled_control_identifiers + enabled_control_identifiers = var.aws_security_hub.enabled_control_identifiers + } + } + + depends_on = [aws_securityhub_organization_configuration.default] +} + +resource "aws_securityhub_configuration_policy_association" "root" { + provider = aws.audit + + target_id = data.aws_organizations_organization.default.roots[0].id + policy_id = aws_securityhub_configuration_policy.default.id +} resource "aws_cloudwatch_event_rule" "security_hub_findings" { provider = aws.audit @@ -104,40 +138,3 @@ resource "aws_securityhub_member" "logging" { depends_on = [aws_securityhub_organization_configuration.default] } - - -resource "aws_securityhub_finding_aggregator" "default" { - count = length(var.regions.linked_regions) == 0 ? 0 : 1 - provider = aws.audit - - linking_mode = var.aws_security_hub.aggregator_linking_mode - specified_regions = var.aws_security_hub.aggregator_linking_mode == "SPECIFIED_REGIONS" ? var.regions.linked_regions : null - - depends_on = [aws_securityhub_account.default] -} - -resource "aws_securityhub_configuration_policy" "default" { - provider = aws.audit - - name = "mcaf-lz" - description = "MCAF Landing Zone default configuration policy" - - configuration_policy { - service_enabled = true - enabled_standard_arns = local.security_hub_standards_arns - - security_controls_configuration { - disabled_control_identifiers = var.aws_security_hub.disabled_control_identifiers - enabled_control_identifiers = var.aws_security_hub.enabled_control_identifiers - } - } - - depends_on = [aws_securityhub_organization_configuration.default] -} - -resource "aws_securityhub_configuration_policy_association" "root" { - provider = aws.audit - - target_id = data.aws_organizations_organization.default.roots[0].id - policy_id = aws_securityhub_configuration_policy.default.id -} diff --git a/variables.tf b/variables.tf index 8884770..0588eeb 100644 --- a/variables.tf +++ b/variables.tf @@ -24,7 +24,12 @@ variable "regions" { home_region = string linked_regions = optional(list(string), ["us-east-1"]) }) - description = "Regions for your AWS organisation. More information: https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html" + description = "Region configuration. See the README for more information on the configuration options." + + validation { + condition = length(var.regions.linked_regions) > 0 + error_message = "The 'linked_regions' list must include at least one region. By default, 'us-east-1' is specified to ensure the tracking of global resources. Please specify at least one region if overriding the default." + } } variable "aws_account_password_policy" { @@ -158,14 +163,15 @@ variable "aws_required_tags" { variable "aws_security_hub" { type = object({ - aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS") - auto_enable_controls = optional(bool, true) - control_finding_generator = optional(string, "SECURITY_CONTROL") - create_cis_metric_filters = optional(bool, true) - disabled_control_identifiers = optional(list(string), null) - enabled_control_identifiers = optional(list(string), null) - product_arns = optional(list(string), []) - standards_arns = optional(list(string), null) + aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS") + auto_enable_controls = optional(bool, true) + auto_enable_default_standards = optional(bool, false) + control_finding_generator = optional(string, "SECURITY_CONTROL") + create_cis_metric_filters = optional(bool, true) + disabled_control_identifiers = optional(list(string), null) + enabled_control_identifiers = optional(list(string), null) + product_arns = optional(list(string), []) + standards_arns = optional(list(string), null) }) default = {} description = "AWS Security Hub settings" @@ -175,9 +181,14 @@ variable "aws_security_hub" { error_message = "The \"control_finding_generator\" variable must be set to either \"SECURITY_CONTROL\" or \"STANDARD_CONTROL\"." } + validation { + condition = contains(["SPECIFIED_REGIONS", "ALL_REGIONS"], var.aws_security_hub.aggregator_linking_mode) + error_message = "The \"aggregator_linking_mode\" variable must be set to either \"SPECIFIED_REGIONS\" or \"ALL_REGIONS\"." + } + validation { condition = try(length(var.aws_security_hub.enabled_control_identifiers), 0) == 0 || try(length(var.aws_security_hub.disabled_control_identifiers), 0) == 0 - error_message = "Only one of \"enabled_control_identifiers\" or \"disabled_control_identifiers\" can be set." + error_message = "Only one of \"enabled_control_identifiers\" or \"disabled_control_identifiers\" variable can be set." } } @@ -301,5 +312,6 @@ variable "ses_root_accounts_mail_forward" { variable "tags" { type = map(string) + default = {} description = "Map of tags" } From fb18a4eae7c40ca0e4288fb4247a41b4a091e50c Mon Sep 17 00:00:00 2001 From: Marwin Baumann Date: Tue, 24 Dec 2024 10:12:28 +0100 Subject: [PATCH 26/28] fix some small issus --- UPGRADING.md | 1 + outputs.tf | 5 +++++ security_hub.tf | 2 +- variables.tf | 17 ++++++++--------- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index d9f49aa..2abc565 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -33,6 +33,7 @@ The following variables have been introduced: The following variables have been removed: * `aws_security_hub.auto_enable_new_accounts`. This variable is not configurable anymore using security hub central configuration. +* `aws_security_hub.auto_enable_default_standards`. This variable is not configurable anymore using security hub central configuration. ### How to upgrade. diff --git a/outputs.tf b/outputs.tf index 82ab63a..cbbdc77 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,3 +1,8 @@ +output "aws_config_s3_bucket_arn" { + description = "ARN of the AWS Config S3 bucket" + value = module.aws_config_s3.arn +} + output "kms_key_arn" { description = "ARN of KMS key for master account" value = module.kms_key.arn diff --git a/security_hub.tf b/security_hub.tf index 720c9e2..93fcf3b 100644 --- a/security_hub.tf +++ b/security_hub.tf @@ -43,7 +43,7 @@ resource "aws_securityhub_organization_configuration" "default" { provider = aws.audit auto_enable = false - auto_enable_standards = var.aws_security_hub.auto_enable_default_standards ? "DEFAULT" : "NONE" + auto_enable_standards = "NONE" organization_configuration { configuration_type = "CENTRAL" diff --git a/variables.tf b/variables.tf index 0588eeb..691ec14 100644 --- a/variables.tf +++ b/variables.tf @@ -163,15 +163,14 @@ variable "aws_required_tags" { variable "aws_security_hub" { type = object({ - aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS") - auto_enable_controls = optional(bool, true) - auto_enable_default_standards = optional(bool, false) - control_finding_generator = optional(string, "SECURITY_CONTROL") - create_cis_metric_filters = optional(bool, true) - disabled_control_identifiers = optional(list(string), null) - enabled_control_identifiers = optional(list(string), null) - product_arns = optional(list(string), []) - standards_arns = optional(list(string), null) + aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS") + auto_enable_controls = optional(bool, true) + control_finding_generator = optional(string, "SECURITY_CONTROL") + create_cis_metric_filters = optional(bool, true) + disabled_control_identifiers = optional(list(string), null) + enabled_control_identifiers = optional(list(string), null) + product_arns = optional(list(string), []) + standards_arns = optional(list(string), null) }) default = {} description = "AWS Security Hub settings" From 450644cac80755b4f388d7faea9c657ef47b8293 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 24 Dec 2024 09:13:14 +0000 Subject: [PATCH 27/28] docs(readme): update module usage --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8b06924..38f4c0e 100644 --- a/README.md +++ b/README.md @@ -555,9 +555,8 @@ module "landing_zone" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [allowed\_regions](#input\_allowed\_regions) | List of AWS regions where operations are allowed and for which central services like Security Hub and AWS Config are configured. | `list(string)` | n/a | yes | | [control\_tower\_account\_ids](#input\_control\_tower\_account\_ids) | Control Tower core account IDs |
object({
audit = string
logging = string
})
| n/a | yes | -| [tags](#input\_tags) | Map of tags | `map(string)` | n/a | yes | +| [regions](#input\_regions) | Region configuration. See the README for more information on the configuration options. |
object({
allowed_regions = list(string)
home_region = string
linked_regions = optional(list(string), ["us-east-1"])
})
| n/a | yes | | [additional\_auditing\_trail](#input\_additional\_auditing\_trail) | CloudTrail configuration for additional auditing trail |
object({
name = string
bucket = string
kms_key_id = string

event_selector = optional(object({
data_resource = optional(object({
type = string
values = list(string)
}))
exclude_management_event_sources = optional(set(string), null)
include_management_events = optional(bool, true)
read_write_type = optional(string, "All")
}))
})
| `null` | no | | [aws\_account\_password\_policy](#input\_aws\_account\_password\_policy) | AWS account password policy parameters for the audit, logging and master account |
object({
allow_users_to_change = bool
max_age = number
minimum_length = number
require_lowercase_characters = bool
require_numbers = bool
require_symbols = bool
require_uppercase_characters = bool
reuse_prevention_history = number
})
|
{
"allow_users_to_change": true,
"max_age": 90,
"minimum_length": 14,
"require_lowercase_characters": true,
"require_numbers": true,
"require_symbols": true,
"require_uppercase_characters": true,
"reuse_prevention_history": 24
}
| no | | [aws\_auditmanager](#input\_aws\_auditmanager) | AWS Audit Manager config settings |
object({
enabled = bool
reports_bucket_prefix = string
})
|
{
"enabled": true,
"reports_bucket_prefix": "audit-manager-reports"
}
| no | @@ -567,9 +566,9 @@ module "landing_zone" { | [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings |
object({
enabled = optional(bool, true)
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
ebs_malware_protection_status = optional(bool, true)
eks_audit_logs_status = optional(bool, true)
lambda_network_logs_status = optional(bool, true)
rds_login_events_status = optional(bool, true)
s3_data_events_status = optional(bool, true)
runtime_monitoring_status = optional(object({
enabled = optional(bool, true)
eks_addon_management_status = optional(bool, true)
ecs_fargate_agent_management_status = optional(bool, true)
ec2_agent_management_status = optional(bool, true)
}), {})
})
| `{}` | no | | [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled |
object({
enabled = optional(bool, false)
enable_scan_ec2 = optional(bool, true)
enable_scan_ecr = optional(bool, true)
enable_scan_lambda = optional(bool, true)
enable_scan_lambda_code = optional(bool, true)
resource_create_timeout = optional(string, "15m")
})
|
{
"enable_scan_ec2": true,
"enable_scan_ecr": true,
"enable_scan_lambda": true,
"enable_scan_lambda_code": true,
"enabled": false,
"resource_create_timeout": "15m"
}
| no | | [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings |
map(list(object({
name = string
values = optional(list(string))
enforced_for = optional(list(string))
})))
| `null` | no | -| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS")
auto_enable_controls = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
disabled_control_identifiers = optional(list(string), [])
})
| `{}` | no | +| [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings |
object({
aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS")
auto_enable_controls = optional(bool, true)
control_finding_generator = optional(string, "SECURITY_CONTROL")
create_cis_metric_filters = optional(bool, true)
disabled_control_identifiers = optional(list(string), null)
enabled_control_identifiers = optional(list(string), null)
product_arns = optional(list(string), [])
standards_arns = optional(list(string), null)
})
| `{}` | no | | [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | -| [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
allowed_regions = optional(list(string), [])
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | +| [aws\_service\_control\_policies](#input\_aws\_service\_control\_policies) | AWS SCP's parameters to disable required/denied policies, set a list of allowed AWS regions, and set principals that are exempt from the restriction |
object({
aws_deny_disabling_security_hub = optional(bool, true)
aws_deny_leaving_org = optional(bool, true)
aws_deny_root_user_ous = optional(list(string), [])
aws_require_imdsv2 = optional(bool, true)
principal_exceptions = optional(list(string), [])
})
| `{}` | no | | [aws\_sso\_permission\_sets](#input\_aws\_sso\_permission\_sets) | Map of AWS IAM Identity Center permission sets with AWS accounts and group names that should be granted access to each account |
map(object({
assignments = list(map(list(string)))
inline_policy = optional(string, null)
managed_policy_arns = optional(list(string), [])
session_duration = optional(string, "PT4H")
}))
| `{}` | no | | [datadog](#input\_datadog) | Datadog integration options for the core accounts |
object({
api_key = string
cspm_resource_collection_enabled = optional(bool, false)
enable_integration = bool
extended_resource_collection_enabled = optional(bool, false)
install_log_forwarder = optional(bool, false)
log_collection_services = optional(list(string), [])
log_forwarder_version = optional(string)
metric_tag_filters = optional(map(string), {})
namespace_rules = optional(list(string), [])
site_url = string
})
| `null` | no | | [datadog\_excluded\_regions](#input\_datadog\_excluded\_regions) | List of regions where metrics collection will be disabled. | `list(string)` | `[]` | no | @@ -580,11 +579,13 @@ module "landing_zone" { | [monitor\_iam\_activity\_sns\_subscription](#input\_monitor\_iam\_activity\_sns\_subscription) | Subscription options for the LandingZone-IAMActivity SNS topic |
map(object({
endpoint = string
protocol = string
}))
| `{}` | no | | [path](#input\_path) | Optional path for all IAM users, user groups, roles, and customer managed policies created by this module | `string` | `"/"` | no | | [ses\_root\_accounts\_mail\_forward](#input\_ses\_root\_accounts\_mail\_forward) | SES config to receive and forward root account emails |
object({
domain = string
from_email = string
recipient_mapping = map(any)

dmarc = object({
policy = optional(string)
rua = optional(string)
ruf = optional(string)
})
})
| `null` | no | +| [tags](#input\_tags) | Map of tags | `map(string)` | `{}` | no | ## Outputs | Name | Description | |------|-------------| +| [aws\_config\_s3\_bucket\_arn](#output\_aws\_config\_s3\_bucket\_arn) | ARN of the AWS Config S3 bucket | | [kms\_key\_arn](#output\_kms\_key\_arn) | ARN of KMS key for master account | | [kms\_key\_audit\_arn](#output\_kms\_key\_audit\_arn) | ARN of KMS key for audit account | | [kms\_key\_audit\_id](#output\_kms\_key\_audit\_id) | ID of KMS key for audit account | From 924916097817523c34ad5c9099408be2b52cb77b Mon Sep 17 00:00:00 2001 From: Johan Steenhoven Date: Tue, 24 Dec 2024 10:16:09 +0100 Subject: [PATCH 28/28] Indentation md --- UPGRADING.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index 2abc565..cd23b4a 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -39,14 +39,14 @@ The following variables have been removed: 1. Verify Control Tower Governed Regions. -Ensure your AWS Control Tower Landing Zone regions includes `us-east-1`. + Ensure your AWS Control Tower Landing Zone regions includes `us-east-1`. -To check: -1. Log in to the **core-management account**. -2. Navigate to **AWS Control Tower** → **Landing Zone Settings**. -3. Confirm `us-east-1` is listed under **Landing Zone Regions**. + To check: + 1. Log in to the **core-management account**. + 2. Navigate to **AWS Control Tower** → **Landing Zone Settings**. + 3. Confirm `us-east-1` is listed under **Landing Zone Regions**. -If `us-east-1` is missing, update your AWS Control Tower settings **before upgrading**. + If `us-east-1` is missing, update your AWS Control Tower settings **before upgrading**. > [!NOTE] > For more details on the `regions` variable, refer to the [Specifying the correct regions section in the readme](README.md). @@ -55,35 +55,35 @@ If `us-east-1` is missing, update your AWS Control Tower settings **before upgra 3. Manually Removing Local Security Hub Standards -Previous versions managed `aws_securityhub_standards_subscription` resources locally in core accounts. These are now centrally configured using `aws_securityhub_configuration_policy`. **Terraform will attempt to remove these resources from the state**. To prevent disabling them, the resources must be manually removed from the Terraform state. + Previous versions managed `aws_securityhub_standards_subscription` resources locally in core accounts. These are now centrally configured using `aws_securityhub_configuration_policy`. **Terraform will attempt to remove these resources from the state**. To prevent disabling them, the resources must be manually removed from the Terraform state. -*Steps to Remove Resources:* + *Steps to Remove Resources:* -a. Generate Removal Commands. Run the following shell snippet: + a. Generate Removal Commands. Run the following shell snippet: -```shell -terraform init -for local_standard in $(terraform state list | grep "module.landing_zone.aws_securityhub_standards_subscription"); do - echo "terraform state rm '$local_standard'" -done -``` + ```shell + terraform init + for local_standard in $(terraform state list | grep "module.landing_zone.aws_securityhub_standards_subscription"); do + echo "terraform state rm '$local_standard'" + done + ``` -b. Execute Commands: Evaluate and run the generated statements. They will look like: + b. Execute Commands: Evaluate and run the generated statements. They will look like: -```shell -terraform state rm 'module.landing_zone.aws_securityhub_standards_subscription.logging["arn:aws:securityhub:eu-central-1::standards/pci-dss/v/3.2.1"]' -... -``` + ```shell + terraform state rm 'module.landing_zone.aws_securityhub_standards_subscription.logging["arn:aws:securityhub:eu-central-1::standards/pci-dss/v/3.2.1"]' + ... + ``` -*Why Manual Removal is Required* + *Why Manual Removal is Required* -Terraform cannot handle `for_each` loops in `removed` statements ([HashiCorp Issue #34439](https://github.com/hashicorp/terraform/issues/34439)). Therefore the resources created with a `for_each` loop on `local.security_hub_standards_arns` must be manually removed from the Terraform state to prevent unintended deletions. + Terraform cannot handle `for_each` loops in `removed` statements ([HashiCorp Issue #34439](https://github.com/hashicorp/terraform/issues/34439)). Therefore the resources created with a `for_each` loop on `local.security_hub_standards_arns` must be manually removed from the Terraform state to prevent unintended deletions. 4. Upgrade your mcaf-landing-zone module to v5.x.x. ### Troubleshooting -#### Issue: AWS Security Hub control `AWS Config should be enabled and use the service-linked role for resource recording` fails for multiple accounts after upgrade +#### Issue: AWS Security Hub control "AWS Config should be enabled and use the service-linked role for resource recording" fails for multiple accounts after upgrade #### Resolution Steps