Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: support central Security Hub configuration #214

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
60db109
feature: support central Security Hub configuration
Dec 13, 2024
7020061
docs(readme): update module usage
github-actions[bot] Dec 13, 2024
9ca7e78
rely on defaults of optionals
Dec 13, 2024
093b092
Merge branch 'central_securithub_configuration' of https://github.com…
Dec 13, 2024
b71a6a5
docs(readme): update module usage
github-actions[bot] Dec 13, 2024
d39d1f0
do not use key if no value
Dec 13, 2024
8e28499
Merge branch 'central_securithub_configuration' of https://github.com…
Dec 13, 2024
83290a5
docs(readme): update module usage
github-actions[bot] Dec 13, 2024
c6ccfc7
docs(readme): update module usage
github-actions[bot] Dec 14, 2024
0923594
feat: Add validation rules to security_hub configuration
Dec 17, 2024
aa028ba
Merge branch 'central_securithub_configuration' of https://github.com…
Dec 17, 2024
750cdc1
docs(readme): update module usage
github-actions[bot] Dec 17, 2024
ac42a57
Set provider for aws_securityhub_finding_aggregator
Dec 17, 2024
f35143f
Merge branch 'central_securithub_configuration' of https://github.com…
Dec 17, 2024
3f1d890
Move Security Hub Configuration to Central
Dec 17, 2024
d623e10
docs(readme): update module usage
github-actions[bot] Dec 17, 2024
5afe357
Use correct org root id
Dec 17, 2024
af592b3
Merge branch 'central_securithub_configuration' of https://github.com…
Dec 17, 2024
f4b3571
Use central allowed_region for all region bound configurations
Dec 17, 2024
5ece85b
docs(readme): update module usage
github-actions[bot] Dec 17, 2024
3e36ef6
Ensure home region is not used for findings aggregator
Dec 17, 2024
341311f
Ensure allowed_regions contains at least one region.
Dec 17, 2024
f63139e
Allow disabling of securityhub controls by ID
Dec 18, 2024
c3cc4b9
docs(readme): update module usage
github-actions[bot] Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,14 +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 |
Expand All @@ -538,18 +537,19 @@ module "landing_zone" {

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_allowed_regions"></a> [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 |
| <a name="input_control_tower_account_ids"></a> [control\_tower\_account\_ids](#input\_control\_tower\_account\_ids) | Control Tower core account IDs | <pre>object({<br> audit = string<br> logging = string<br> })</pre> | n/a | yes |
| <a name="input_tags"></a> [tags](#input\_tags) | Map of tags | `map(string)` | n/a | yes |
| <a name="input_additional_auditing_trail"></a> [additional\_auditing\_trail](#input\_additional\_auditing\_trail) | CloudTrail configuration for additional auditing trail | <pre>object({<br> name = string<br> bucket = string<br> kms_key_id = string<br><br> event_selector = optional(object({<br> data_resource = optional(object({<br> type = string<br> values = list(string)<br> }))<br> exclude_management_event_sources = optional(set(string), null)<br> include_management_events = optional(bool, true)<br> read_write_type = optional(string, "All")<br> }))<br> })</pre> | `null` | no |
| <a name="input_aws_account_password_policy"></a> [aws\_account\_password\_policy](#input\_aws\_account\_password\_policy) | AWS account password policy parameters for the audit, logging and master account | <pre>object({<br> allow_users_to_change = bool<br> max_age = number<br> minimum_length = number<br> require_lowercase_characters = bool<br> require_numbers = bool<br> require_symbols = bool<br> require_uppercase_characters = bool<br> reuse_prevention_history = number<br> })</pre> | <pre>{<br> "allow_users_to_change": true,<br> "max_age": 90,<br> "minimum_length": 14,<br> "require_lowercase_characters": true,<br> "require_numbers": true,<br> "require_symbols": true,<br> "require_uppercase_characters": true,<br> "reuse_prevention_history": 24<br>}</pre> | no |
| <a name="input_aws_auditmanager"></a> [aws\_auditmanager](#input\_aws\_auditmanager) | AWS Audit Manager config settings | <pre>object({<br> enabled = bool<br> reports_bucket_prefix = string<br> })</pre> | <pre>{<br> "enabled": true,<br> "reports_bucket_prefix": "audit-manager-reports"<br>}</pre> | no |
| <a name="input_aws_config"></a> [aws\_config](#input\_aws\_config) | AWS Config settings | <pre>object({<br> aggregator_account_ids = optional(list(string), [])<br> aggregator_regions = optional(list(string), [])<br> delivery_channel_s3_bucket_name = optional(string, null)<br> delivery_channel_s3_key_prefix = optional(string, null)<br> delivery_frequency = optional(string, "TwentyFour_Hours")<br> rule_identifiers = optional(list(string), [])<br> })</pre> | <pre>{<br> "aggregator_account_ids": [],<br> "aggregator_regions": [],<br> "delivery_channel_s3_bucket_name": null,<br> "delivery_channel_s3_key_prefix": null,<br> "delivery_frequency": "TwentyFour_Hours",<br> "rule_identifiers": []<br>}</pre> | no |
| <a name="input_aws_config"></a> [aws\_config](#input\_aws\_config) | AWS Config settings | <pre>object({<br> aggregator_account_ids = optional(list(string), [])<br> delivery_channel_s3_bucket_name = optional(string, null)<br> delivery_channel_s3_key_prefix = optional(string, null)<br> delivery_frequency = optional(string, "TwentyFour_Hours")<br> rule_identifiers = optional(list(string), [])<br> })</pre> | <pre>{<br> "aggregator_account_ids": [],<br> "delivery_channel_s3_bucket_name": null,<br> "delivery_channel_s3_key_prefix": null,<br> "delivery_frequency": "TwentyFour_Hours",<br> "rule_identifiers": []<br>}</pre> | no |
| <a name="input_aws_config_sns_subscription"></a> [aws\_config\_sns\_subscription](#input\_aws\_config\_sns\_subscription) | Subscription options for the aws-controltower-AggregateSecurityNotifications (AWS Config) SNS topic | <pre>map(object({<br> endpoint = string<br> protocol = string<br> }))</pre> | `{}` | no |
| <a name="input_aws_ebs_encryption_by_default"></a> [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 |
| <a name="input_aws_guardduty"></a> [aws\_guardduty](#input\_aws\_guardduty) | AWS GuardDuty settings | <pre>object({<br> enabled = optional(bool, true)<br> finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")<br> ebs_malware_protection_status = optional(bool, true)<br> eks_audit_logs_status = optional(bool, true)<br> lambda_network_logs_status = optional(bool, true)<br> rds_login_events_status = optional(bool, true)<br> s3_data_events_status = optional(bool, true)<br> runtime_monitoring_status = optional(object({<br> enabled = optional(bool, true)<br> eks_addon_management_status = optional(bool, true)<br> ecs_fargate_agent_management_status = optional(bool, true)<br> ec2_agent_management_status = optional(bool, true)<br> }), {})<br> })</pre> | `{}` | no |
| <a name="input_aws_inspector"></a> [aws\_inspector](#input\_aws\_inspector) | AWS Inspector settings, at least one of the scan options must be enabled | <pre>object({<br> enabled = optional(bool, false)<br> enable_scan_ec2 = optional(bool, true)<br> enable_scan_ecr = optional(bool, true)<br> enable_scan_lambda = optional(bool, true)<br> enable_scan_lambda_code = optional(bool, true)<br> resource_create_timeout = optional(string, "15m")<br> })</pre> | <pre>{<br> "enable_scan_ec2": true,<br> "enable_scan_ecr": true,<br> "enable_scan_lambda": true,<br> "enable_scan_lambda_code": true,<br> "enabled": false,<br> "resource_create_timeout": "15m"<br>}</pre> | no |
| <a name="input_aws_required_tags"></a> [aws\_required\_tags](#input\_aws\_required\_tags) | AWS Required tags settings | <pre>map(list(object({<br> name = string<br> values = optional(list(string))<br> enforced_for = optional(list(string))<br> })))</pre> | `null` | no |
| <a name="input_aws_security_hub"></a> [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings | <pre>object({<br> auto_enable_controls = optional(bool, true)<br> auto_enable_default_standards = optional(bool, false)<br> auto_enable_new_accounts = optional(bool, true)<br> control_finding_generator = optional(string, "SECURITY_CONTROL")<br> create_cis_metric_filters = optional(bool, true)<br> product_arns = optional(list(string), [])<br> standards_arns = optional(list(string), null)<br> })</pre> | <pre>{<br> "auto_enable_controls": true,<br> "auto_enable_default_standards": false,<br> "auto_enable_new_accounts": true,<br> "control_finding_generator": "SECURITY_CONTROL",<br> "create_cis_metric_filters": true,<br> "product_arns": [],<br> "standards_arns": null<br>}</pre> | no |
| <a name="input_aws_security_hub"></a> [aws\_security\_hub](#input\_aws\_security\_hub) | AWS Security Hub settings | <pre>object({<br> aggregator_linking_mode = optional(string, "SPECIFIED_REGIONS")<br> auto_enable_controls = optional(bool, true)<br> control_finding_generator = optional(string, "SECURITY_CONTROL")<br> create_cis_metric_filters = optional(bool, true)<br> product_arns = optional(list(string), [])<br> standards_arns = optional(list(string), null)<br> disabled_control_identifiers = optional(list(string), [])<br> })</pre> | `{}` | no |
| <a name="input_aws_security_hub_sns_subscription"></a> [aws\_security\_hub\_sns\_subscription](#input\_aws\_security\_hub\_sns\_subscription) | Subscription options for the LandingZone-SecurityHubFindings SNS topic | <pre>map(object({<br> endpoint = string<br> protocol = string<br> }))</pre> | `{}` | no |
| <a name="input_aws_service_control_policies"></a> [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 | <pre>object({<br> allowed_regions = optional(list(string), [])<br> aws_deny_disabling_security_hub = optional(bool, true)<br> aws_deny_leaving_org = optional(bool, true)<br> aws_deny_root_user_ous = optional(list(string), [])<br> aws_require_imdsv2 = optional(bool, true)<br> principal_exceptions = optional(list(string), [])<br> })</pre> | `{}` | no |
| <a name="input_aws_sso_permission_sets"></a> [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 | <pre>map(object({<br> assignments = list(map(list(string)))<br> inline_policy = optional(string, null)<br> managed_policy_arns = optional(list(string), [])<br> session_duration = optional(string, "PT4H")<br> }))</pre> | `{}` | no |
Expand Down
19 changes: 19 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

This document captures required refactoring on your part when upgrading to a module version that contains breaking changes.

## Upgrading to v5.0.0

### 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 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

> [!WARNING]
Expand Down
4 changes: 2 additions & 2 deletions config.tf
Original file line number Diff line number Diff line change
@@ -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(local.allowed_regions_with_us_east, [])) : {
account_id = account
region = region
}
Expand Down Expand Up @@ -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 = local.allowed_regions_with_us_east

account_id = var.control_tower_account_ids.audit
region = each.value
Expand Down
4 changes: 4 additions & 0 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
6 changes: 3 additions & 3 deletions organizations_policy.tf
Original file line number Diff line number Diff line change
@@ -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 = 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
}
Expand Down
71 changes: 39 additions & 32 deletions security_hub.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -41,29 +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"

depends_on = [aws_securityhub_organization_admin_account.default]
}

resource "aws_securityhub_product_subscription" "default" {
for_each = toset(var.aws_security_hub.product_arns)
provider = aws.audit
auto_enable = false
auto_enable_standards = "NONE"

product_arn = each.value
organization_configuration {
configuration_type = "CENTRAL"
}

depends_on = [aws_securityhub_account.default]
depends_on = [aws_securityhub_organization_admin_account.default, aws_securityhub_finding_aggregator.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
Expand Down Expand Up @@ -126,10 +105,38 @@ 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" {
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.allowed_regions_except_home_region : 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
}
}

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
}
Loading