Skip to content

Commit

Permalink
Adding support for WIF based auth to Webhook Datasource module (#21)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ravinadhruve10 authored Mar 8, 2024
1 parent 6224133 commit 3ee5fc4
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 6 deletions.
20 changes: 20 additions & 0 deletions modules/services/webhook-datasource/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ 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

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | >= 4.21.0 |
| <a name="requirement_sysdig"></a> [sysdig](#requirement\_sysdig) | >= 1.19.0 |

## Providers

| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 5.0.0 |
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.1 |

## Modules

Expand All @@ -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

Expand All @@ -56,6 +69,9 @@ No modules.
| <a name="input_organization_domain"></a> [organization\_domain](#input\_organization\_domain) | Organization domain. e.g. sysdig.com | `string` | `""` | no |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | (Required) Target Project identifier provided by the customer | `string` | n/a | yes |
| <a name="input_push_endpoint"></a> [push\_endpoint](#input\_push\_endpoint) | (Required) Final endpoint towards which audit logs POST calls will be directed | `string` | n/a | yes |
| <a name="input_role_name"></a> [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 |
| <a name="input_external_id"></a> [external\_id](#input\_external\_id) | (Optional) Random string generated unique to a customer | `string` | `""` | no |
| <a name="input_suffix"></a> [suffix](#input\_suffix) | (Optional) Suffix to uniquely identify resources during multiple installs. If not provided, random value is autogenerated | `string` | `null` | no |

## Outputs

Expand All @@ -65,3 +81,7 @@ No modules.
| <a name="output_ingestion_pubsub_topic_name"></a> [ingestion\_pubsub\_topic\_name](#output\_ingestion\_pubsub\_topic\_name) | PubSub ingestion topic that will hold all the AuditLogs coming from the specified project |
| <a name="output_ingestion_sink_name"></a> [ingestion\_sink\_name](#output\_ingestion\_sink\_name) | Project/Organization sink to direct the AuditLogs towards a dedicated PubSub topic |
| <a name="output_ingestion_push_subscription_name"></a> [ingestion\_push\_subscription\_name](#output\_ingestion\_push\_subscription\_name) | Push Subscription that will POST the AuditLogs collected from the project towards Sysdig's backend |
| <a name="output_workload_identity_pool_id"></a> [workload\_identity\_pool\_id](#output\_workload\_identity\_pool\_id) | Id of Workload Identity Pool for authenticating to GCP to access data ingestion resources |
| <a name="output_workload_identity_pool_provider_id"></a> [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 |
| <a name="output_workload_identity_project_number"></a> [workload\_identity\_project\_number](#output\_workload\_identity\_project\_number) | GCP project number |
| <a name="output_service_account_email"></a> [service\_account\_email](#output\_service\_account\_email) | email of the Service Account created |
85 changes: 85 additions & 0 deletions modules/services/webhook-datasource/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
}
27 changes: 27 additions & 0 deletions modules/services/webhook-datasource/organizational.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
}
20 changes: 20 additions & 0 deletions modules/services/webhook-datasource/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
18 changes: 18 additions & 0 deletions modules/services/webhook-datasource/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
8 changes: 8 additions & 0 deletions modules/services/webhook-datasource/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ terraform {
required_providers {
sysdig = {
source = "sysdiglabs/sysdig"
version = "~> 1.18.2"
version = "~> 1.19.0"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ terraform {
required_providers {
sysdig = {
source = "sysdiglabs/sysdig"
version = "~> 1.18.2"
version = "~> 1.19.0"
}
}
}
Expand Down
29 changes: 27 additions & 2 deletions test/examples/secure_threat_detection/organization/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -24,7 +25,7 @@ terraform {
required_providers {
sysdig = {
source = "sysdiglabs/sysdig"
version = "~> 1.18.2"
version = "~> 1.19.0"
}
}
}
Expand All @@ -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"
Expand Down
29 changes: 27 additions & 2 deletions test/examples/secure_threat_detection/single/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ 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 {

required_providers {
sysdig = {
source = "sysdiglabs/sysdig"
version = "~> 1.18.2"
version = "~> 1.19.0"
}
}
}
Expand All @@ -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
}
})
}
}

0 comments on commit 3ee5fc4

Please sign in to comment.