Skip to content

Commit

Permalink
feat(security): create securityhub multi-region module (#13)
Browse files Browse the repository at this point in the history
* feat(security): create securityhub multi-region module

* fix: exclude securityhub dir from validate
  • Loading branch information
p5 authored Aug 29, 2024
1 parent 23d50e4 commit b030e8d
Show file tree
Hide file tree
Showing 15 changed files with 605 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ jobs:
- name: Validate
env:
excluded_directories: ""
# Some modules (especially those using multiple providers) do not work with `terraform validate`
# so we exclude them from the validation process
excluded_directories: |
modules/aws/security/securityhub
run: |
excluded_dirs=($excluded_directories)
for dir in $MODULE_DIRS; do
Expand Down
Empty file.
9 changes: 9 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
generate MODULE="":
#!/usr/bin/env bash
echo "Generating Modules"
bin_dir=$(mktemp -d)
pushd tools/generate-aws-multi-region
go build -o $bin_dir/generate-aws-multi-region .
popd
$bin_dir/generate-aws-multi-region -dir {{MODULE}} -regionsFile ./modules/aws/regions.json
rm -rf $bin_dir
21 changes: 21 additions & 0 deletions modules/aws/regions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"regions": [
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"ap-south-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-northeast-3",
"ca-central-1",
"eu-central-1",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"eu-north-1",
"sa-east-1"
]
}
222 changes: 222 additions & 0 deletions modules/aws/security/securityhub/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
# This file is generated. Do not edit! Your changes will be lost.
module "securityhub_us_east_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "us-east-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "us-east-1"

providers = {
aws = aws.us_east_1
}
}
module "securityhub_us_east_2" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "us-east-2")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "us-east-2"

providers = {
aws = aws.us_east_2
}
}
module "securityhub_us_west_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "us-west-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "us-west-1"

providers = {
aws = aws.us_west_1
}
}
module "securityhub_us_west_2" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "us-west-2")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "us-west-2"

providers = {
aws = aws.us_west_2
}
}
module "securityhub_ap_south_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "ap-south-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "ap-south-1"

providers = {
aws = aws.ap_south_1
}
}
module "securityhub_ap_northeast_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "ap-northeast-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "ap-northeast-1"

providers = {
aws = aws.ap_northeast_1
}
}
module "securityhub_ap_northeast_2" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "ap-northeast-2")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "ap-northeast-2"

providers = {
aws = aws.ap_northeast_2
}
}
module "securityhub_ap_southeast_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "ap-southeast-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "ap-southeast-1"

providers = {
aws = aws.ap_southeast_1
}
}
module "securityhub_ap_southeast_2" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "ap-southeast-2")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "ap-southeast-2"

providers = {
aws = aws.ap_southeast_2
}
}
module "securityhub_ap_northeast_3" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "ap-northeast-3")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "ap-northeast-3"

providers = {
aws = aws.ap_northeast_3
}
}
module "securityhub_ca_central_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "ca-central-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "ca-central-1"

providers = {
aws = aws.ca_central_1
}
}
module "securityhub_eu_central_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "eu-central-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "eu-central-1"

providers = {
aws = aws.eu_central_1
}
}
module "securityhub_eu_west_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "eu-west-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "eu-west-1"

providers = {
aws = aws.eu_west_1
}
}
module "securityhub_eu_west_2" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "eu-west-2")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "eu-west-2"

providers = {
aws = aws.eu_west_2
}
}
module "securityhub_eu_west_3" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "eu-west-3")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "eu-west-3"

providers = {
aws = aws.eu_west_3
}
}
module "securityhub_eu_north_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "eu-north-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "eu-north-1"

providers = {
aws = aws.eu_north_1
}
}
module "securityhub_sa_east_1" {
source = "./single-region"

create_resources = contains(var.opt_in_regions, "sa-east-1")
standards = var.enabled_standards
external_member_accounts = var.external_member_accounts
delegated_administrator_account_id = var.delegated_administrator_account_id
is_aggregate_region = var.aggregate_region == "sa-east-1"

providers = {
aws = aws.sa_east_1
}
}
Empty file.
53 changes: 53 additions & 0 deletions modules/aws/security/securityhub/single-region/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
data "aws_partition" "current" {}
data "aws_region" "current" {}
data "aws_caller_identity" "current" {}

locals {
is_delegated_administrator = var.delegated_administrator_account_id == data.aws_caller_identity.current.account_id
}

resource "aws_securityhub_account" "this" {
count = var.create_resources ? 1 : 0

enable_default_standards = var.enable_default_standards
control_finding_generator = var.control_finding_generator
auto_enable_controls = var.auto_enable_controls
}

resource "aws_securityhub_standards_subscription" "this" {
for_each = var.create_resources ? { for standard in var.standards : standard => standard } : {}

standards_arn = startswith(each.value, "ruleset/") ? "arn:${data.aws_partition.current.partition}:securityhub:::${each.value}" : "arn:${data.aws_partition.current.partition}:securityhub:${data.aws_region.current.name}::${each.value}"

depends_on = [
aws_securityhub_account.this,
]
}

resource "aws_securityhub_member" "external" {
for_each = var.create_resources && local.is_delegated_administrator ? var.external_member_accounts : {}

account_id = each.value.account_id
email = each.value.email
invite = true

depends_on = [
aws_securityhub_account.this,
]
}

resource "aws_securityhub_invite_accepter" "external" {
count = var.create_resources && !local.is_delegated_administrator && var.delegated_administrator_account_id != null ? 1 : 0

master_id = var.delegated_administrator_account_id

depends_on = [aws_securityhub_account.this]
}

resource "aws_securityhub_finding_aggregator" "this" {
count = var.create_resources && local.is_delegated_administrator && var.is_aggregate_region ? 1 : 0

linking_mode = "ALL_REGIONS"

depends_on = [aws_securityhub_account.this]
}
58 changes: 58 additions & 0 deletions modules/aws/security/securityhub/single-region/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
variable "create_resources" {
type = bool
}

variable "standards" {
type = list(string)
}

variable "external_member_accounts" {
type = map(object({
account_id = string
email = string
}))
default = {}
}


variable "enable_default_standards" {
type = bool
description = "Whether to enable the CIS AWS Foundations Benchmark and AWS Foundational Security Best Practices standards. Defaults to false so these can be defined alongside other standards."
default = false
}

variable "control_finding_generator" {
type = string
default = null
description = <<-EOT
Updates whether the calling account has consolidated control
findings turned on. If the value for this field is set to SECURITY_CONTROL,
Security Hub generates a single finding for a control check even when the
check applies to multiple enabled standards. If the value for this field is
set to STANDARD_CONTROL, Security Hub generates separate findings for a
control check when the check applies to multiple enabled standards. For
accounts that are part of an organization, this value can only be updated
in the administrator account.
EOT
validation {
condition = var.control_finding_generator == null || var.control_finding_generator == "SECURITY_CONTROL" || var.control_finding_generator == "STANDARD_CONTROL"
error_message = "control_finding_generator must be either null, 'SECURITY_CONTROL' or 'STANDARD_CONTROL'"
}
}

variable "auto_enable_controls" {
type = bool
default = true
}

variable "delegated_administrator_account_id" {
type = string
description = "The account ID of the delegated administrator for Security Hub"
default = null
}

variable "is_aggregate_region" {
type = bool
description = "Whether this region is an aggregate region"
default = false
}
10 changes: 10 additions & 0 deletions modules/aws/security/securityhub/single-region/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = ">=1.3"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">=4.0"
}
}
}
Loading

0 comments on commit b030e8d

Please sign in to comment.