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

breaking: Central Security Hub configuration #216

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 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
5dc95f8
Allow enabling of securityhub controls by ID
Dec 18, 2024
200076f
Assume AWS Config is enabled externally for linked regions
Dec 18, 2024
0319956
Split regions
Dec 19, 2024
2b50aef
[skip ci] update readme
Dec 19, 2024
dc00853
[skip ci] update readme
Dec 19, 2024
45c794f
improve documentation, formatting and code placement
marwinbaumannsbp Dec 23, 2024
fb18a4e
fix some small issus
marwinbaumannsbp Dec 24, 2024
450644c
docs(readme): update module usage
github-actions[bot] Dec 24, 2024
9249160
Indentation md
Dec 24, 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
129 changes: 74 additions & 55 deletions README.md

Large diffs are not rendered by default.

106 changes: 106 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,112 @@

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

## Upgrading to v5.0.0

### Key Changes

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

#### Dropping Support for Local Configuration

**Local configurations are no longer supported.** Centralized configuration aligns with AWS best practices and reduces complexity.

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

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.
* `aws_security_hub.auto_enable_default_standards`. 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).

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"); do
echo "terraform state rm '$local_standard'"
done
```

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"]'
...
```

*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.

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.


## Upgrading to v4.0.0

> [!WARNING]
Expand Down
6 changes: 4 additions & 2 deletions config.tf
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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.all_organisation_regions, [])) : {
account_id = account
region = region
}
]
])

aws_config_rules = setunion(
try(var.aws_config.rule_identifiers, []),
[
Expand All @@ -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}"
Expand All @@ -32,7 +34,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.all_organisation_regions

account_id = var.control_tower_account_ids.audit
region = each.value
Expand Down
12 changes: 8 additions & 4 deletions examples/basic/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -41,5 +41,9 @@ module "landing_zone" {
source = "../../"

control_tower_account_ids = local.control_tower_account_ids
tags = { Terraform = true }

regions = {
allowed_regions = ["eu-central-1"]
home_region = "eu-central-1"
}
}
1 change: 1 addition & 0 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +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
all_organisation_regions = toset(distinct(concat([var.regions.home_region], var.regions.linked_regions, var.regions.allowed_regions, [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.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
}
Expand Down
5 changes: 5 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -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
Expand Down
61 changes: 33 additions & 28 deletions security_hub.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,46 +23,59 @@ 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

control_finding_generator = var.aws_security_hub.control_finding_generator
}

resource "aws_securityhub_organization_configuration" "default" {
resource "aws_securityhub_finding_aggregator" "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"
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_organization_admin_account.default]
depends_on = [aws_securityhub_account.default]
}

resource "aws_securityhub_product_subscription" "default" {
for_each = toset(var.aws_security_hub.product_arns)
resource "aws_securityhub_organization_configuration" "default" {
provider = aws.audit

product_arn = each.value
auto_enable = false
auto_enable_standards = "NONE"

depends_on = [aws_securityhub_account.default]
organization_configuration {
configuration_type = "CENTRAL"
}

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)
resource "aws_securityhub_configuration_policy" "default" {
provider = aws.audit

standards_arn = each.value
name = "mcaf-lz"
description = "MCAF Landing Zone default configuration policy"

depends_on = [aws_securityhub_account.default]
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" {
Expand Down Expand Up @@ -125,11 +138,3 @@ 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]
}
Loading
Loading