Skip to content

Commit

Permalink
feat(vm): add vm modular support (#39)
Browse files Browse the repository at this point in the history
* feat(modular): add modular support for cdr/ciem

* feat(modular): add modular support for cdr/ciem

* feat(modular): add modular support for cdr/ciem

* feat(modular): add modular support for cdr/ciem

* feat(modular): add modular support for cdr/ciem

* feat(modular): address feedback for  modular support for cdr/ciem

* adding modular onboarding module

* fix var refns

* adding modular onboarding example

* adding config posture module for modular onboarding

* updating README

* fix role naming & version metadata

* updating examples for onboarding & cspm org

* cleanup foundational READMEs

* use external_id datasource

* update README

* remove mgmt_group_ids in cspm module

* bump sysdig provider version to be consistent & have latest datasources

* updating examples

* update example

* feat(modular): address feedback for  modular support for cdr/ciem, rebase and cleanup

* feat(modular): address feedback for  modular support for cdr/ciem, rebase and cleanup

* add explicit dependency

* feat(modular): address feedback for  modular support for cdr/ciem, rebase and cleanup

* feat(modular): address feedback for  modular support for cdr/ciem, rebase and cleanup

* feat(modular): address feedback for  modular support for cdr/ciem, rebase and cleanup

* feat(vm): add vm modular support

* feat(vm): add vm modular support

* feat(modular): add vm modular support

* feat(vm): add vm modular support

* feat(vm): add vm modular support

* delete some test files

* feat(vm): modular vm feedback

* feat(vm): modular vm feedback

---------

Co-authored-by: Haresh Suresh <[email protected]>
  • Loading branch information
jose-pablo-camacho and haresh-suresh authored Sep 17, 2024
1 parent e35f083 commit d8e8da8
Show file tree
Hide file tree
Showing 8 changed files with 447 additions and 0 deletions.
95 changes: 95 additions & 0 deletions modules/agentless-scan/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# GCP Agentless Scanning Module

This Module creates the resources required to scan hosts on Google Cloud Projects. 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 VM feature are listed below:
- Compute Engine API

- The following resources will be created in 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.

This module will also deploy a Service Principal Component in Sysdig Backend for onboarded Sysdig Cloud Account.

## Single Project Setup

![permission_diagram_single](./permissions_diagram_single.png)

## Organizational Setup

Set `is_organizatinal=true` together with the `organization_domain=<domain>`.
![permission_diagram_org](./permissions_diagram_org.png)

## Requirements

| Name | Version |
|---------------------------------------------------------------------------|-----------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | >= 4.21.0 |
| <a name="requirement_sysdig"></a> [sysdig](#requirement\_sysdig) | >= 1.34.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.1 |

## Providers

| Name | Version |
|---------------------------------------------------------------------------|-----------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | >= 4.21.0 |
| <a name="requirement_sysdig"></a> [sysdig](#requirement\_sysdig) | >= 1.34.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.1 |

## Modules

No modules.

## Resources

| Name | Type |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
| [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_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 |
| [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 |
| [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 |
| [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_agentless_scan](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/resources/secure_cloud_auth_account_component) | resource |

## Inputs

| Name | Description | Type | Default | Required |
|----------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------|-----------------------------|:--------:|
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | GCP Project ID | `string` | n/a | yes |
| <a name="input_is_organizational"></a> [is\_organizational](#input\_is\_organizational) | Optional. Determines whether module must scope whole organization. Otherwise single project will be scoped | `bool` | `false` | no |
| <a name="input_organization_domain"></a> [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 |
| <a name="input_sysdig_account_id"></a> [sysdig\_account\_id](#input\_sysdig\_account\_id) | Sysdig provided GCP Account designated for the host scan.<br/>One of `sysdig_backend` or `sysdig_account_id`must be provided | `string` | `null` | no |
| <a name="input_sysdig_secure_account_id"></a> [sysdig\_secure\_account\_id](#input\_sysdig\_secure\_account\_id) | ID of the Sysdig Cloud Account to enable Agentless Scanning integration for (in case of organization, ID of the Sysdig management account) | `string` | `null` | no |
| <a name="input_suffix"></a> [suffix](#input\_suffix) | Optional. Suffix word to enable multiple deployments with different naming<br/>(Workload Identity Pool and Providers have a soft deletion on Google Platform that will disallow name re-utilization)<br/>By default a random value will be autogenerated. | `string` | `null` | no |

## Outputs

| Name | Description |
|--------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| <a name="agentless_scan_component_id"></a> [agentless\_scan\_component\_id](#agentless\_scan\_component\_id) | Component identifier of Agentless Scan integration created in Sysdig Backend for VM |

## Authors

Module is maintained by [Sysdig](https://sysdig.com).

## License

Apache 2 Licensed. See LICENSE for full details.
208 changes: 208 additions & 0 deletions modules/agentless-scan/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#-----------------------------------------------------------------------------------------
# Fetch the data sources
#-----------------------------------------------------------------------------------------
data "sysdig_secure_agentless_scanning_assets" "assets" {}

#-----------------------------------------------------------------------------------------
# These locals indicate the suffix to create unique name for resources and permissions
#-----------------------------------------------------------------------------------------
locals {
suffix = var.suffix == null ? random_id.suffix[0].hex : var.suffix
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",
]
}

#-----------------------------------------------------------------------------------------------------------------------
# A random resource is used to generate unique Agentless Scan name suffix for resources.
# This prevents conflicts when recreating an Agentless Scan resources with the same name.
#-----------------------------------------------------------------------------------------------------------------------
resource "random_id" "suffix" {
count = var.suffix == null ? 1 : 0
byte_length = 3
}


resource "google_service_account" "controller" {
project = var.project_id
account_id = "sysdig-ahs-${local.suffix}"
display_name = "Sysdig Agentless Host Scanning"
}

#-----------------------------------------------------------------------------------------------------------------------
# Configure Workload Identity Federation for auth
# See https://cloud.google.com/iam/docs/access-resources-aws
#-----------------------------------------------------------------------------------------------------------------------

resource "google_iam_workload_identity_pool" "agentless" {
workload_identity_pool_id = "sysdig-ahs-${local.suffix}"
}

resource "google_iam_workload_identity_pool_provider" "agentless" {
count = data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id != null ? 1 : 0

lifecycle {
precondition {
condition = (data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id != 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==\"${data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id}\""

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 = data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id
}
}

resource "google_service_account_iam_member" "controller_custom" {
count = data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id != null ? 1 : 0

lifecycle {
precondition {
condition = (data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id != 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/${data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id}"
}

resource "google_iam_workload_identity_pool_provider" "agentless_gcp" {
count = var.sysdig_account_id != null ? 1 : 0

lifecycle {
precondition {
condition = (data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id == 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 = (data.sysdig_secure_agentless_scanning_assets.assets.backend.cloud_id == 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}"
}

#-----------------------------------------------------------------------------------------
# Custom IAM roles and bindings
#-----------------------------------------------------------------------------------------

resource "google_project_iam_custom_role" "controller_role" {
count = var.is_organizational ? 0 : 1

project = var.project_id
role_id = "SysdigCloudVMDiscovery${local.suffix}"
title = "SysdigCloudVM, for Host Discovery"
permissions = local.host_discovery_permissions
}

resource "google_project_iam_binding" "controller_binding" {
count = var.is_organizational ? 0 : 1

project = var.project_id
role = google_project_iam_custom_role.controller_role[0].id
members = [
"serviceAccount:${google_service_account.controller.email}",
]
}

resource "google_project_iam_custom_role" "worker_role" {
count = var.is_organizational ? 0 : 1

project = var.project_id
role_id = "SysdigCloudVMScan${local.suffix}"
title = "SysdigCloudVM, for Host Scan"
permissions = local.host_scan_permissions
}

resource "google_project_iam_binding" "admin_account_iam" {
count = var.is_organizational ? 0 : 1

project = var.project_id
role = google_project_iam_custom_role.worker_role[0].id
members = [
"serviceAccount:${data.sysdig_secure_agentless_scanning_assets.assets.gcp.worker_identity}",
]
}

#-----------------------------------------------------------------------------------------------------------------------------------------
# Call Sysdig Backend to add the agentless-scan 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_agentless_scan" {
account_id = var.sysdig_secure_account_id
type = "COMPONENT_SERVICE_PRINCIPAL"
instance = "secure-scanning"
version = "v0.1.0"
service_principal_metadata = jsonencode({
gcp = {
workload_identity_federation = {
pool_provider_id = data.sysdig_secure_agentless_scanning_assets.assets.gcp.worker_identity != 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
}
email = google_service_account.controller.email
}
})
}
Loading

0 comments on commit d8e8da8

Please sign in to comment.