From ca186054df8b157d5109d8aacbb10152fb73f668 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Wed, 16 Feb 2022 01:18:51 +0100 Subject: [PATCH] 02-security --- fast/stages/02-security/README.md | 36 +++++++++--------- fast/stages/02-security/core-dev.tf | 14 +++++-- fast/stages/02-security/core-prod.tf | 14 +++++-- fast/stages/02-security/outputs.tf | 56 +++++++++++++++++++++------- fast/stages/02-security/variables.tf | 36 +++++++++++------- 5 files changed, 104 insertions(+), 52 deletions(-) diff --git a/fast/stages/02-security/README.md b/fast/stages/02-security/README.md index 613b1c49..1a11c8db 100644 --- a/fast/stages/02-security/README.md +++ b/fast/stages/02-security/README.md @@ -285,27 +285,29 @@ Some references that might be useful in setting up this stage: | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| -| [billing_account_id](variables.tf#L17) | Billing account id. | string | ✓ | | bootstrap | -| [folder_id](variables.tf#L23) | Folder to be used for the networking resources in folders/nnnn format. | string | ✓ | | resman | -| [organization](variables.tf#L73) | Organization details. | object({…}) | ✓ | | bootstrap | -| [prefix](variables.tf#L89) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 00-bootstrap | -| [groups](variables.tf#L29) | Group names to grant organization-level permissions. | map(string) | | {…} | bootstrap | -| [kms_defaults](variables.tf#L44) | Defaults used for KMS keys. | object({…}) | | {…} | | -| [kms_keys](variables.tf#L56) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | map(object({…})) | | {} | | -| [kms_restricted_admins](variables.tf#L67) | Map of environment => [identities] who can assign the encrypt/decrypt roles on keys. | map(list(string)) | | {} | | -| [outputs_location](variables.tf#L83) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | -| [vpc_sc_access_levels](variables.tf#L100) | VPC SC access level definitions. | map(object({…})) | | {} | | -| [vpc_sc_egress_policies](variables.tf#L115) | VPC SC egress policy defnitions. | map(object({…})) | | {} | | -| [vpc_sc_ingress_policies](variables.tf#L133) | VPC SC ingress policy defnitions. | map(object({…})) | | {} | | -| [vpc_sc_perimeter_access_levels](variables.tf#L153) | VPC SC perimeter access_levels. | object({…}) | | null | | -| [vpc_sc_perimeter_egress_policies](variables.tf#L163) | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | -| [vpc_sc_perimeter_ingress_policies](variables.tf#L173) | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | -| [vpc_sc_perimeter_projects](variables.tf#L183) | VPC SC perimeter resources. | object({…}) | | null | | +| [billing_account](variables.tf#L17) | Billing account id and organization id ('nnnnnnnn' or null). | object({…}) | ✓ | | 00-bootstrap | +| [folder_ids](variables.tf#L26) | Folder name => id mappings, the 'security' folder name must exist. | object({…}) | ✓ | | 01-resman | +| [organization](variables.tf#L81) | Organization details. | object({…}) | ✓ | | 00-bootstrap | +| [prefix](variables.tf#L97) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 00-bootstrap | +| [service_accounts](variables.tf#L72) | Automation service accounts that can assign the encrypt/decrypt roles on keys. | object({…}) | ✓ | | 01-resman | +| [groups](variables.tf#L34) | Group names to grant organization-level permissions. | map(string) | | {…} | 00-bootstrap | +| [kms_defaults](variables.tf#L49) | Defaults used for KMS keys. | object({…}) | | {…} | | +| [kms_keys](variables.tf#L61) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | map(object({…})) | | {} | | +| [outputs_location](variables.tf#L91) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| [vpc_sc_access_levels](variables.tf#L108) | VPC SC access level definitions. | map(object({…})) | | {} | | +| [vpc_sc_egress_policies](variables.tf#L123) | VPC SC egress policy defnitions. | map(object({…})) | | {} | | +| [vpc_sc_ingress_policies](variables.tf#L141) | VPC SC ingress policy defnitions. | map(object({…})) | | {} | | +| [vpc_sc_perimeter_access_levels](variables.tf#L161) | VPC SC perimeter access_levels. | object({…}) | | null | | +| [vpc_sc_perimeter_egress_policies](variables.tf#L171) | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | +| [vpc_sc_perimeter_ingress_policies](variables.tf#L181) | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | object({…}) | | null | | +| [vpc_sc_perimeter_projects](variables.tf#L191) | VPC SC perimeter resources. | object({…}) | | null | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [stage_perimeter_projects](outputs.tf#L37) | Security project numbers. They can be added to perimeter resources. | | | +| [kms_keys](outputs.tf#L52) | KMS key ids. | | | +| [stage_perimeter_projects](outputs.tf#L57) | Security project numbers. They can be added to perimeter resources. | | | +| [tfvars](outputs.tf#L67) | Terraform variable files for the following stages. | ✓ | | diff --git a/fast/stages/02-security/core-dev.tf b/fast/stages/02-security/core-dev.tf index db12bdee..92fcaec0 100644 --- a/fast/stages/02-security/core-dev.tf +++ b/fast/stages/02-security/core-dev.tf @@ -14,14 +14,20 @@ * limitations under the License. */ +locals { + dev_kms_restricted_admins = [ + "serviceAccount:${var.service_accounts.project-factory-dev}" + ] +} + module "dev-sec-project" { source = "../../../modules/project" name = "dev-sec-core-0" - parent = var.folder_id + parent = var.folder_ids.security prefix = var.prefix - billing_account = var.billing_account_id + billing_account = var.billing_account.id iam = { - "roles/cloudkms.viewer" = try(var.kms_restricted_admins.dev, []) + "roles/cloudkms.viewer" = local.dev_kms_restricted_admins } labels = { environment = "dev", team = "security" } services = local.project_services @@ -46,7 +52,7 @@ module "dev-sec-kms" { # TODO(ludo): grant delegated role at key instead of project level resource "google_project_iam_member" "dev_key_admin_delegated" { - for_each = toset(try(var.kms_restricted_admins.dev, [])) + for_each = toset(local.dev_kms_restricted_admins) project = module.dev-sec-project.project_id role = "roles/cloudkms.admin" member = each.key diff --git a/fast/stages/02-security/core-prod.tf b/fast/stages/02-security/core-prod.tf index 4154d050..d00c724d 100644 --- a/fast/stages/02-security/core-prod.tf +++ b/fast/stages/02-security/core-prod.tf @@ -14,14 +14,20 @@ * limitations under the License. */ +locals { + prod_kms_restricted_admins = [ + "serviceAccount:${var.service_accounts.project-factory-prod}" + ] +} + module "prod-sec-project" { source = "../../../modules/project" name = "prod-sec-core-0" - parent = var.folder_id + parent = var.folder_ids.security prefix = var.prefix - billing_account = var.billing_account_id + billing_account = var.billing_account.id iam = { - "roles/cloudkms.viewer" = try(var.kms_restricted_admins.prod, []) + "roles/cloudkms.viewer" = local.prod_kms_restricted_admins } labels = { environment = "prod", team = "security" } services = local.project_services @@ -45,7 +51,7 @@ module "prod-sec-kms" { # TODO(ludo): add support for conditions to Fabric modules resource "google_project_iam_member" "prod_key_admin_delegated" { - for_each = toset(try(var.kms_restricted_admins.prod, [])) + for_each = toset(local.prod_kms_restricted_admins) project = module.prod-sec-project.project_id role = "roles/cloudkms.admin" member = each.key diff --git a/fast/stages/02-security/outputs.tf b/fast/stages/02-security/outputs.tf index 15c75a2a..515f0712 100644 --- a/fast/stages/02-security/outputs.tf +++ b/fast/stages/02-security/outputs.tf @@ -14,26 +14,46 @@ * limitations under the License. */ -# optionally generate files for subsequent stages - -resource "local_file" "dev_sec_kms" { - for_each = var.outputs_location == null ? {} : { 1 = 1 } - filename = "${pathexpand(var.outputs_location)}/yamls/02-security-kms-dev-keys.yaml" - content = yamlencode({ - for k, m in module.dev-sec-kms : k => m.key_ids - }) +locals { + _output_kms_keys = concat( + flatten([ + for location, mod in module.dev-sec-kms : [ + for name, id in mod.key_ids : { + key = "dev:${name}:${location}" + id = id + } + ] + ]), + flatten([ + for location, mod in module.prod-sec-kms : [ + for name, id in mod.key_ids : { + key = "prod:${name}:${location}" + id = id + } + ] + ]) + ) + output_kms_keys = { for k in local._output_kms_keys : k.key => k.id } } -resource "local_file" "prod_sec_kms" { - for_each = var.outputs_location == null ? {} : { 1 = 1 } - filename = "${pathexpand(var.outputs_location)}/yamls/02-security-kms-prod-keys.yaml" - content = yamlencode({ - for k, m in module.prod-sec-kms : k => m.key_ids +# optionally generate files for subsequent stages + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + file_permission = "0644" + filename = "${pathexpand(var.outputs_location)}/tfvars/02-security.auto.tfvars.json" + content = jsonencode({ + kms_keys = local.output_kms_keys }) } # outputs +output "kms_keys" { + description = "KMS key ids." + value = local.output_kms_keys +} + output "stage_perimeter_projects" { description = "Security project numbers. They can be added to perimeter resources." value = { @@ -41,3 +61,13 @@ output "stage_perimeter_projects" { prod = ["projects/${module.prod-sec-project.number}"] } } + +# ready to use variable values for subsequent stages + +output "tfvars" { + description = "Terraform variable files for the following stages." + sensitive = true + value = { + kms_keys = local.output_kms_keys + } +} diff --git a/fast/stages/02-security/variables.tf b/fast/stages/02-security/variables.tf index 00cbe4dc..8ff52ffd 100644 --- a/fast/stages/02-security/variables.tf +++ b/fast/stages/02-security/variables.tf @@ -14,20 +14,25 @@ * limitations under the License. */ -variable "billing_account_id" { - # tfdoc:variable:source bootstrap - description = "Billing account id." - type = string +variable "billing_account" { + # tfdoc:variable:source 00-bootstrap + description = "Billing account id and organization id ('nnnnnnnn' or null)." + type = object({ + id = string + organization_id = number + }) } -variable "folder_id" { - # tfdoc:variable:source resman - description = "Folder to be used for the networking resources in folders/nnnn format." - type = string +variable "folder_ids" { + # tfdoc:variable:source 01-resman + description = "Folder name => id mappings, the 'security' folder name must exist." + type = object({ + security = string + }) } variable "groups" { - # tfdoc:variable:source bootstrap + # tfdoc:variable:source 00-bootstrap description = "Group names to grant organization-level permissions." type = map(string) # https://cloud.google.com/docs/enterprise/setup-checklist @@ -64,14 +69,17 @@ variable "kms_keys" { default = {} } -variable "kms_restricted_admins" { - description = "Map of environment => [identities] who can assign the encrypt/decrypt roles on keys." - type = map(list(string)) - default = {} +variable "service_accounts" { + # tfdoc:variable:source 01-resman + description = "Automation service accounts that can assign the encrypt/decrypt roles on keys." + type = object({ + project-factory-dev = string + project-factory-prod = string + }) } variable "organization" { - # tfdoc:variable:source bootstrap + # tfdoc:variable:source 00-bootstrap description = "Organization details." type = object({ domain = string