diff --git a/modules/services/agentless-scan/README.md b/modules/services/agentless-scan/README.md new file mode 100644 index 0000000..d8a3a91 --- /dev/null +++ b/modules/services/agentless-scan/README.md @@ -0,0 +1,92 @@ +# GCP Agentless Scanning Module + + + + +This module will deploy required resources for Sysdig to be able to scan hosts on your Google Cloud Account. + + +The following resources will be created on each instrumented project: +- For the **Resource Discovery**: Enable Sysdig to authenticate through a Workload Identity Pool (requires provider, + service account, role, and related bindings) in order to be able to discover the VPC/Instance/Volumes +- For the **Host Data Extraction**: Enable Sysdig to create a disk copy on our SaaS platform, to be able to extract + the data required for security assessment. + +![permission-diagram.png](permission-diagram.png) + +Organizational support will be added later on. + +

+ +## Usage + +For usage instructions refer to Sysdig official documentation. +While on Controlled Availability check with your Sysdig representative. +

+ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >=1.0 | +| [google](#requirement\_google) | >= 4.1, < 5.0 | +| [random](#requirement\_random) | >= 3.1, < 4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | >= 4.1, < 5.0 | +| [random](#provider\_random) | >= 3.1, < 4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [google_iam_workload_identity_pool.agentless](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iam_workload_identity_pool) | resource | +| [google_iam_workload_identity_pool_provider.agentless](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iam_workload_identity_pool_provider) | resource | +| [google_iam_workload_identity_pool_provider.agentless_gcp](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iam_workload_identity_pool_provider) | resource | +| [google_project_iam_binding.admin-account-iam](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_binding) | resource | +| [google_project_iam_binding.controller_custom](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_binding) | resource | +| [google_project_iam_custom_role.controller](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_custom_role) | resource | +| [google_project_iam_custom_role.worker_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_iam_custom_role) | resource | +| [google_service_account.controller](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource | +| [google_service_account_iam_member.controller_custom](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_iam_member) | resource | +| [google_service_account_iam_member.controller_custom_gcp](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_iam_member) | resource | +| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | +| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [project\_id](#input\_project\_id) | GCP Project ID | `string` | n/a | yes | +| [worker\_identity](#input\_worker\_identity) | Sysdig provided Identity for the Service Account in charge of performing the host disk analysis | `string` | n/a | yes | +| [role\_name](#input\_role\_name) | Name for the Worker Role on the Customer infrastructure | `string` | `"SysdigAgentlessHostRole"` | no | +| [suffix](#input\_suffix) | By default a random value will be autogenerated.
Suffix word to enable multiple deployments with different naming
(Workload Identity Pool and Providers have a soft deletion on Google Platform that will disallow name re-utilization) | `string` | `null` | no | +| [sysdig\_account\_id](#input\_sysdig\_account\_id) | Sysdig provided GCP Account designated for the host scan.
One of `sysdig_backend` or `sysdig_account_id`must be provided | `string` | `null` | no | +| [sysdig\_backend](#input\_sysdig\_backend) | Sysdig provided AWS Account designated for the host scan.
One of `sysdig_backend` or `sysdig_account_id`must be provided | `string` | `null` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [controller\_service\_account](#output\_controller\_service\_account) | n/a | +| [json\_payload](#output\_json\_payload) | n/a | +| [project\_id](#output\_project\_id) | n/a | +| [project\_number](#output\_project\_number) | n/a | +| [workload\_identity\_pool\_provider](#output\_workload\_identity\_pool\_provider) | n/a | + + +## Authors + +Module is maintained by [Sysdig](https://sysdig.com). + +## License + +Apache 2 Licensed. See LICENSE for full details. \ No newline at end of file diff --git a/modules/services/agentless-scan/controller.tf b/modules/services/agentless-scan/controller.tf new file mode 100644 index 0000000..18e41ed --- /dev/null +++ b/modules/services/agentless-scan/controller.tf @@ -0,0 +1,125 @@ +resource "google_service_account" "controller" { + project = var.project_id + account_id = "sysdig-ahs-${local.suffix}" + display_name = "Sysdig Agentless Host Scanning" +} + +resource "google_project_iam_custom_role" "controller" { + project = var.project_id + role_id = "${var.role_name}Controller${title(local.suffix)}" + title = "Role for Sysdig Agentless Host Workers" + permissions = [ + # networks + "compute.networks.list", + "compute.networks.get", + # instances + "compute.instances.list", + "compute.instances.get", + # disks + "compute.disks.list", + "compute.disks.get", + # workload identity federation + "iam.serviceAccounts.getAccessToken", + ] +} + +resource "google_project_iam_binding" "controller_custom" { + project = var.project_id + role = google_project_iam_custom_role.controller.id + + members = [ + "serviceAccount:${google_service_account.controller.email}", + ] +} + +resource "google_iam_workload_identity_pool" "agentless" { + workload_identity_pool_id = "sysdig-ahs-${local.suffix}" +} + +resource "google_iam_workload_identity_pool_provider" "agentless" { + count = var.sysdig_backend != null ? 1 : 0 + + lifecycle { + precondition { + condition = (var.sysdig_backend != null && var.sysdig_account_id == null) + error_message = "Cannot provide both sysdig_backend or sysdig_account_id" + } + } + + workload_identity_pool_id = google_iam_workload_identity_pool.agentless.workload_identity_pool_id + workload_identity_pool_provider_id = "sysdig-ahs-${local.suffix}" + display_name = "Sysdig Agentless Controller" + description = "AWS identity pool provider for Sysdig Secure Agentless Host Scanning" + disabled = false + + attribute_condition = "attribute.aws_account==\"${var.sysdig_backend}\"" + + attribute_mapping = { + "google.subject" = "assertion.arn" + "attribute.aws_account" = "assertion.account" + "attribute.role" = "assertion.arn.extract(\"/assumed-role/{role}/\")" + "attribute.session" = "assertion.arn.extract(\"/assumed-role/{role_and_session}/\").extract(\"/{session}\")" + } + + aws { + account_id = var.sysdig_backend + } +} + +resource "google_service_account_iam_member" "controller_custom" { + count = var.sysdig_backend != null ? 1 : 0 + + lifecycle { + precondition { + condition = (var.sysdig_backend != null && var.sysdig_account_id == null) + error_message = "Cannot provide both sysdig_backend or sysdig_account_id" + } + } + + service_account_id = google_service_account.controller.name + role = "roles/iam.workloadIdentityUser" + member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.agentless.name}/attribute.aws_account/${var.sysdig_backend}" +} + +resource "google_iam_workload_identity_pool_provider" "agentless_gcp" { + count = var.sysdig_account_id != null ? 1 : 0 + + lifecycle { + precondition { + condition = (var.sysdig_backend == null && var.sysdig_account_id != null) + error_message = "Cannot provide both sysdig_backend or sysdig_account_id" + } + } + + workload_identity_pool_id = google_iam_workload_identity_pool.agentless.workload_identity_pool_id + workload_identity_pool_provider_id = "sysdig-ahs-${local.suffix}-gcp" + display_name = "Sysdig Agentless Controller" + description = "GCP identity pool provider for Sysdig Secure Agentless Host Scanning" + disabled = false + + attribute_condition = "google.subject == \"${var.sysdig_account_id}\"" + + attribute_mapping = { + "google.subject" = "assertion.sub" + "attribute.sa_id" = "assertion.sub" + } + + oidc { + issuer_uri = "https://accounts.google.com" + } +} + +resource "google_service_account_iam_member" "controller_custom_gcp" { + count = var.sysdig_account_id != null ? 1 : 0 + + lifecycle { + precondition { + condition = (var.sysdig_backend == null && var.sysdig_account_id != null) + error_message = "Cannot provide both sysdig_backend or sysdig_account_id" + } + } + + service_account_id = google_service_account.controller.name + role = "roles/iam.workloadIdentityUser" + member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.agentless.name}/attribute.sa_id/${var.sysdig_account_id}" +} diff --git a/modules/services/agentless-scan/data.tf b/modules/services/agentless-scan/data.tf new file mode 100644 index 0000000..c2d738c --- /dev/null +++ b/modules/services/agentless-scan/data.tf @@ -0,0 +1,3 @@ +data "google_project" "project" { + project_id = var.project_id +} \ No newline at end of file diff --git a/modules/services/agentless-scan/locals.tf b/modules/services/agentless-scan/locals.tf new file mode 100644 index 0000000..8a77ae2 --- /dev/null +++ b/modules/services/agentless-scan/locals.tf @@ -0,0 +1,9 @@ +locals { + suffix = var.suffix == null ? random_id.suffix[0].hex : var.suffix +} + + +resource "random_id" "suffix" { + count = var.suffix == null ? 1 : 0 + byte_length = 3 +} \ No newline at end of file diff --git a/modules/services/agentless-scan/outputs.tf b/modules/services/agentless-scan/outputs.tf new file mode 100644 index 0000000..f2e10fa --- /dev/null +++ b/modules/services/agentless-scan/outputs.tf @@ -0,0 +1,32 @@ +output "project_id" { + value = var.project_id +} + +output "project_number" { + value = data.google_project.project.number +} + +output "controller_service_account" { + value = google_service_account.controller.email +} + +output "workload_identity_pool_provider" { + value = var.sysdig_backend != null ? google_iam_workload_identity_pool_provider.agentless[0].name : var.sysdig_account_id != null ? google_iam_workload_identity_pool_provider.agentless_gcp[0].name : null + precondition { + condition = (var.sysdig_backend != null && var.sysdig_account_id == null) || (var.sysdig_backend == null && var.sysdig_account_id != null) + error_message = "Cannot provide both sysdig_backend or sysdig_account_id" + } +} + +output "json_payload" { + value = jsonencode({ + "projectId" = var.project_id + "projectNumber" = data.google_project.project.number + "serviceAccount" = google_service_account.controller.email + "identityProvider" = var.sysdig_backend != null ? google_iam_workload_identity_pool_provider.agentless[0].name : var.sysdig_account_id != null ? google_iam_workload_identity_pool_provider.agentless_gcp[0].name : null + }) + precondition { + condition = (var.sysdig_backend != null && var.sysdig_account_id == null) || (var.sysdig_backend == null && var.sysdig_account_id != null) + error_message = "Cannot provide both sysdig_backend or sysdig_account_id" + } +} diff --git a/modules/services/agentless-scan/permission-diagram.png b/modules/services/agentless-scan/permission-diagram.png new file mode 100644 index 0000000..a50c2e0 Binary files /dev/null and b/modules/services/agentless-scan/permission-diagram.png differ diff --git a/modules/services/agentless-scan/provider.tf b/modules/services/agentless-scan/provider.tf new file mode 100644 index 0000000..fba49cf --- /dev/null +++ b/modules/services/agentless-scan/provider.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">=1.0" + + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.1, < 5.0" + } + random = { + source = "hashicorp/random" + version = ">= 3.1, < 4.0" + } + } +} \ No newline at end of file diff --git a/modules/services/agentless-scan/variables.tf b/modules/services/agentless-scan/variables.tf new file mode 100644 index 0000000..7485be6 --- /dev/null +++ b/modules/services/agentless-scan/variables.tf @@ -0,0 +1,37 @@ +variable "project_id" { + type = string + description = "GCP Project ID" +} + +variable "worker_identity" { + type = string + description = "Sysdig provided Identity for the Service Account in charge of performing the host disk analysis" +} + +variable "sysdig_backend" { + type = string + description = "Sysdig provided AWS Account designated for the host scan.
One of `sysdig_backend` or `sysdig_account_id`must be provided" + default = null +} + +variable "sysdig_account_id" { + type = string + description = "Sysdig provided GCP Account designated for the host scan.
One of `sysdig_backend` or `sysdig_account_id`must be provided" + default = null +} + + +# optionals +variable "role_name" { + type = string + description = "Name for the Worker Role on the Customer infrastructure" + default = "SysdigAgentlessHostRole" +} + + + +variable "suffix" { + type = string + description = "By default a random value will be autogenerated.
Suffix word to enable multiple deployments with different naming
(Workload Identity Pool and Providers have a soft deletion on Google Platform that will disallow name re-utilization)" + default = null +} \ No newline at end of file diff --git a/modules/services/agentless-scan/worker.tf b/modules/services/agentless-scan/worker.tf new file mode 100644 index 0000000..c958b5d --- /dev/null +++ b/modules/services/agentless-scan/worker.tf @@ -0,0 +1,21 @@ +resource "google_project_iam_custom_role" "worker_role" { + project = var.project_id + role_id = "${var.role_name}Worker${title(local.suffix)}" + title = "${var.role_name} - Sysdig Agentless" + permissions = [ + # general stuff + "compute.zoneOperations.get", + # disks + "compute.disks.get", + "compute.disks.useReadOnly", + ] +} + +resource "google_project_iam_binding" "admin-account-iam" { + project = var.project_id + role = google_project_iam_custom_role.worker_role.id + + members = [ + "serviceAccount:${var.worker_identity}", + ] +} \ No newline at end of file