From f756faed9a07e15796a9317938b95ae3d3c92c2f Mon Sep 17 00:00:00 2001 From: Noah Kraemer Date: Fri, 15 Oct 2021 17:04:05 -0700 Subject: [PATCH] wip --- .../single-account-benchmark/main.tf | 10 +- .../single-account-benchmark/versions.tf | 2 +- examples/organizational/main.tf | 251 +++++++++--------- examples/organizational/utils.tf | 98 +++---- examples/organizational/variables.tf | 11 +- examples/organizational/versions.tf | 2 +- examples/single-account/main.tf | 9 +- examples/single-account/variables.tf | 15 +- examples/single-account/versions.tf | 2 +- modules/services/cloud-bench/main.tf | 116 ++++++-- modules/services/cloud-bench/variables.tf | 19 +- modules/services/cloud-bench/versions.tf | 2 +- 12 files changed, 307 insertions(+), 230 deletions(-) diff --git a/examples-internal/single-account-benchmark/main.tf b/examples-internal/single-account-benchmark/main.tf index d653e891..3a1678fb 100644 --- a/examples-internal/single-account-benchmark/main.tf +++ b/examples-internal/single-account-benchmark/main.tf @@ -8,14 +8,10 @@ provider "sysdig" { sysdig_secure_insecure_tls = length(regexall("https://.*?\\.sysdig(cloud)?.com/?", var.sysdig_secure_endpoint)) == 1 ? false : true } - -data "aws_caller_identity" "me" {} - module "cloud_bench" { source = "../../modules/services/cloud-bench" - account_id = data.aws_caller_identity.me.account_id - tags = var.tags - regions = var.benchmark_regions - name = "${var.name}-cloudbench" + name = "${var.name}-cloudbench" + tags = var.tags + benchmark_regions = var.benchmark_regions } diff --git a/examples-internal/single-account-benchmark/versions.tf b/examples-internal/single-account-benchmark/versions.tf index 9e0768e1..ab0d6a11 100644 --- a/examples-internal/single-account-benchmark/versions.tf +++ b/examples-internal/single-account-benchmark/versions.tf @@ -2,7 +2,7 @@ terraform { required_version = ">= 0.15.0" required_providers { aws = { - version = ">= 3.50.0" + version = ">= 3.62.0" } sysdig = { source = "sysdiglabs/sysdig" diff --git a/examples/organizational/main.tf b/examples/organizational/main.tf index 0bf89e87..ed6c1392 100644 --- a/examples/organizational/main.tf +++ b/examples/organizational/main.tf @@ -15,149 +15,142 @@ provider "sysdig" { sysdig_secure_api_token = var.sysdig_secure_api_token sysdig_secure_insecure_tls = length(regexall("https://.*?\\.sysdig(cloud)?.com/?", var.sysdig_secure_endpoint)) == 1 ? false : true } - -#------------------------------------- -# resources deployed always in management account -# with default provider -#------------------------------------- - -module "resource_group" { - source = "../../modules/infrastructure/resource-group" - name = var.name - tags = var.tags -} - -module "cloudtrail" { - source = "../../modules/infrastructure/cloudtrail" - name = var.name - - is_organizational = true - organizational_config = { - sysdig_secure_for_cloud_member_account_id = var.sysdig_secure_for_cloud_member_account_id - } - - is_multi_region_trail = var.cloudtrail_is_multi_region_trail - cloudtrail_kms_enable = var.cloudtrail_kms_enable - - tags = var.tags -} - - -#------------------------------------- -# secure-for-cloud member account workload -#------------------------------------- - -module "ecs_fargate_cluster" { - providers = { - aws = aws.member - } - source = "../../modules/infrastructure/ecs-fargate-cluster" - name = var.name - tags = var.tags -} - - -module "ssm" { - providers = { - aws = aws.member - } - source = "../../modules/infrastructure/ssm" - name = var.name - sysdig_secure_api_token = var.sysdig_secure_api_token -} - - # -# cloud-connector +##------------------------------------- +## resources deployed always in management account +## with default provider +##------------------------------------- # -module "cloud_connector" { - providers = { - aws = aws.member - } - source = "../../modules/services/cloud-connector" - name = "${var.name}-cloudconnector" - - sysdig_secure_endpoint = var.sysdig_secure_endpoint - secure_api_token_secret_name = module.ssm.secure_api_token_secret_name - - is_organizational = true - organizational_config = { - sysdig_secure_for_cloud_role_arn = module.secure_for_cloud_role.sysdig_secure_for_cloud_role_arn - connector_ecs_task_role_name = aws_iam_role.connector_ecs_task.name - } - - sns_topic_arn = module.cloudtrail.sns_topic_arn - - ecs_cluster = module.ecs_fargate_cluster.id - vpc_id = module.ecs_fargate_cluster.vpc_id - vpc_subnets = module.ecs_fargate_cluster.vpc_subnets - - tags = var.tags - depends_on = [module.cloudtrail, module.ecs_fargate_cluster, module.ssm] -} - - +#module "resource_group" { +# source = "../../modules/infrastructure/resource-group" +# name = var.name +# tags = var.tags +#} # -# cloud-bench -# WIP +#module "cloudtrail" { +# source = "../../modules/infrastructure/cloudtrail" +# name = var.name # - -#data "aws_caller_identity" "me" {} -#module "cloud_bench" { +# is_organizational = true +# organizational_config = { +# sysdig_secure_for_cloud_member_account_id = var.sysdig_secure_for_cloud_member_account_id +# } +# +# is_multi_region_trail = var.cloudtrail_is_multi_region_trail +# cloudtrail_kms_enable = var.cloudtrail_kms_enable +# +# tags = var.tags +#} +# +# +##------------------------------------- +## secure-for-cloud member account workload +##------------------------------------- +# +#module "ecs_fargate_cluster" { # providers = { # aws = aws.member # } -# source = "../../modules/services/cloud-bench" +# source = "../../modules/infrastructure/ecs-fargate-cluster" +# name = var.name +# tags = var.tags +#} +# +# +#module "ssm" { +# providers = { +# aws = aws.member +# } +# source = "../../modules/infrastructure/ssm" +# name = var.name +# sysdig_secure_api_token = var.sysdig_secure_api_token +#} +# +# +## +## cloud-connector +## +#module "cloud_connector" { +# providers = { +# aws = aws.member +# } +# source = "../../modules/services/cloud-connector" +# name = "${var.name}-cloudconnector" +# +# sysdig_secure_endpoint = var.sysdig_secure_endpoint +# secure_api_token_secret_name = module.ssm.secure_api_token_secret_name +# +# is_organizational = true +# organizational_config = { +# sysdig_secure_for_cloud_role_arn = module.secure_for_cloud_role.sysdig_secure_for_cloud_role_arn +# connector_ecs_task_role_name = aws_iam_role.connector_ecs_task.name +# } +# +# sns_topic_arn = module.cloudtrail.sns_topic_arn +# +# ecs_cluster = module.ecs_fargate_cluster.id +# vpc_id = module.ecs_fargate_cluster.vpc_id +# vpc_subnets = module.ecs_fargate_cluster.vpc_subnets # -# account_id = var.organizational_config.sysdig_secure_for_cloud_member_account_id # tags = var.tags +# depends_on = [module.cloudtrail, module.ecs_fargate_cluster, module.ssm] #} - - - - - # -# cloud-scanning +## +## cloud-scanning +## +### FIXME? if this is a non-shared resource, move its usage to scanning service? +#module "codebuild" { +# providers = { +# aws = aws.member +# } +# source = "../../modules/infrastructure/codebuild" +# name = var.name +# secure_api_token_secret_name = module.ssm.secure_api_token_secret_name +# depends_on = [module.ssm] +#} # -## FIXME? if this is a non-shared resource, move its usage to scanning service? -module "codebuild" { - providers = { - aws = aws.member - } - source = "../../modules/infrastructure/codebuild" - name = var.name - secure_api_token_secret_name = module.ssm.secure_api_token_secret_name - depends_on = [module.ssm] -} - -module "cloud_scanning" { - providers = { - aws = aws.member - } - - source = "../../modules/services/cloud-scanning" - name = "${var.name}-cloudscanning" +#module "cloud_scanning" { +# providers = { +# aws = aws.member +# } +# +# source = "../../modules/services/cloud-scanning" +# name = "${var.name}-cloudscanning" +# +# sysdig_secure_endpoint = var.sysdig_secure_endpoint +# secure_api_token_secret_name = module.ssm.secure_api_token_secret_name +# +# build_project_arn = module.codebuild.project_arn +# build_project_name = module.codebuild.project_name +# +# is_organizational = true +# organizational_config = { +# sysdig_secure_for_cloud_role_arn = module.secure_for_cloud_role.sysdig_secure_for_cloud_role_arn +# organizational_role_per_account = "OrganizationAccountAccessRole" +# scanning_ecs_task_role_name = aws_iam_role.connector_ecs_task.name +# } +# +# sns_topic_arn = module.cloudtrail.sns_topic_arn +# +# ecs_cluster = module.ecs_fargate_cluster.id +# vpc_id = module.ecs_fargate_cluster.vpc_id +# vpc_subnets = module.ecs_fargate_cluster.vpc_subnets +# +# tags = var.tags +# depends_on = [module.cloudtrail, module.ecs_fargate_cluster, module.codebuild, module.ssm] +#} - sysdig_secure_endpoint = var.sysdig_secure_endpoint - secure_api_token_secret_name = module.ssm.secure_api_token_secret_name +#------------------------------------- +# cloud-bench +#------------------------------------- - build_project_arn = module.codebuild.project_arn - build_project_name = module.codebuild.project_name +module "cloud_bench" { + source = "../../modules/services/cloud-bench" + name = "${var.name}-cloudbench" + tags = var.tags is_organizational = true - organizational_config = { - sysdig_secure_for_cloud_role_arn = module.secure_for_cloud_role.sysdig_secure_for_cloud_role_arn - organizational_role_per_account = "OrganizationAccountAccessRole" - scanning_ecs_task_role_name = aws_iam_role.connector_ecs_task.name - } - - sns_topic_arn = module.cloudtrail.sns_topic_arn - - ecs_cluster = module.ecs_fargate_cluster.id - vpc_id = module.ecs_fargate_cluster.vpc_id - vpc_subnets = module.ecs_fargate_cluster.vpc_subnets - - tags = var.tags - depends_on = [module.cloudtrail, module.ecs_fargate_cluster, module.codebuild, module.ssm] + region = var.region + benchmark_regions = var.benchmark_regions } diff --git a/examples/organizational/utils.tf b/examples/organizational/utils.tf index 4bda3a29..d037b0f1 100644 --- a/examples/organizational/utils.tf +++ b/examples/organizational/utils.tf @@ -1,49 +1,49 @@ -module "resource_group_secure_for_cloud_member" { - providers = { - aws = aws.member - } - source = "../../modules/infrastructure/resource-group" - name = var.name - tags = var.tags -} - - -module "secure_for_cloud_role" { - source = "../../modules/infrastructure/permissions/org-management-role" - providers = { - aws.member = aws.member - } - name = var.name - - cloudtrail_s3_arn = module.cloudtrail.s3_bucket_arn - cloudconnector_ecs_task_role_name = aws_iam_role.connector_ecs_task.name - - tags = var.tags - depends_on = [aws_iam_role.connector_ecs_task] -} - - -# ----------------------------------------------------------------- -# secure_for_cloud_role <-> ecs_role trust relationship -# note: -# - definition of a ROOT lvl secure_for_cloud_connector_ecs_tas_role to avoid cyclic dependencies -# - duplicated in ../../modules/services/cloud-connector/ecs-service-security.tf -# ----------------------------------------------------------------- -resource "aws_iam_role" "connector_ecs_task" { - provider = aws.member - name = "${var.name}-${var.connector_ecs_task_role_name}" - assume_role_policy = data.aws_iam_policy_document.task_assume_role.json - path = "/" - tags = var.tags -} -data "aws_iam_policy_document" "task_assume_role" { - provider = aws.member - statement { - effect = "Allow" - principals { - identifiers = ["ecs-tasks.amazonaws.com"] - type = "Service" - } - actions = ["sts:AssumeRole"] - } -} +#module "resource_group_secure_for_cloud_member" { +# providers = { +# aws = aws.member +# } +# source = "../../modules/infrastructure/resource-group" +# name = var.name +# tags = var.tags +#} +# +# +#module "secure_for_cloud_role" { +# source = "../../modules/infrastructure/permissions/org-management-role" +# providers = { +# aws.member = aws.member +# } +# name = var.name +# +# cloudtrail_s3_arn = module.cloudtrail.s3_bucket_arn +# cloudconnector_ecs_task_role_name = aws_iam_role.connector_ecs_task.name +# +# tags = var.tags +# depends_on = [aws_iam_role.connector_ecs_task] +#} +# +# +## ----------------------------------------------------------------- +## secure_for_cloud_role <-> ecs_role trust relationship +## note: +## - definition of a ROOT lvl secure_for_cloud_connector_ecs_tas_role to avoid cyclic dependencies +## - duplicated in ../../modules/services/cloud-connector/ecs-service-security.tf +## ----------------------------------------------------------------- +#resource "aws_iam_role" "connector_ecs_task" { +# provider = aws.member +# name = "${var.name}-${var.connector_ecs_task_role_name}" +# assume_role_policy = data.aws_iam_policy_document.task_assume_role.json +# path = "/" +# tags = var.tags +#} +#data "aws_iam_policy_document" "task_assume_role" { +# provider = aws.member +# statement { +# effect = "Allow" +# principals { +# identifiers = ["ecs-tasks.amazonaws.com"] +# type = "Service" +# } +# actions = ["sts:AssumeRole"] +# } +#} diff --git a/examples/organizational/variables.tf b/examples/organizational/variables.tf index 469a1885..632b2892 100644 --- a/examples/organizational/variables.tf +++ b/examples/organizational/variables.tf @@ -41,6 +41,16 @@ variable "cloudtrail_kms_enable" { description = "true/false whether cloudtrail delivered events to S3 should persist encrypted" } +# +# benchmark configuration +# + +variable "benchmark_regions" { + type = list(string) + description = "List of regions in which to run the benchmark. If empty, the task will contain all aws regions by default." + default = [] +} + # # general # @@ -51,7 +61,6 @@ variable "region" { description = "Default region for resource creation in both organization master and secure-for-cloud member account" } - variable "name" { type = string description = "Name to be assigned to all child resources. A suffix may be added internally when required. Use default value unless you need to install multiple instances" diff --git a/examples/organizational/versions.tf b/examples/organizational/versions.tf index 4d39bb30..770e4053 100644 --- a/examples/organizational/versions.tf +++ b/examples/organizational/versions.tf @@ -2,7 +2,7 @@ terraform { required_version = ">= 0.15.0" required_providers { aws = { - version = ">= 3.50.0" + version = ">= 3.62.0" } sysdig = { source = "sysdiglabs/sysdig" diff --git a/examples/single-account/main.tf b/examples/single-account/main.tf index 9a531cb9..4581f468 100644 --- a/examples/single-account/main.tf +++ b/examples/single-account/main.tf @@ -100,8 +100,6 @@ module "cloud_scanning" { #------------------------------------- # cloud-bench #------------------------------------- -data "aws_caller_identity" "me" {} - provider "sysdig" { sysdig_secure_url = var.sysdig_secure_endpoint sysdig_secure_api_token = var.sysdig_secure_api_token @@ -110,9 +108,8 @@ provider "sysdig" { module "cloud_bench" { source = "../../modules/services/cloud-bench" - name = "${var.name}-cloudbench" - account_id = data.aws_caller_identity.me.account_id - tags = var.tags - regions = var.benchmark_regions + name = "${var.name}-cloudbench" + tags = var.tags + benchmark_regions = var.benchmark_regions } diff --git a/examples/single-account/variables.tf b/examples/single-account/variables.tf index db166f75..007552fd 100644 --- a/examples/single-account/variables.tf +++ b/examples/single-account/variables.tf @@ -25,6 +25,15 @@ variable "cloudtrail_kms_enable" { description = "true/false whether cloudtrail delivered events to S3 should persist encrypted" } +# +# benchmark configuration +# + +variable "benchmark_regions" { + type = list(string) + description = "List of regions in which to run the benchmark. If empty, the task will contain all aws regions by default." + default = [] +} # # general @@ -54,9 +63,3 @@ variable "tags" { "product" = "sysdig-secure-for-cloud" } } - -variable "benchmark_regions" { - type = list(string) - description = "List of regions in which to run the benchmark. If empty, the task will contain all aws regions by default." - default = [] -} diff --git a/examples/single-account/versions.tf b/examples/single-account/versions.tf index 9e0768e1..ab0d6a11 100644 --- a/examples/single-account/versions.tf +++ b/examples/single-account/versions.tf @@ -2,7 +2,7 @@ terraform { required_version = ">= 0.15.0" required_providers { aws = { - version = ">= 3.50.0" + version = ">= 3.62.0" } sysdig = { source = "sysdiglabs/sysdig" diff --git a/modules/services/cloud-bench/main.tf b/modules/services/cloud-bench/main.tf index e59f2b8b..76d063b4 100644 --- a/modules/services/cloud-bench/main.tf +++ b/modules/services/cloud-bench/main.tf @@ -1,26 +1,49 @@ -# -# Sysdig Secure cloud provisioning -# -resource "sysdig_secure_cloud_account" "cloud_account" { - account_id = var.account_id +#---------------------------------------------------------- +# Fetch & compute required data +#---------------------------------------------------------- + +data "aws_caller_identity" "me" {} + +data "aws_organizations_organization" "org" {} + +data "sysdig_secure_trusted_cloud_identity" "trusted_identity" { cloud_provider = "aws" - role_enabled = "true" - role_name = var.name } -data "sysdig_secure_trusted_cloud_identity" "trusted_identity" { +locals { + // TODO Possibly `accounts` if stackset creates in master as well + member_account_ids = var.is_organizational ? [for a in data.aws_organizations_organization.org.non_master_accounts : a.id] : [] + + benchmark_task_name = var.is_organizational ? "Organization: ${data.aws_organizations_organization.org.id}" : data.aws_caller_identity.me.account_id + accounts_scope_clause = var.is_organizational ? "aws.accountId in (\"${join("\", \"", local.member_account_ids)}\")" : "aws.accountId = \"${data.aws_caller_identity.me.account_id}\"" + regions_scope_clause = length(var.benchmark_regions) == 0 ? "" : " and aws.region in (\"${join("\", \"", var.benchmark_regions)}\")" +} + +#---------------------------------------------------------- +# Configure Sysdig Backend +#---------------------------------------------------------- + +resource "sysdig_secure_cloud_account" "cloud_account" { + for_each = var.is_organizational ? toset(local.member_account_ids) : [data.aws_caller_identity.me.account_id] + + account_id = each.value cloud_provider = "aws" + role_enabled = "true" + role_name = var.name } locals { - regions_scope_clause = length(var.regions) == 0 ? "" : " and aws.region in (\"${join("\", \"", var.regions)}\")" + external_id = try( + sysdig_secure_cloud_account.cloud_account[local.member_account_ids[0]].external_id, + sysdig_secure_cloud_account.cloud_account[data.aws_caller_identity.me.account_id].external_id, + ) } resource "sysdig_secure_benchmark_task" "benchmark_task" { - name = "Sysdig Secure for Cloud (AWS) - ${var.account_id}" + name = "Sysdig Secure for Cloud (AWS) - ${local.benchmark_task_name}" schedule = "0 6 * * *" schema = "aws_foundations_bench-1.3.0" - scope = "aws.accountId = \"${var.account_id}\"${local.regions_scope_clause}" + scope = "${local.accounts_scope_clause}${local.regions_scope_clause}" # Creation of a task requires that the Cloud Account already exists in the backend, and has `role_enabled = true` # We only want to create the task once the rust relationship is established, otherwise running the task will fail. @@ -30,14 +53,13 @@ resource "sysdig_secure_benchmark_task" "benchmark_task" { ] } -# -# aws role provisioning -# -resource "aws_iam_role" "cloudbench_role" { - name = var.name - assume_role_policy = data.aws_iam_policy_document.trust_relationship.json - tags = var.tags +#---------------------------------------------------------- +# If this is not an Organizational deploy, create role/polices directly +#---------------------------------------------------------- + +data "aws_iam_policy" "security_audit" { + arn = "arn:aws:iam::aws:policy/SecurityAudit" } data "aws_iam_policy_document" "trust_relationship" { @@ -51,16 +73,66 @@ data "aws_iam_policy_document" "trust_relationship" { condition { test = "StringEquals" variable = "sts:ExternalId" - values = [sysdig_secure_cloud_account.cloud_account.external_id] + values = [local.external_id] } } } +resource "aws_iam_role" "cloudbench_role" { + count = var.is_organizational ? 0 : 1 + + name = var.name + assume_role_policy = data.aws_iam_policy_document.trust_relationship.json + tags = var.tags +} + + resource "aws_iam_role_policy_attachment" "cloudbench_security_audit" { - role = aws_iam_role.cloudbench_role.id + count = var.is_organizational ? 0 : 1 + + role = aws_iam_role.cloudbench_role[0].id policy_arn = data.aws_iam_policy.security_audit.arn } -data "aws_iam_policy" "security_audit" { - arn = "arn:aws:iam::aws:policy/SecurityAudit" +resource "aws_cloudformation_stack_set" "stackset" { + count = var.is_organizational ? 1 : 0 + + name = var.name + permission_model = "SERVICE_MANAGED" + capabilities = ["CAPABILITY_NAMED_IAM"] + + auto_deployment { + enabled = true + retain_stacks_on_account_removal = false + } + + template_body = <