From 3ee5fc41f7fa69e157ecd3be8e1b1b04983e288a Mon Sep 17 00:00:00 2001 From: Ravina Dhruve <136399755+ravinadhruve10@users.noreply.github.com> Date: Fri, 8 Mar 2024 15:29:16 -0800 Subject: [PATCH] Adding support for WIF based auth to Webhook Datasource module (#21) * Adding support for WIF based auth to Webhook Datasource module Change summary: ----------------- To add auth support for validating cloud ingestion resources :- - Adding the support for creating WIF resources with AWS provider. - Allowing external trusted identity to be able to impersonate the SA and use temporary service tokens for authentication. This is to allow Sysdig backend to be able to talk to GCP to access and read the cloud resources. - Using assumed role attribute as the signature for trusted identity. - Creating and binding a custom role to the SA, with just the right set of permissions to read data ingestion cloud resources. - Updating the module outputs with the fields to pass in as metadata to Sysdig backend to do the auth. * Fix fmt * Fix sysdig provider version in tests * Restrict the WIF identity signtaure to use external_id within the assumed role * Fix org installs for webhook datasource with org-level permissions * Add README and tests * Add comment for randomly generated suffix --- modules/services/webhook-datasource/README.md | 20 +++++ modules/services/webhook-datasource/main.tf | 85 +++++++++++++++++++ .../webhook-datasource/organizational.tf | 27 ++++++ .../services/webhook-datasource/outputs.tf | 20 +++++ .../services/webhook-datasource/variables.tf | 18 ++++ .../services/webhook-datasource/versions.tf | 8 ++ .../organization/main.tf | 2 +- .../single/main.tf | 2 +- .../organization/main.tf | 29 ++++++- .../secure_threat_detection/single/main.tf | 29 ++++++- 10 files changed, 234 insertions(+), 6 deletions(-) diff --git a/modules/services/webhook-datasource/README.md b/modules/services/webhook-datasource/README.md index 71a2c09..40486d4 100644 --- a/modules/services/webhook-datasource/README.md +++ b/modules/services/webhook-datasource/README.md @@ -8,6 +8,7 @@ The following resources will be created in each instrumented account: - A `PubSub` ingestion topic that will hold all the AuditLogs coming from the specified project - A `Push` Subscription that will POST the AuditLogs collected from the project towards Sysdig's backend - All the necessary `Service Accounts` and `Policies` to enable the `AuditLogs` publishing operation +- A `Workload Identity Pool`, `Provider` and added custom role permissions to the `Service Account`, to allow Sysdig to authenticate to GCP on your behalf to validate resources. ## Requirements @@ -15,12 +16,14 @@ The following resources will be created in each instrumented account: |------|---------| | [terraform](#requirement\_terraform) | >= 1.0.0 | | [google](#requirement\_google) | >= 4.21.0 | +| [sysdig](#requirement\_sysdig) | >= 1.19.0 | ## Providers | Name | Version | |------|---------| | [google](#provider\_google) | 5.0.0 | +| [random](#provider\_random) | >= 3.1 | ## Modules @@ -41,6 +44,16 @@ No modules. | [google_service_account.push_auth](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource | | [google_service_account_iam_binding.push_auth_binding](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_iam_binding) | resource | | [google_organization.org](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/organization) | data source | +| [sysdig_secure_trusted_cloud_identity.trusted_identity](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_trusted_cloud_identity) | data source | +| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source | +| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | +| [google_iam_workload_identity_pool.ingestion_auth_pool](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iam_workload_identity_pool) | resource | +| [google_iam_workload_identity_pool_provider.ingestion_auth_pool_provider](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iam_workload_identity_pool_provider) | resource | +| [google_project_iam_custom_role.custom_ingestion_auth_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam_custom_role) | resource | +| [google_project_iam_member.custom](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_member) | resource | +| [google_service_account_iam_member.custom_auth](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account_iam#google_service_account_iam_member) | resource | +| [google_organization_iam_custom_role.custom_ingestion_auth_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_organization_iam_custom_role) | resource | +| [google_organization_iam_member.custom](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_organization_iam#google_organization_iam_member) | resource | ## Inputs @@ -56,6 +69,9 @@ No modules. | [organization\_domain](#input\_organization\_domain) | Organization domain. e.g. sysdig.com | `string` | `""` | no | | [project\_id](#input\_project\_id) | (Required) Target Project identifier provided by the customer | `string` | n/a | yes | | [push\_endpoint](#input\_push\_endpoint) | (Required) Final endpoint towards which audit logs POST calls will be directed | `string` | n/a | yes | +| [role\_name](#input\_role\_name) | (Optional) Role name for custom role binding to the service account, with read permissions for data ingestion resources | `string` | `"SysdigIngestionAuthRole"` | no | +| [external\_id](#input\_external\_id) | (Optional) Random string generated unique to a customer | `string` | `""` | no | +| [suffix](#input\_suffix) | (Optional) Suffix to uniquely identify resources during multiple installs. If not provided, random value is autogenerated | `string` | `null` | no | ## Outputs @@ -65,3 +81,7 @@ No modules. | [ingestion\_pubsub\_topic\_name](#output\_ingestion\_pubsub\_topic\_name) | PubSub ingestion topic that will hold all the AuditLogs coming from the specified project | | [ingestion\_sink\_name](#output\_ingestion\_sink\_name) | Project/Organization sink to direct the AuditLogs towards a dedicated PubSub topic | | [ingestion\_push\_subscription\_name](#output\_ingestion\_push\_subscription\_name) | Push Subscription that will POST the AuditLogs collected from the project towards Sysdig's backend | +| [workload\_identity\_pool\_id](#output\_workload\_identity\_pool\_id) | Id of Workload Identity Pool for authenticating to GCP to access data ingestion resources | +| [workload\_identity\_pool\_provider\_id](#output\_workload\_identity\_pool\_provider\_id) | Id of Workload Identity Pool Provider for authenticating to GCP to access data ingestion resources | +| [workload\_identity\_project\_number](#output\_workload\_identity\_project\_number) | GCP project number | +| [service\_account\_email](#output\_service\_account\_email) | email of the Service Account created | \ No newline at end of file diff --git a/modules/services/webhook-datasource/main.tf b/modules/services/webhook-datasource/main.tf index 719015a..bf5e07a 100644 --- a/modules/services/webhook-datasource/main.tf +++ b/modules/services/webhook-datasource/main.tf @@ -141,3 +141,88 @@ resource "google_pubsub_subscription" "ingestion_topic_push_subscription" { } } +#------------------------------------------------------------------# +# Fetch and compute required data for Workload Identity Federation # +#------------------------------------------------------------------# + +data "sysdig_secure_trusted_cloud_identity" "trusted_identity" { + cloud_provider = "gcp" +} + +data "google_project" "project" { + project_id = var.project_id +} + +locals { + suffix = var.suffix == null ? random_id.suffix[0].hex : var.suffix +} + +// suffix to uniquely identify WIF pool and provider during multiple installs. If suffix value is not provided, this will generate a random value. +resource "random_id" "suffix" { + count = var.suffix == null ? 1 : 0 + byte_length = 3 +} + +#------------------------------------------------------------# +# Configure Workload Identity Federation for auth # +# See https://cloud.google.com/iam/docs/access-resources-aws # +#------------------------------------------------------------# + +resource "google_iam_workload_identity_pool" "ingestion_auth_pool" { + project = var.project_id + workload_identity_pool_id = "sysdig-ingestion-${local.suffix}" +} + +resource "google_iam_workload_identity_pool_provider" "ingestion_auth_pool_provider" { + project = var.project_id + workload_identity_pool_id = google_iam_workload_identity_pool.ingestion_auth_pool.workload_identity_pool_id + workload_identity_pool_provider_id = "sysdig-ingestion-${local.suffix}" + display_name = "Sysdigcloud ingestion auth" + description = "AWS identity pool provider for Sysdig Secure Data Ingestion resources" + disabled = false + + attribute_condition = "attribute.aws_role==\"arn:aws:sts::${data.sysdig_secure_trusted_cloud_identity.trusted_identity.aws_account_id}:assumed-role/${data.sysdig_secure_trusted_cloud_identity.trusted_identity.aws_role_name}/${var.external_id}\"" + + attribute_mapping = { + "google.subject" = "assertion.arn", + "attribute.aws_role" = "assertion.arn" + } + + aws { + account_id = data.sysdig_secure_trusted_cloud_identity.trusted_identity.aws_account_id + } +} + +# creating custom role with project-level permissions to access data ingestion resources +resource "google_project_iam_custom_role" "custom_ingestion_auth_role" { + count = var.is_organizational ? 0 : 1 + + project = var.project_id + role_id = var.role_name + title = "Sysdigcloud Ingestion Auth Role" + description = "A Role providing the required permissions for Sysdig Backend to read cloud resources created for data ingestion" + permissions = [ + "pubsub.topics.get", + "pubsub.topics.list", + "pubsub.subscriptions.get", + "pubsub.subscriptions.list", + "logging.sinks.get", + "logging.sinks.list", + ] +} + +# adding custom role with project-level permissions to the service account for auth +resource "google_project_iam_member" "custom" { + count = var.is_organizational ? 0 : 1 + + project = var.project_id + role = google_project_iam_custom_role.custom_ingestion_auth_role[0].id + member = "serviceAccount:${google_service_account.push_auth.email}" +} + +# attaching WIF as a member to the service account for auth +resource "google_service_account_iam_member" "custom_auth" { + service_account_id = google_service_account.push_auth.name + role = "roles/iam.workloadIdentityUser" + member = "principalSet://iam.googleapis.com/projects/${data.google_project.project.number}/locations/global/workloadIdentityPools/${google_iam_workload_identity_pool.ingestion_auth_pool.workload_identity_pool_id}/attribute.aws_role/arn:aws:sts::${data.sysdig_secure_trusted_cloud_identity.trusted_identity.aws_account_id}:assumed-role/${data.sysdig_secure_trusted_cloud_identity.trusted_identity.aws_role_name}/${var.external_id}" +} diff --git a/modules/services/webhook-datasource/organizational.tf b/modules/services/webhook-datasource/organizational.tf index 74fdedd..42d5c4e 100644 --- a/modules/services/webhook-datasource/organizational.tf +++ b/modules/services/webhook-datasource/organizational.tf @@ -48,3 +48,30 @@ resource "google_logging_organization_sink" "ingestion_sink" { # even from potential sub-organizations include_children = true } + +# creating custom role with organization-level permissions to access data ingestion resources +resource "google_organization_iam_custom_role" "custom_ingestion_auth_role" { + count = var.is_organizational ? 1 : 0 + + org_id = data.google_organization.org[0].org_id + role_id = var.role_name + title = "Sysdigcloud Ingestion Auth Role" + description = "A Role providing the required permissions for Sysdig Backend to read cloud resources created for data ingestion" + permissions = [ + "pubsub.topics.get", + "pubsub.topics.list", + "pubsub.subscriptions.get", + "pubsub.subscriptions.list", + "logging.sinks.get", + "logging.sinks.list", + ] +} + +# adding custom role with organization-level permissions to the service account for auth +resource "google_organization_iam_member" "custom" { + count = var.is_organizational ? 1 : 0 + + org_id = data.google_organization.org[0].org_id + role = google_organization_iam_custom_role.custom_ingestion_auth_role[0].id + member = "serviceAccount:${google_service_account.push_auth.email}" +} diff --git a/modules/services/webhook-datasource/outputs.tf b/modules/services/webhook-datasource/outputs.tf index b726ec3..9f15f3b 100644 --- a/modules/services/webhook-datasource/outputs.tf +++ b/modules/services/webhook-datasource/outputs.tf @@ -17,3 +17,23 @@ output "ingestion_push_subscription_name" { value = google_pubsub_subscription.ingestion_topic_push_subscription.name description = "Push Subscription that will POST the AuditLogs collected from the project towards Sysdig's backend" } + +output "workload_identity_pool_id" { + value = google_iam_workload_identity_pool.ingestion_auth_pool.workload_identity_pool_id + description = "Id of Workload Identity Pool for authenticating to GCP to access data ingestion resources" +} + +output "workload_identity_pool_provider_id" { + value = google_iam_workload_identity_pool_provider.ingestion_auth_pool_provider.workload_identity_pool_provider_id + description = "Id of Workload Identity Pool Provider for authenticating to GCP to access data ingestion resources" +} + +output "workload_identity_project_number" { + value = data.google_project.project.number + description = "GCP project number" +} + +output "service_account_email" { + value = google_service_account.push_auth.email + description = "email of the Service Account created" +} diff --git a/modules/services/webhook-datasource/variables.tf b/modules/services/webhook-datasource/variables.tf index 859cced..894f4ba 100644 --- a/modules/services/webhook-datasource/variables.tf +++ b/modules/services/webhook-datasource/variables.tf @@ -57,3 +57,21 @@ variable "organization_domain" { description = "(Optional) Organization domain. e.g. sysdig.com" default = "" } + +variable "role_name" { + type = string + description = "Name for the Ingestion auth Role on the Customer infrastructure" + default = "SysdigIngestionAuthRole" +} + +variable "external_id" { + type = string + description = "Random string generated unique to a customer" + default = "" +} + +variable "suffix" { + type = string + description = "Suffix to uniquely identify resources during multiple installs. If not provided, random value is autogenerated" + default = null +} diff --git a/modules/services/webhook-datasource/versions.tf b/modules/services/webhook-datasource/versions.tf index 675359b..2ee91bc 100644 --- a/modules/services/webhook-datasource/versions.tf +++ b/modules/services/webhook-datasource/versions.tf @@ -6,5 +6,13 @@ terraform { source = "hashicorp/google" version = ">= 4.21.0" } + sysdig = { + source = "sysdiglabs/sysdig" + version = "~> 1.19.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.1, < 4.0" + } } } diff --git a/test/examples/secure_config_posture_identity_access/organization/main.tf b/test/examples/secure_config_posture_identity_access/organization/main.tf index cbbfc99..c80e69a 100644 --- a/test/examples/secure_config_posture_identity_access/organization/main.tf +++ b/test/examples/secure_config_posture_identity_access/organization/main.tf @@ -16,7 +16,7 @@ terraform { required_providers { sysdig = { source = "sysdiglabs/sysdig" - version = "~> 1.18.2" + version = "~> 1.19.0" } } } diff --git a/test/examples/secure_config_posture_identity_access/single/main.tf b/test/examples/secure_config_posture_identity_access/single/main.tf index 67ecee1..c6a41b3 100644 --- a/test/examples/secure_config_posture_identity_access/single/main.tf +++ b/test/examples/secure_config_posture_identity_access/single/main.tf @@ -14,7 +14,7 @@ terraform { required_providers { sysdig = { source = "sysdiglabs/sysdig" - version = "~> 1.18.2" + version = "~> 1.19.0" } } } diff --git a/test/examples/secure_threat_detection/organization/main.tf b/test/examples/secure_threat_detection/organization/main.tf index fe441df..c21296d 100644 --- a/test/examples/secure_threat_detection/organization/main.tf +++ b/test/examples/secure_threat_detection/organization/main.tf @@ -9,6 +9,7 @@ module "organization-threat-detection" { push_endpoint = "test_sysdig_secure_cloudingestion_endpoint" is_organizational = true organization_domain = "mytestorg.com" + external_id = "external_id" } module "organization-posture" { @@ -24,7 +25,7 @@ terraform { required_providers { sysdig = { source = "sysdiglabs/sysdig" - version = "~> 1.18.2" + version = "~> 1.19.0" } } } @@ -43,12 +44,36 @@ resource "sysdig_secure_cloud_auth_account" "gcp_project_mytestproject" { secure_threat_detection { enabled = true - components = ["COMPONENT_WEBHOOK_DATASOURCE/secure-runtime"] + components = ["COMPONENT_WEBHOOK_DATASOURCE/secure-runtime", "COMPONENT_SERVICE_PRINCIPAL/secure-runtime"] } } component { type = "COMPONENT_WEBHOOK_DATASOURCE" instance = "secure-runtime" + webhook_datasource_metadata = jsonencode({ + gcp = { + webhook_datasource = { + pubsub_topic_name = module.organization-threat-detection.ingestion_pubsub_topic_name + sink_name = module.organization-threat-detection.ingestion_sink_name + push_subscription_name = module.organization-threat-detection.ingestion_push_subscription_name + push_endpoint = module.organization-threat-detection.push_endpoint + } + } + }) + } + component { + type = "COMPONENT_SERVICE_PRINCIPAL" + instance = "secure-runtime" + service_principal_metadata = jsonencode({ + gcp = { + workload_identity_federation = { + pool_id = module.organization-threat-detection.workload_identity_pool_id + pool_provider_id = module.organization-threat-detection.workload_identity_pool_provider_id + project_number = module.organization-threat-detection.workload_identity_project_number + } + email = module.organization-threat-detection.service_account_email + } + }) } component { type = "COMPONENT_SERVICE_PRINCIPAL" diff --git a/test/examples/secure_threat_detection/single/main.tf b/test/examples/secure_threat_detection/single/main.tf index 494a3ae..b5f5e73 100644 --- a/test/examples/secure_threat_detection/single/main.tf +++ b/test/examples/secure_threat_detection/single/main.tf @@ -7,6 +7,7 @@ module "single-project-threat-detection" { source = "../../../..//modules/services/webhook-datasource" project_id = "mytestproject" push_endpoint = "test_sysdig_secure_cloudingestion_endpoint" + external_id = "external_id" } terraform { @@ -14,7 +15,7 @@ terraform { required_providers { sysdig = { source = "sysdiglabs/sysdig" - version = "~> 1.18.2" + version = "~> 1.19.0" } } } @@ -33,12 +34,36 @@ resource "sysdig_secure_cloud_auth_account" "gcp_project_mytestproject" { secure_threat_detection { enabled = true - components = ["COMPONENT_WEBHOOK_DATASOURCE/secure-runtime"] + components = ["COMPONENT_WEBHOOK_DATASOURCE/secure-runtime", "COMPONENT_SERVICE_PRINCIPAL/secure-runtime"] } } component { type = "COMPONENT_WEBHOOK_DATASOURCE" instance = "secure-runtime" + webhook_datasource_metadata = jsonencode({ + gcp = { + webhook_datasource = { + pubsub_topic_name = module.single-project-threat-detection.ingestion_pubsub_topic_name + sink_name = module.single-project-threat-detection.ingestion_sink_name + push_subscription_name = module.single-project-threat-detection.ingestion_push_subscription_name + push_endpoint = module.single-project-threat-detection.push_endpoint + } + } + }) + } + component { + type = "COMPONENT_SERVICE_PRINCIPAL" + instance = "secure-runtime" + service_principal_metadata = jsonencode({ + gcp = { + workload_identity_federation = { + pool_id = module.single-project-threat-detection.workload_identity_pool_id + pool_provider_id = module.single-project-threat-detection.workload_identity_pool_provider_id + project_number = module.single-project-threat-detection. workload_identity_project_number + } + email = module.single-project-threat-detection.service_account_email + } + }) } }