diff --git a/.github/workflows/ci-pull-request.yaml b/.github/workflows/ci-pull-request.yaml
index 1639f72..9b5f78d 100644
--- a/.github/workflows/ci-pull-request.yaml
+++ b/.github/workflows/ci-pull-request.yaml
@@ -40,6 +40,7 @@ jobs:
- "secure_config_posture_identity_access/organization/main.tf"
- "secure_threat_detection/single/main.tf"
- "secure_threat_detection/organization/main.tf"
+ - "agentless-scan/organization/main.tf"
steps:
- name: Set up Go
uses: actions/setup-go@v2
diff --git a/Makefile b/Makefile
index 98b7924..946d1ac 100644
--- a/Makefile
+++ b/Makefile
@@ -18,8 +18,11 @@ deps: $(TFLINT)
lint: $(TFLINT)
$(MAKE) -C modules lint
+fmt-check: fmt
fmt:
terraform fmt -check -recursive modules
+fmt-fix:
+ terraform fmt -recursive modules
clean:
find -name ".terraform" -type d | xargs rm -rf
diff --git a/modules/services/agentless-scan/README.md b/modules/services/agentless-scan/README.md
index d8a3a91..efe23cf 100644
--- a/modules/services/agentless-scan/README.md
+++ b/modules/services/agentless-scan/README.md
@@ -12,9 +12,16 @@ The following resources will be created on each instrumented project:
- 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.
+## Single Project Setup
+![permission_diagram_single](./permissions_diagram_single.png)
+
+## Organizational Setup
+
+Set `is_organizatinal=true` together with the `organization_domain=YOUR_DOMAIN`
+
+![permission_diagram_org](./permissions_diagram_org.png)
+
@@ -37,8 +44,8 @@ While on Controlled Availability check with your Sysdig representative.
| Name | Version |
|------|---------|
-| [google](#provider\_google) | >= 4.1, < 5.0 |
-| [random](#provider\_random) | >= 3.1, < 4.0 |
+| [google](#provider\_google) | 4.84.0 |
+| [random](#provider\_random) | 3.6.0 |
## Modules
@@ -51,7 +58,11 @@ No modules.
| [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_organization_iam_binding.admin_account_iam](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/organization_iam_binding) | resource |
+| [google_organization_iam_binding.controller_custom](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/organization_iam_binding) | resource |
+| [google_organization_iam_custom_role.controller](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/organization_iam_custom_role) | resource |
+| [google_organization_iam_custom_role.worker_role](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/organization_iam_custom_role) | 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 |
@@ -59,18 +70,21 @@ No modules.
| [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_organization.org](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/organization) | data source |
| [google_project.project](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/project) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
+| [is\_organizational](#input\_is\_organizational) | Optional. Determines whether module must scope whole organization. Otherwise single project will be scoped | `bool` | `false` | no |
+| [organization\_domain](#input\_organization\_domain) | Optional. If `is_organizational=true` is set, its mandatory to specify this value, with the GCP Organization domain. e.g. sysdig.com | `string` | `null` | no |
| [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 |
+| [role\_name](#input\_role\_name) | Optional. Name for the Worker Role on the Customer infrastructure | `string` | `"SysdigAgentlessHostRole"` | no |
+| [suffix](#input\_suffix) | Optional. 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)
By default a random value will be autogenerated. | `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 |
+| [worker\_identity](#input\_worker\_identity) | Sysdig provided Identity for the Service Account in charge of performing the host disk analysis | `string` | n/a | yes |
## Outputs
diff --git a/modules/services/agentless-scan/controller.tf b/modules/services/agentless-scan/controller.tf
index 18e41ed..492ec65 100644
--- a/modules/services/agentless-scan/controller.tf
+++ b/modules/services/agentless-scan/controller.tf
@@ -4,34 +4,6 @@ resource "google_service_account" "controller" {
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}"
}
diff --git a/modules/services/agentless-scan/controller_org.tf b/modules/services/agentless-scan/controller_org.tf
new file mode 100644
index 0000000..e8c6336
--- /dev/null
+++ b/modules/services/agentless-scan/controller_org.tf
@@ -0,0 +1,18 @@
+resource "google_organization_iam_custom_role" "controller" {
+ count = local.is_organizational ? 1 : 0
+
+ org_id = data.google_organization.org[0].org_id
+ role_id = "${var.role_name}Discovery${title(local.suffix)}"
+ title = "${var.role_name}, for Host Discovery"
+ permissions = local.host_discovery_permissions
+}
+
+resource "google_organization_iam_binding" "controller_custom" {
+ count = local.is_organizational ? 1 : 0
+
+ org_id = data.google_organization.org[0].org_id
+ role = google_organization_iam_custom_role.controller[0].id
+ members = [
+ "serviceAccount:${google_service_account.controller.email}",
+ ]
+}
\ No newline at end of file
diff --git a/modules/services/agentless-scan/controller_single.tf b/modules/services/agentless-scan/controller_single.tf
new file mode 100644
index 0000000..b91eb16
--- /dev/null
+++ b/modules/services/agentless-scan/controller_single.tf
@@ -0,0 +1,18 @@
+resource "google_project_iam_custom_role" "controller" {
+ count = local.is_organizational ? 0 : 1
+
+ project = var.project_id
+ role_id = "${var.role_name}Discovery${title(local.suffix)}"
+ title = "${var.role_name}, for Host Discovery"
+ permissions = local.host_discovery_permissions
+}
+
+resource "google_project_iam_binding" "controller_custom" {
+ count = local.is_organizational ? 0 : 1
+
+ project = var.project_id
+ role = google_project_iam_custom_role.controller[0].id
+ members = [
+ "serviceAccount:${google_service_account.controller.email}",
+ ]
+}
\ No newline at end of file
diff --git a/modules/services/agentless-scan/data.tf b/modules/services/agentless-scan/data.tf
index c2d738c..de45ac7 100644
--- a/modules/services/agentless-scan/data.tf
+++ b/modules/services/agentless-scan/data.tf
@@ -1,3 +1,8 @@
data "google_project" "project" {
project_id = var.project_id
+}
+
+data "google_organization" "org" {
+ count = local.is_organizational ? 1 : 0
+ domain = var.organization_domain
}
\ No newline at end of file
diff --git a/modules/services/agentless-scan/locals.tf b/modules/services/agentless-scan/locals.tf
index 8a77ae2..366172b 100644
--- a/modules/services/agentless-scan/locals.tf
+++ b/modules/services/agentless-scan/locals.tf
@@ -1,5 +1,29 @@
locals {
suffix = var.suffix == null ? random_id.suffix[0].hex : var.suffix
+
+ is_organizational = var.is_organizational && var.organization_domain != null ? true : false
+
+ host_discovery_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",
+ ]
+
+ host_scan_permissions = [
+ # general stuff
+ "compute.zoneOperations.get",
+ # disks
+ "compute.disks.get",
+ "compute.disks.useReadOnly",
+ ]
}
diff --git a/modules/services/agentless-scan/permission-diagram.png b/modules/services/agentless-scan/permission-diagram.png
deleted file mode 100644
index a50c2e0..0000000
Binary files a/modules/services/agentless-scan/permission-diagram.png and /dev/null differ
diff --git a/modules/services/agentless-scan/permissions_diagram_org.png b/modules/services/agentless-scan/permissions_diagram_org.png
new file mode 100644
index 0000000..75a6bfb
Binary files /dev/null and b/modules/services/agentless-scan/permissions_diagram_org.png differ
diff --git a/modules/services/agentless-scan/permissions_diagram_single.png b/modules/services/agentless-scan/permissions_diagram_single.png
new file mode 100644
index 0000000..1f3d87f
Binary files /dev/null and b/modules/services/agentless-scan/permissions_diagram_single.png differ
diff --git a/modules/services/agentless-scan/variables.tf b/modules/services/agentless-scan/variables.tf
index 7485be6..081bfc6 100644
--- a/modules/services/agentless-scan/variables.tf
+++ b/modules/services/agentless-scan/variables.tf
@@ -1,3 +1,4 @@
+# mandatory
variable "project_id" {
type = string
description = "GCP Project ID"
@@ -20,18 +21,28 @@ variable "sysdig_account_id" {
default = null
}
-
-# optionals
+# optional
variable "role_name" {
type = string
- description = "Name for the Worker Role on the Customer infrastructure"
- default = "SysdigAgentlessHostRole"
+ description = "Optional. Name for Sysdig operations on discovery and scan"
+ default = "SysdigCloudVM"
}
-
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)"
+ description = "Optional. 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)
By default a random value will be autogenerated."
default = null
-}
\ No newline at end of file
+}
+
+variable "is_organizational" {
+ description = "Optional. Determines whether module must scope whole organization. Otherwise single project will be scoped"
+ type = bool
+ default = false
+}
+
+variable "organization_domain" {
+ type = string
+ description = "Optional. If `is_organizational=true` is set, its mandatory to specify this value, with the GCP Organization domain. e.g. sysdig.com"
+ default = null
+}
diff --git a/modules/services/agentless-scan/worker.tf b/modules/services/agentless-scan/worker.tf
deleted file mode 100644
index c958b5d..0000000
--- a/modules/services/agentless-scan/worker.tf
+++ /dev/null
@@ -1,21 +0,0 @@
-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
diff --git a/modules/services/agentless-scan/worker_org.tf b/modules/services/agentless-scan/worker_org.tf
new file mode 100644
index 0000000..0ff5351
--- /dev/null
+++ b/modules/services/agentless-scan/worker_org.tf
@@ -0,0 +1,18 @@
+resource "google_organization_iam_custom_role" "worker_role" {
+ count = local.is_organizational ? 1 : 0
+
+ org_id = data.google_organization.org[0].org_id
+ role_id = "${var.role_name}Scan${title(local.suffix)}"
+ title = "${var.role_name}, for Host Scan"
+ permissions = local.host_scan_permissions
+}
+
+resource "google_organization_iam_binding" "admin_account_iam" {
+ count = local.is_organizational ? 1 : 0
+
+ org_id = data.google_organization.org[0].org_id
+ role = google_organization_iam_custom_role.worker_role[0].id
+ members = [
+ "serviceAccount:${var.worker_identity}",
+ ]
+}
diff --git a/modules/services/agentless-scan/worker_single.tf b/modules/services/agentless-scan/worker_single.tf
new file mode 100644
index 0000000..5b2ffd0
--- /dev/null
+++ b/modules/services/agentless-scan/worker_single.tf
@@ -0,0 +1,18 @@
+resource "google_project_iam_custom_role" "worker_role" {
+ count = local.is_organizational ? 0 : 1
+
+ project = var.project_id
+ role_id = "${var.role_name}Scan${title(local.suffix)}"
+ title = "${var.role_name}, for Host Scan"
+ permissions = local.host_scan_permissions
+}
+
+resource "google_project_iam_binding" "admin_account_iam" {
+ count = local.is_organizational ? 0 : 1
+
+ project = var.project_id
+ role = google_project_iam_custom_role.worker_role[0].id
+ members = [
+ "serviceAccount:${var.worker_identity}",
+ ]
+}
diff --git a/test/examples/agentless-scan/README b/test/examples/agentless-scan/README
new file mode 100644
index 0000000..30a151a
--- /dev/null
+++ b/test/examples/agentless-scan/README
@@ -0,0 +1,6 @@
+
+note;
+
+- we test the cloud-scan module together with its dependencies on the minimal use-case
+ - cspm; for discovery + organizational setup (`secure-onboarding` component)
+ - sysdig provider `sysdig_secure_cloud_auth_account`; for authentication
\ No newline at end of file
diff --git a/test/examples/agentless-scan/organization/deps_scanning_org.tf b/test/examples/agentless-scan/organization/deps_scanning_org.tf
new file mode 100644
index 0000000..b7419cc
--- /dev/null
+++ b/test/examples/agentless-scan/organization/deps_scanning_org.tf
@@ -0,0 +1,9 @@
+# this is required for organizational setup (+cloud-host vm)
+
+module "organization-posture" {
+ source = "sysdiglabs/secure/google//modules/services/service-principal"
+ project_id = "org-child-project-1"
+ service_account_name = "sysdig-secure-igm6"
+ is_organizational = true
+ organization_domain = "draios.com"
+}
\ No newline at end of file
diff --git a/test/examples/agentless-scan/organization/main.tf b/test/examples/agentless-scan/organization/main.tf
new file mode 100644
index 0000000..a580f46
--- /dev/null
+++ b/test/examples/agentless-scan/organization/main.tf
@@ -0,0 +1,14 @@
+provider "google"{
+ project="mytestproject"
+}
+
+
+module "cloud_host" {
+ source = "../../../..//modules/services/agentless-scan"
+ project_id = "mytestproject"
+ sysdig_account_id = "012345678"
+ worker_identity = "foo@bar.com"
+
+ is_organizational = true
+ organization_domain = "myorg.com"
+}
\ No newline at end of file
diff --git a/test/examples/agentless-scan/organization/provider.tf b/test/examples/agentless-scan/organization/provider.tf
new file mode 100644
index 0000000..951e8f0
--- /dev/null
+++ b/test/examples/agentless-scan/organization/provider.tf
@@ -0,0 +1,18 @@
+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"
+ }
+ sysdig = {
+ source = "sysdiglabs/sysdig"
+ version = ">= 1.23.1"
+ }
+ }
+}
diff --git a/test/examples/agentless-scan/organization/sysdig_provider.tf b/test/examples/agentless-scan/organization/sysdig_provider.tf
new file mode 100644
index 0000000..be3ecd9
--- /dev/null
+++ b/test/examples/agentless-scan/organization/sysdig_provider.tf
@@ -0,0 +1,49 @@
+provider "sysdig" {
+ sysdig_secure_url = "https://secure-staging.sysdig.com"
+ sysdig_secure_api_token = "12124235"
+}
+
+resource "sysdig_secure_cloud_auth_account" "gcp_project" {
+ enabled = true
+ provider_id = "org-child-project-1"
+ provider_type = "PROVIDER_GCP"
+
+ feature {
+ secure_agentless_scanning {
+ enabled = true
+ components = ["COMPONENT_SERVICE_PRINCIPAL/secure-scanning"]
+ }
+ }
+
+
+ component {
+ type = "COMPONENT_SERVICE_PRINCIPAL"
+ instance = "secure-onboarding"
+ service_principal_metadata = jsonencode({
+ gcp = {
+ key = module.organization-posture.service_account_key
+ }
+ })
+ }
+
+
+ component {
+ type = "COMPONENT_SERVICE_PRINCIPAL"
+ instance = "secure-scanning"
+ service_principal_metadata = jsonencode({
+ gcp = {
+ workload_identity_federation = {
+ pool_provider_id = module.cloud_host.workload_identity_pool_provider
+ }
+ email = module.cloud_host.controller_service_account
+ }
+ })
+ }
+
+ depends_on = [module.cloud_host, module.organization-posture]
+}
+
+resource "sysdig_secure_organization" "gcp_organization_myproject" {
+ management_account_id = sysdig_secure_cloud_auth_account.gcp_project.id
+ depends_on = [module.organization-posture]
+}
\ No newline at end of file