diff --git a/modules/integrations/pub-sub/README.md b/modules/integrations/pub-sub/README.md new file mode 100644 index 0000000..27d53a6 --- /dev/null +++ b/modules/integrations/pub-sub/README.md @@ -0,0 +1,108 @@ +# GCP PubSub Module + +This Module creates the resources required to send AuditLogs logs to Sysdig via GCP Pub Subscription. These resources enable Threat Detection in the given GCP project or organization. +Before applying the changes defined in this module, the following operations need to be performed on the target GCP environment: + +- The APIs needed for the CDR/CIEM feature are listed below: + - Cloud Pub/Sub API + +- The following resources will be created in each instrumented project: + - A `PubSub Topic` to send the AuditLogs from the project + - A `Logging Sink` that export AuditLogs to the PubSub topic + - A `PubSub Topic IAM member` to assign a role to the PubSub topic + - A `PubSub Subscription` to allows receiving messages from the PubSub topic, including its `Service Account` + - An `IAM Workload Identity Pool` that enables identities from external systems(AWS) tp access GCP resources through IAM + - An `IAM role and member` that provides the required permissions for Sysdig Backend to read cloud resources created for data ingestion + +When run in organizational mode, this module is similar however the main difference is that an organizational sink is used +instead of a project-specific one, as well as enabling AuditLogs for all the projects that fall within the organization. + +This module will also deploy a Webhook Datasource Component in Sysdig Backend for onboarded Sysdig Cloud Account. + + +## Requirements + +| Name | Version | +|---------------------------------------------------------------------------|-----------| +| [terraform](#requirement\_terraform) | >= 1.0.0 | +| [google](#requirement\_google) | >= 4.21.0 | +| [sysdig](#requirement\_sysdig) | | + +## Providers + +| Name | Version | +|------------------------------------------------------------------|-----------| +| [google](#provider\_google) | >= 4.21.0 | +| [random](#requirement\_random) | >= 3.1 | +| [sysdig](#requirement\_sysdig) | | + + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------| +| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | +| [google_project_iam_audit_config.audit_config](https://registry.terraform.io/providers/hashicorp/google/3.0.0-beta.1/docs/resources/google_project_iam#google_project_iam_audit_config) | resource | +| [google_pubsub_topic.ingestion_topic](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic.html) | resource | +| [google_pubsub_topic.deadletter_topic](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic.html) | resource | +| [google_logging_project_sink.ingestion_sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/logging_project_sink) | resource | +| [google_pubsub_topic_iam_member.publisher_iam_member](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_topic_iam#google_pubsub_topic_iam_member) | resource | +| [google_service_account.push_auth](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account) | resource | +| [google_service_account_iam_binding.push_auth_binding](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account_iam#google_service_account_iam_binding) | resource | +| [google_pubsub_subscription.ingestion_topic_push_subscription](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/pubsub_subscription.html) | 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/3.22.0/docs/resources/google_project_iam#google_project_iam_member) | resource | +| [google_project_iam_member.identity_mgmt](https://registry.terraform.io/providers/hashicorp/google/3.22.0/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 | +| [sysdig_secure_trusted_cloud_identity.trusted_identity](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_trusted_cloud_identity) | data source | +| [sysdig_secure_cloud_auth_account_component.gcp_pubsub_datasource](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/resources/secure_cloud_auth_account_component) | resource | +| [sysdig_secure_tenant_external_id](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_tenant_external_id) | data source | +| [sysdig_secure_cloud_ingestion_assets](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_cloud_ingestion_assets) | data source | +| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source | +| [google_organization.org](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/organization) | data source | +| [google_organization_iam_audit_config.audit_config](https://registry.terraform.io/providers/hashicorp/google/3.24.0/docs/resources/google_organization_iam_audit_config) | resource | +| [google_logging_organization_sink.ingestion_sink](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/logging_organization_sink) | resoruce | +| [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 | +| [google_organization_iam_member.identity_mgmt](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_organization_iam#google_organization_iam_member) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:| +| [ack\_deadline\_seconds](#input\_ack\_deadline\_seconds) | (Optional) Maximum time in seconds after Sysdig's subscriber receives a message before the subscriber should acknowledge the message | `number` | `60` | no | +| [is\_organizational](#input\_is\_organizational) | (Optional) Set this field to 'true' to deploy secure-for-cloud to a GCP Organization. | `bool` | `false` | no | +| [labels](#input\_labels) | (Optional) Labels to be associated with Sysdig-originated resources | `map(string)` |
{
"originator": "sysdig"
} | no |
+| [max\_delivery\_attempts](#input\_max\_delivery\_attempts) | (Optional) Number of attempts redelivering missed messages from the deadletter topic to the main one | `number` | `5` | no |
+| [maximum\_backoff](#input\_maximum\_backoff) | (Optional) Maximum backoff time for exponential backoff of the push subscription retry policy | `string` | `"600s"` | no |
+| [message\_retention\_duration](#input\_message\_retention\_duration) | (Optional) How long unacknowledged messages are retained in Sysdig's subscription backlog, from the moment a message is published | `string` | `"604800s"` | no |
+| [minimum\_backoff](#input\_minimum\_backoff) | (Optional) Minimum backoff time for exponential backoff of the push subscription retry policy | `string` | `"10s"` | no |
+| [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 |
+| [suffix](#input\_suffix) | (Optional) Suffix to uniquely identify resources during multiple installs. If not provided, random value is autogenerated | `string` | `null` | no |
+| [audit\_log\_config](#input\_audit\_log\_config) | List of services and their audit log configurations to be ingested. Default is to ingest all logs. | list(object({
service = string,
log_config = list(object({
log_type = string,
exempted_members = optional(list(string))
}))
})) | [| no | +| [ingestion\_sink\_filter](#input\_ingestion\_sink\_filter) | Filter the Sink is set up with. Ingests AuditLogs by default. | `string` | `protoPayload.@type = "type.googleapis.com/google.cloud.audit.AuditLog"` | no | +| [exclude\_logs\_filter](#input\_exclude\_logs\_filter) | Filter to exclude logs from ingestion. Default is to ingest all google.cloud.audit.AuditLog logs. with no exclusions. |
{
"log_config": [
{
"log_type": "ADMIN_READ"
},
{
"log_type": "DATA_READ"
},
{
"log_type": "DATA_WRITE"
}
],
"service": "allServices"
}
]
list(object({
name = string,
description = optional(string),
filter = string,
disabled = optional(bool)
})) | `[]` | no |
+| [sysdig\_secure\_account\_id](#input\_sysdig\_secure\_account\_id) | ID of the Sysdig Cloud Account to enable Event Bridge integration for (incase of organization, ID of the Sysdig management account) | `string` | `""` | no |
+
+## Outputs
+
+| Name | Description |
+|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
+| [pubsub\_datasource\_component\_id](#pubsub\_datasource\_component\_id) | Component identifier of Pub Sub integration created in Sysdig Backend for Log Ingestion |
+
+
+
+## Authors
+
+Module is maintained by [Sysdig](https://sysdig.com).
+
+## License
+
+Apache 2 Licensed. See LICENSE for full details.
diff --git a/modules/integrations/pub-sub/main.tf b/modules/integrations/pub-sub/main.tf
new file mode 100644
index 0000000..afe2c9e
--- /dev/null
+++ b/modules/integrations/pub-sub/main.tf
@@ -0,0 +1,274 @@
+#-----------------------------------------------------------------------------------------------------------------------------------------
+# For Organizational installs, see organizational.tf.
+# This module takes care of provisioning the necessary resources to make Sysdig's backend able to ingest data from a
+# single GCP project.
+#
+# Note: The alternative definitions for the organizational variant of this module are contained
+# in organizational.tf. The only differences w.r.t. the standalone template is in using an
+# organizational sink instead of a project-specific one, as well as enabling AuditLogs for
+# all the projects that fall within the organization.
+#-----------------------------------------------------------------------------------------------------------------------------------------
+
+#-----------------------------------------------------------------------------------------
+# Fetch the data sources
+#-----------------------------------------------------------------------------------------
+data "sysdig_secure_trusted_cloud_identity" "trusted_identity" {
+ cloud_provider = "gcp"
+}
+
+data "google_project" "project" {
+ project_id = var.project_id
+}
+
+data "sysdig_secure_tenant_external_id" "external_id" {}
+
+data "sysdig_secure_cloud_ingestion_assets" "assets" {}
+
+#-----------------------------------------------------------------------------------------
+# These locals indicate the suffix to create unique name for resources
+#-----------------------------------------------------------------------------------------
+locals {
+ suffix = var.suffix == null ? random_id.suffix[0].hex : var.suffix
+ role_name = "SysdigIngestionAuthRole"
+}
+
+
+#-----------------------------------------------------------------------------------------------------------------------
+# A random resource is used to generate unique Pub Sub name suffix for resources.
+# This prevents conflicts when recreating a Pub Sub resources with the same name.
+#-----------------------------------------------------------------------------------------------------------------------
+resource "random_id" "suffix" {
+ count = var.suffix == null ? 1 : 0
+ byte_length = 3
+}
+
+#-----------------------------------------------------------------------------------------
+# Audit Logs
+#-----------------------------------------------------------------------------------------
+locals {
+ # Data structure will be a map for each service, that can have multiple audit_log_config
+ audit_log_config = { for audit in var.audit_log_config :
+ audit["service"] => {
+ log_config = audit["log_config"]
+ }
+ }
+}
+
+resource "google_project_iam_audit_config" "audit_config" {
+ for_each = var.is_organizational ? {} : local.audit_log_config
+ project = var.project_id
+ service = each.key
+
+ dynamic "audit_log_config" {
+ for_each = each.value.log_config
+ iterator = log_config
+ content {
+ log_type = log_config.value.log_type
+ exempted_members = log_config.value.exempted_members
+ }
+ }
+}
+
+#-----------------------------------------------------------------------------------------
+# Ingestion Topic
+#-----------------------------------------------------------------------------------------
+resource "google_pubsub_topic" "ingestion_topic" {
+ name = "ingestion_topic"
+ labels = var.labels
+ project = var.project_id
+ message_retention_duration = var.message_retention_duration
+}
+
+resource "google_pubsub_topic" "deadletter_topic" {
+ name = "dl-${google_pubsub_topic.ingestion_topic.name}"
+ project = var.project_id
+ message_retention_duration = var.message_retention_duration
+}
+
+#-----------------------------------------------------------------------------------------
+# Sink
+#-----------------------------------------------------------------------------------------
+resource "google_logging_project_sink" "ingestion_sink" {
+ count = var.is_organizational ? 0 : 1
+ name = "${google_pubsub_topic.ingestion_topic.name}_sink"
+ description = "Sysdig sink to direct the AuditLogs to the PubSub topic used for data gathering"
+
+ # NOTE: The target destination is a PubSub topic
+ destination = "pubsub.googleapis.com/projects/${var.project_id}/topics/${google_pubsub_topic.ingestion_topic.name}"
+ filter = var.ingestion_sink_filter
+
+ # Dynamic block to exclude logs from ingestion
+ dynamic "exclusions" {
+ for_each = var.exclude_logs_filter
+ content {
+ name = exclusions.value.name
+ description = exclusions.value.description
+ filter = exclusions.value.filter
+ disabled = exclusions.value.disabled
+ }
+ }
+
+ # NOTE: Used to create a dedicated writer identity and not using the default one
+ unique_writer_identity = true
+}
+
+resource "google_pubsub_topic_iam_member" "publisher_iam_member" {
+ project = google_pubsub_topic.ingestion_topic.project
+ topic = google_pubsub_topic.ingestion_topic.name
+ role = "roles/pubsub.publisher"
+ member = var.is_organizational ? google_logging_organization_sink.ingestion_sink[0].writer_identity : google_logging_project_sink.ingestion_sink[0].writer_identity
+}
+
+#-----------------------------------------------------------------------------------------
+# Push Subscription
+#-----------------------------------------------------------------------------------------
+resource "google_service_account" "push_auth" {
+ account_id = "sysdig-ingestion-${local.suffix}"
+ display_name = "Sysdig Ingestion Push Auth Service Account"
+ project = var.project_id
+}
+
+resource "google_service_account_iam_binding" "push_auth_binding" {
+ service_account_id = google_service_account.push_auth.name
+ role = "roles/iam.workloadIdentityUser"
+
+ members = [
+ "serviceAccount:${google_service_account.push_auth.email}",
+ ]
+}
+
+resource "google_pubsub_subscription" "ingestion_topic_push_subscription" {
+ name = "${google_pubsub_topic.ingestion_topic.name}_push_subscription"
+ topic = google_pubsub_topic.ingestion_topic.name
+ labels = var.labels
+ ack_deadline_seconds = var.ack_deadline_seconds
+ message_retention_duration = var.message_retention_duration
+ project = var.project_id
+
+ push_config {
+ push_endpoint = data.sysdig_secure_cloud_ingestion_assets.assets.gcp_metadata.ingestionURL
+ attributes = {
+ x-goog-version = "v1"
+ }
+ oidc_token {
+ service_account_email = google_service_account.push_auth.email
+ audience = "sysdig_secure"
+ }
+ }
+
+ retry_policy {
+ minimum_backoff = var.minimum_backoff
+ maximum_backoff = var.maximum_backoff
+ }
+
+ dead_letter_policy {
+ dead_letter_topic = google_pubsub_topic.deadletter_topic.id
+ max_delivery_attempts = var.max_delivery_attempts
+ }
+}
+
+#-----------------------------------------------------------------------------------------
+# 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}/${data.sysdig_secure_tenant_external_id.external_id.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 = "${local.role_name}${local.suffix}"
+ 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}/${data.sysdig_secure_tenant_external_id.external_id.external_id}"
+}
+
+# adding ciem role with permissions to the service account
+resource "google_project_iam_member" "identity_mgmt" {
+ for_each = var.is_organizational ? [] : toset(["roles/recommender.viewer", "roles/iam.serviceAccountViewer", "roles/iam.roleViewer", "roles/container.clusterViewer", "roles/compute.viewer"])
+
+ project = var.project_id
+ role = each.key
+ member = "serviceAccount:${google_service_account.push_auth.email}"
+}
+
+#-----------------------------------------------------------------------------------------------------------------------------------------
+# Call Sysdig Backend to add the pub-sub integration to the Sysdig Cloud Account
+#
+# Note (optional): To ensure this gets called after all cloud resources are created, add
+# explicit dependency using depends_on
+#-----------------------------------------------------------------------------------------------------------------------------------------
+
+resource "sysdig_secure_cloud_auth_account_component" "gcp_pubsub_datasource" {
+ account_id = var.sysdig_secure_account_id
+ type = "COMPONENT_WEBHOOK_DATASOURCE"
+ instance = "secure-runtime"
+ version = "v0.1.0"
+ webhook_datasource_metadata = jsonencode({
+ gcp = {
+ webhook_datasource = {
+ pubsub_topic_name = google_pubsub_topic.ingestion_topic.name
+ sink_name = var.is_organizational ? google_logging_organization_sink.ingestion_sink[0].name : google_logging_project_sink.ingestion_sink[0].name
+ push_subscription_name = google_pubsub_subscription.ingestion_topic_push_subscription.name
+ push_endpoint = google_pubsub_subscription.ingestion_topic_push_subscription.push_config[0].push_endpoint
+ routing_key = data.sysdig_secure_cloud_ingestion_assets.assets.gcp_routing_key
+ }
+ service_principal = {
+ workload_identity_federation = {
+ pool_id = google_iam_workload_identity_pool.ingestion_auth_pool.workload_identity_pool_id
+ pool_provider_id = google_iam_workload_identity_pool_provider.ingestion_auth_pool_provider.workload_identity_pool_provider_id
+ project_number = data.google_project.project.number
+ }
+ email = google_service_account.push_auth.email
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/modules/integrations/pub-sub/organizational.tf b/modules/integrations/pub-sub/organizational.tf
new file mode 100644
index 0000000..b721906
--- /dev/null
+++ b/modules/integrations/pub-sub/organizational.tf
@@ -0,0 +1,95 @@
+#-----------------------------------------------------------------------------------------
+# Fetch the data sources
+#-----------------------------------------------------------------------------------------
+
+data "google_organization" "org" {
+ count = var.is_organizational ? 1 : 0
+ domain = var.organization_domain
+}
+
+#-----------------------------------------------------------------------------------------
+# Audit Logs
+#-----------------------------------------------------------------------------------------
+
+resource "google_organization_iam_audit_config" "audit_config" {
+ for_each = var.is_organizational ? local.audit_log_config : {}
+
+ org_id = data.google_organization.org[0].org_id
+ service = each.key
+
+ dynamic "audit_log_config" {
+ for_each = each.value.log_config
+ iterator = log_config
+ content {
+ log_type = log_config.value.log_type
+ exempted_members = log_config.value.exempted_members
+ }
+ }
+}
+
+#-----------------------------------------------------------------------------------------
+# Sink
+#-----------------------------------------------------------------------------------------
+
+resource "google_logging_organization_sink" "ingestion_sink" {
+ count = var.is_organizational ? 1 : 0
+
+ name = "${google_pubsub_topic.ingestion_topic.name}_sink"
+ description = "Sysdig sink to direct the AuditLogs to the PubSub topic used for data gathering"
+ org_id = data.google_organization.org[0].org_id
+
+ # NOTE: The target destination is a PubSub topic
+ destination = "pubsub.googleapis.com/projects/${var.project_id}/topics/${google_pubsub_topic.ingestion_topic.name}"
+ filter = var.ingestion_sink_filter
+
+ # Dynamic block to exclude logs from ingestion
+ dynamic "exclusions" {
+ for_each = var.exclude_logs_filter
+ content {
+ name = exclusions.value.name
+ description = exclusions.value.description
+ filter = exclusions.value.filter
+ disabled = exclusions.value.disabled
+ }
+ }
+
+ # NOTE: The include_children attribute is set to true in order to ingest data
+ # 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 = "${local.role_name}Org${local.suffix}"
+ title = "Sysdigcloud Ingestion Org 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}"
+}
+
+# adding ciem role with permissions to the service account for org
+resource "google_organization_iam_member" "identity_mgmt" {
+ for_each = var.is_organizational ? toset(["roles/recommender.viewer", "roles/iam.serviceAccountViewer", "roles/iam.organizationRoleViewer", "roles/container.clusterViewer", "roles/compute.viewer"]) : []
+
+ org_id = data.google_organization.org[0].org_id
+ role = each.key
+ member = "serviceAccount:${google_service_account.push_auth.email}"
+}
\ No newline at end of file
diff --git a/modules/integrations/pub-sub/outputs.tf b/modules/integrations/pub-sub/outputs.tf
new file mode 100644
index 0000000..a1f7b73
--- /dev/null
+++ b/modules/integrations/pub-sub/outputs.tf
@@ -0,0 +1,5 @@
+output "pubsub_datasource_component_id" {
+ value = "${sysdig_secure_cloud_auth_account_component.gcp_pubsub_datasource.type}/${sysdig_secure_cloud_auth_account_component.gcp_pubsub_datasource.instance}"
+ description = "Component identifier of Webhook Datasource integration created in Sysdig Backend for Log Ingestion"
+ depends_on = [sysdig_secure_cloud_auth_account_component.gcp_pubsub_datasource]
+}
\ No newline at end of file
diff --git a/modules/integrations/pub-sub/variables.tf b/modules/integrations/pub-sub/variables.tf
new file mode 100644
index 0000000..6cf397b
--- /dev/null
+++ b/modules/integrations/pub-sub/variables.tf
@@ -0,0 +1,103 @@
+variable "project_id" {
+ type = string
+ description = "(Required) Target Project identifier provided by the customer"
+}
+
+variable "labels" {
+ type = map(string)
+ description = "(Optional) Labels to be associated with Sysdig-originated resources"
+ default = {
+ originator = "sysdig"
+ }
+}
+
+variable "ack_deadline_seconds" {
+ type = number
+ description = "(Optional) Maximum time in seconds after Sysdig's subscriber receives a message before the subscriber should acknowledge the message"
+ default = 60
+}
+
+variable "message_retention_duration" {
+ type = string
+ description = "(Optional) How long unacknowledged messages are retained in Sysdig's subscription backlog, from the moment a message is published"
+ default = "604800s"
+}
+
+variable "max_delivery_attempts" {
+ type = number
+ description = "(Optional) Number of attempts redelivering missed messages from the deadletter topic to the main one"
+ default = 5
+}
+
+variable "minimum_backoff" {
+ type = string
+ description = "(Optional) Minimum backoff time for exponential backoff of the push subscription retry policy"
+ default = "10s"
+}
+
+variable "maximum_backoff" {
+ type = string
+ description = "(Optional) Maximum backoff time for exponential backoff of the push subscription retry policy"
+ default = "600s"
+}
+
+variable "is_organizational" {
+ description = "(Optional) Set this field to 'true' to deploy secure-for-cloud to a GCP Organization."
+ type = bool
+ default = false
+}
+
+variable "organization_domain" {
+ type = string
+ description = "(Optional) Organization domain. e.g. sysdig.com"
+ default = ""
+}
+
+variable "suffix" {
+ type = string
+ description = "Suffix to uniquely identify resources during multiple installs. If not provided, random value is autogenerated"
+ default = null
+}
+
+variable "audit_log_config" {
+ description = "List of services and their audit log configurations to be ingested. Default is to ingest all logs."
+ type = list(object({
+ service = string,
+ log_config = list(object({
+ log_type = string,
+ exempted_members = optional(list(string))
+ }))
+ }))
+ default = [
+ {
+ service = "allServices"
+ log_config = [
+ { log_type = "ADMIN_READ" },
+ { log_type = "DATA_READ" },
+ { log_type = "DATA_WRITE" }
+ ]
+ }
+ ]
+}
+
+variable "exclude_logs_filter" {
+ description = "Filter to exclude logs from ingestion. Default is to ingest all google.cloud.audit.AuditLog logs. with no exclusions."
+ type = list(object({
+ name = string,
+ description = optional(string),
+ filter = string,
+ disabled = optional(bool)
+ }))
+ default = []
+}
+
+variable "ingestion_sink_filter" {
+ type = string
+ description = "Filter the Logging Sink is set up with. Default is to ingests AuditLogs"
+ default = "protoPayload.@type = \"type.googleapis.com/google.cloud.audit.AuditLog\""
+}
+
+variable "sysdig_secure_account_id" {
+ type = string
+ description = "ID of the Sysdig Cloud Account to enable to enable Pub Sub integration for (incase of organization, ID of the Sysdig management account)"
+}
\ No newline at end of file
diff --git a/modules/integrations/pub-sub/versions.tf b/modules/integrations/pub-sub/versions.tf
new file mode 100644
index 0000000..adb6e1a
--- /dev/null
+++ b/modules/integrations/pub-sub/versions.tf
@@ -0,0 +1,18 @@
+terraform {
+ required_version = ">= 1.0.0"
+
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ version = ">= 4.21.0"
+ }
+ sysdig = {
+ source = "sysdiglabs/sysdig"
+ version = ">= 1.34.0"
+ }
+ random = {
+ source = "hashicorp/random"
+ version = ">= 3.1"
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/examples/modular_organization/pub-sub.tf b/test/examples/modular_organization/pub-sub.tf
new file mode 100644
index 0000000..7cbaad9
--- /dev/null
+++ b/test/examples/modular_organization/pub-sub.tf
@@ -0,0 +1,28 @@
+#---------------------------------------------------------------------------------------------
+# Ensure installation flow for foundational onboarding has been completed before
+# installing additional Sysdig features.
+#---------------------------------------------------------------------------------------------
+
+module "pub-sub" {
+ source = "../../../modules/integrations/pub-sub"
+ project_id = module.onboarding.project_id
+ is_organizational = module.onboarding.is_organizational
+ organization_domain = module.onboarding.organization_domain
+ sysdig_secure_account_id = module.onboarding.sysdig_secure_account_id
+}
+
+resource "sysdig_secure_cloud_auth_account_feature" "threat_detection" {
+ account_id = module.onboarding.sysdig_secure_account_id
+ type = "FEATURE_SECURE_THREAT_DETECTION"
+ enabled = true
+ components = [ module.pub-sub.pubsub_datasource_component_id ]
+ depends_on = [ module.pub-sub ]
+}
+
+resource "sysdig_secure_cloud_auth_account_feature" "identity_entitlement" {
+ account_id = module.onboarding.sysdig_secure_account_id
+ type = "FEATURE_SECURE_IDENTITY_ENTITLEMENT"
+ enabled = true
+ components = [module.pub-sub.pubsub_datasource_component_id]
+ depends_on = [sysdig_secure_cloud_auth_account_feature.config_posture, module.pub-sub]
+}
\ No newline at end of file
diff --git a/test/examples/modular_single_project/pub-sub.tf b/test/examples/modular_single_project/pub-sub.tf
new file mode 100644
index 0000000..dbe4c43
--- /dev/null
+++ b/test/examples/modular_single_project/pub-sub.tf
@@ -0,0 +1,26 @@
+#---------------------------------------------------------------------------------------------
+# Ensure installation flow for foundational onboarding has been completed before
+# installing additional Sysdig features.
+#---------------------------------------------------------------------------------------------
+
+module "pub-sub" {
+ source = "../../../modules/integrations/pub-sub"
+ project_id = module.onboarding.project_id
+ sysdig_secure_account_id = module.onboarding.sysdig_secure_account_id
+}
+
+resource "sysdig_secure_cloud_auth_account_feature" "threat_detection" {
+ account_id = module.onboarding.sysdig_secure_account_id
+ type = "FEATURE_SECURE_THREAT_DETECTION"
+ enabled = true
+ components = [ module.pub-sub.pubsub_datasource_component_id ]
+ depends_on = [ module.pub-sub ]
+}
+
+resource "sysdig_secure_cloud_auth_account_feature" "identity_entitlement" {
+ account_id = module.onboarding.sysdig_secure_account_id
+ type = "FEATURE_SECURE_IDENTITY_ENTITLEMENT"
+ enabled = true
+ components = [module.pub-sub.pubsub_datasource_component_id]
+ depends_on = [sysdig_secure_cloud_auth_account_feature.config_posture, module.pub-sub]
+}
\ No newline at end of file