From 4b749ec7ebc39862bbcbed5e29413497e1dab884 Mon Sep 17 00:00:00 2001 From: Mitul Sheth <97634367+mms2409@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:12:26 -0500 Subject: [PATCH 1/3] feat: workload scanning initial commit --- modules/services/workload-scanning/README.md | 66 +++++++++++++ modules/services/workload-scanning/main.tf | 96 +++++++++++++++++++ .../workload-scanning/organizational.tf | 93 ++++++++++++++++++ modules/services/workload-scanning/outputs.tf | 4 + .../services/workload-scanning/variables.tf | 47 +++++++++ .../services/workload-scanning/versions.tf | 9 ++ .../single/main.tf.OFF | 17 ++++ 7 files changed, 332 insertions(+) create mode 100644 modules/services/workload-scanning/README.md create mode 100644 modules/services/workload-scanning/main.tf create mode 100644 modules/services/workload-scanning/organizational.tf create mode 100644 modules/services/workload-scanning/outputs.tf create mode 100644 modules/services/workload-scanning/variables.tf create mode 100644 modules/services/workload-scanning/versions.tf create mode 100644 test/examples/secure_workload_scanning/single/main.tf.OFF diff --git a/modules/services/workload-scanning/README.md b/modules/services/workload-scanning/README.md new file mode 100644 index 0000000..e7e8807 --- /dev/null +++ b/modules/services/workload-scanning/README.md @@ -0,0 +1,66 @@ +# AWS Agentless Scanning Module + +This Module creates the resources required to perform agentless workload (ECR) scanning. + +The following resources will be created in each instrumented account: +- An IAM Role and associated policies that allows Sysdig to perform tasks necessary for agentless workload scanning, i.e. +pull images from ECR. + + +## Requirements + +| Name | Version | +|------|-----------| +| [terraform](#requirement\_terraform) | >= 1.2.0 | +| [aws](#requirement\_aws) | >= 4.39.0 | + +## Providers + +| Name | Version | +|------|---------| +| [aws](#provider\_aws) | >= 4.39.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------| +| [aws_cloudformation_stack_set.scanning_role_stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource | +| [aws_cloudformation_stack_set_registry.scanning_role_stackset_registry](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource | +| [aws_iam_policy.scanning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [aws_iam_policy_attachment.scanning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy_attachment) | resource | +| [aws_iam_role.scanning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | +| [aws_iam_policy_document.scanning](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.scanning_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_organizations_organization.org](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------|-------------------------------------------------------------|:--------:| +| [deploy\_global\_resources](#input\_deploy\_global\_resources) | (Optional) Set this field to 'true' to deploy Agentless Scanning when deploying to the main region (Non Organization Setup) | `bool` | `false` | no | +| [external\_id](#input\_external\_id) | Random string generated unique to a customer | `string` | n/a | yes | +| [role\_arn](#input\_role\_arn) | (Optional) The ARN of the role to be associated with the with regional resources. Must be set if deploy_global_resources is false | `string` | `""` | no | +| [is\_organizational](#input\_is\_organizational) | (Optional) Set this field to 'true' to deploy Agentless Workload Scanning to an AWS Organization (Or specific OUs) | `bool` | `false` | no | +| [name](#input\_name) | The name of the installation. Assigned to most child resource(s) | `string` | `"sysdig-workload-scanning"` | no | +| [org\_units](#input\_org\_units) | (Optional) List of Organization Unit IDs in which to setup Agentless Workload Scanning. By default, Agentless Workload Scanning will be setup in all accounts within the Organization. This field is ignored if `is_organizational = false` | `set(string)` | `[]` | no | +| [tags](#input\_tags) | sysdig secure-for-cloud tags. always include 'product' default tag for resource-group proper functioning | `map(string)` |
{| no | +| [trusted\_identity](#input\_trusted\_identity) | The name of sysdig trusted identity | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|---------------------------------------------------------------------| +| [role\_arn](#output\_role\_arn) | Role used by Sysdig Platform for Secure Agentless Workload Scanning | + + +## Authors + +Module is maintained by [Sysdig](https://sysdig.com). + +## License + +Apache 2 Licensed. See LICENSE for full details. diff --git a/modules/services/workload-scanning/main.tf b/modules/services/workload-scanning/main.tf new file mode 100644 index 0000000..06f9678 --- /dev/null +++ b/modules/services/workload-scanning/main.tf @@ -0,0 +1,96 @@ +########################################### +# Workload Controller IAM roles and stuff # +########################################### + +#----------------------------------------------------------------------------------------------------------------------- +# Determine if this is an Organizational install, or a single account install. For Single Account installs, resources +# are created directly using the AWS Terraform Provider (This is the default behaviour). For Organizational installs, +# see organizational.tf, and the resources in this file are used to instrument the management account (StackSets do not +# include the management account they are created in, even if this account is within the target Organization). +#----------------------------------------------------------------------------------------------------------------------- + +#----------------------------------------------------------------------------------------------------------------------- +# We have two types of resources. global and regional. Global resources are deployed only once (mostly in the primary +# region). We use deploy_global_resources boolean to determine that. +#----------------------------------------------------------------------------------------------------------------------- + +#----------------------------------------------------------------------------------------------------------------------- +# These resources create an Agentless Workload Scanning IAM Role and IAM Policy in the account. +#----------------------------------------------------------------------------------------------------------------------- + +data "aws_iam_policy_document" "scanning" { + count = (var.deploy_global_resources || var.is_organizational) ? 1 : 0 + + # General ECR read permission, necessary for the fetching artifacts. + statement { + sid = "EcrReadPermissions" + + effect = "Allow" + + actions = [ + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:GetRepositoryPolicy", + "ecr:DescribeRepositories", + "ecr:ListImages", + "ecr:DescribeImages", + "ecr:ListTagsForResource", + "ecr:GetAuthorizationToken", + ] + + resources = [ + "*", + ] + } +} + +resource "aws_iam_policy" "scanning" { + count = (var.deploy_global_resources || var.is_organizational) ? 1 : 0 + + name = var.name + description = "Grants Sysdig Secure access to volumes and snapshots" + policy = data.aws_iam_policy_document.scanning[0].json + tags = var.tags +} + +data "aws_iam_policy_document" "scanning_assume_role_policy" { + count = (var.deploy_global_resources || var.is_organizational) ? 1 : 0 + + statement { + sid = "SysdigWorkloadScanning" + + actions = [ + "sts:AssumeRole" + ] + + principals { + type = "AWS" + identifiers = [ + var.trusted_identity, + ] + } + + condition { + test = "StringEquals" + variable = "sts:ExternalId" + values = [var.external_id] + } + } +} + +resource "aws_iam_role" "scanning" { + count = (var.deploy_global_resources || var.is_organizational) ? 1 : 0 + + name = var.name + tags = var.tags + assume_role_policy = data.aws_iam_policy_document.scanning_assume_role_policy[0].json +} + +resource "aws_iam_policy_attachment" "scanning" { + count = (var.deploy_global_resources || var.is_organizational) ? 1 : 0 + + name = var.name + roles = [aws_iam_role.scanning[0].name] + policy_arn = aws_iam_policy.scanning[0].arn +} diff --git a/modules/services/workload-scanning/organizational.tf b/modules/services/workload-scanning/organizational.tf new file mode 100644 index 0000000..1f9a4bf --- /dev/null +++ b/modules/services/workload-scanning/organizational.tf @@ -0,0 +1,93 @@ +#----------------------------------------------------------------------------------------------------------------------- +# Determine if this is an Organizational install, or a single account install. For Organizational installs, resources +# are created using CloudFormation StackSet. For Single Account installs see main.tf. +#----------------------------------------------------------------------------------------------------------------------- + +data "aws_organizations_organization" "org" { + count = var.is_organizational ? 1 : 0 +} + +locals { + organizational_unit_ids = var.is_organizational && length(var.org_units) == 0 ? [for root in data.aws_organizations_organization.org[0].roots : root.id] : toset(var.org_units) +} + +#----------------------------------------------------------------------------------------------------------------------- +# The resources in this file set up an Agentless Workload Scanning IAM Role and Policies in all accounts +# in an AWS Organization via a CloudFormation StackSet. +# Global resources: IAM Role and Policy +#----------------------------------------------------------------------------------------------------------------------- + +#----------------------------------------------------------------------------------------------------------------------- +# stackset and stackset instance deployed in organization units for Agentless Scanning IAM Role, Policies +#----------------------------------------------------------------------------------------------------------------------- + +# stackset to deploy agentless workload scanning role in organization unit +resource "aws_cloudformation_stack_set" "scanning_role_stackset" { + count = var.is_organizational ? 1 : 0 + + name = join("-", [var.name, "ScanningRoleOrg"]) + tags = var.tags + permission_model = "SERVICE_MANAGED" + capabilities = ["CAPABILITY_NAMED_IAM"] + + auto_deployment { + enabled = true + retain_stacks_on_account_removal = false + } + + lifecycle { + ignore_changes = [administration_role_arn] + } + + template_body = < Date: Fri, 16 Feb 2024 10:13:16 -0500 Subject: [PATCH 2/3] fix: limit permissions --- modules/services/workload-scanning/main.tf | 6 +----- .../services/workload-scanning/organizational.tf | 4 ---- modules/services/workload-scanning/outputs.tf | 16 ++++++++++++++++ modules/services/workload-scanning/variables.tf | 5 +++-- modules/services/workload-scanning/versions.tf | 5 +++-- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/modules/services/workload-scanning/main.tf b/modules/services/workload-scanning/main.tf index 06f9678..983c165 100644 --- a/modules/services/workload-scanning/main.tf +++ b/modules/services/workload-scanning/main.tf @@ -31,11 +31,7 @@ data "aws_iam_policy_document" "scanning" { "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "ecr:BatchCheckLayerAvailability", - "ecr:GetRepositoryPolicy", - "ecr:DescribeRepositories", "ecr:ListImages", - "ecr:DescribeImages", - "ecr:ListTagsForResource", "ecr:GetAuthorizationToken", ] @@ -49,7 +45,7 @@ resource "aws_iam_policy" "scanning" { count = (var.deploy_global_resources || var.is_organizational) ? 1 : 0 name = var.name - description = "Grants Sysdig Secure access to volumes and snapshots" + description = "Grants Sysdig Secure access to ECR images" policy = data.aws_iam_policy_document.scanning[0].json tags = var.tags } diff --git a/modules/services/workload-scanning/organizational.tf b/modules/services/workload-scanning/organizational.tf index 1f9a4bf..f3ee675 100644 --- a/modules/services/workload-scanning/organizational.tf +++ b/modules/services/workload-scanning/organizational.tf @@ -67,11 +67,7 @@ Resources: - "ecr:GetDownloadUrlForLayer" - "ecr:BatchGetImage" - "ecr:BatchCheckLayerAvailability" - - "ecr:GetRepositoryPolicy" - - "ecr:DescribeRepositories" - "ecr:ListImages" - - "ecr:DescribeImages" - - "ecr:ListTagsForResource" - "ecr:GetAuthorizationToken" Resource: "*" diff --git a/modules/services/workload-scanning/outputs.tf b/modules/services/workload-scanning/outputs.tf index 75b028c..55d2cb8 100644 --- a/modules/services/workload-scanning/outputs.tf +++ b/modules/services/workload-scanning/outputs.tf @@ -2,3 +2,19 @@ output "role_arn" { description = "Role used by Sysdig Platform for Agentless Workload Scanning" value = var.is_organizational ? null : var.deploy_global_resources ? aws_iam_role.scanning[0].arn : var.role_arn } + +output "validate_deploy_global_resources" { + value = null + precondition { + condition = (var.deploy_global_resources && var.external_id != null) + error_message = "Please provide external_id or set deploy_global_resources to false." + } + precondition { + condition = (var.deploy_global_resources && var.role_arn != null) + error_message = "Please provide ecr_role_name or set deploy_global_resources set to false." + } + precondition { + condition = (var.deploy_global_resources && var.trusted_identity != null) + error_message = "Please provide trusted_identity or set deploy_global_resources to false." + } +} diff --git a/modules/services/workload-scanning/variables.tf b/modules/services/workload-scanning/variables.tf index d24b5c6..1271218 100644 --- a/modules/services/workload-scanning/variables.tf +++ b/modules/services/workload-scanning/variables.tf @@ -1,11 +1,12 @@ variable "external_id" { - description = "Random string generated unique to a customer" + description = "(Optional) This value should be provided by Sysdig. External ID is optional information that you can use in an IAM role trust policy to designate who in Sysdig can assume the role." type = string + default = null } variable "trusted_identity" { type = string - description = "The name of sysdig trusted identity" + description = "This value should be provided by Sysdig. The field refers to Sysdig's IAM role that will be authorized to pull ECR images" } variable "name" { diff --git a/modules/services/workload-scanning/versions.tf b/modules/services/workload-scanning/versions.tf index 00773b6..bd4ef84 100644 --- a/modules/services/workload-scanning/versions.tf +++ b/modules/services/workload-scanning/versions.tf @@ -1,9 +1,10 @@ terraform { - required_version = ">= 1.2.0" + required_version = "~> 1.7" + required_providers { aws = { source = "hashicorp/aws" - version = ">= 4.39.0" + version = "~> 5.0" } } } From 05bddae9f445f326639589e8c9ebc7826e8a2573 Mon Sep 17 00:00:00 2001 From: Mitul Sheth <97634367+mms2409@users.noreply.github.com> Date: Tue, 12 Mar 2024 08:42:50 -0400 Subject: [PATCH 3/3] Delete main.tf.Off file --- modules/services/workload-scanning/main.tf | 4 ++-- .../workload-scanning/organizational.tf | 2 +- .../secure_workload_scanning/single/main.tf.OFF | 17 ----------------- 3 files changed, 3 insertions(+), 20 deletions(-) delete mode 100644 test/examples/secure_workload_scanning/single/main.tf.OFF diff --git a/modules/services/workload-scanning/main.tf b/modules/services/workload-scanning/main.tf index 983c165..f4780b3 100644 --- a/modules/services/workload-scanning/main.tf +++ b/modules/services/workload-scanning/main.tf @@ -41,7 +41,7 @@ data "aws_iam_policy_document" "scanning" { } } -resource "aws_iam_policy" "scanning" { +resource "aws_iam_policy" "ecr_scanning" { count = (var.deploy_global_resources || var.is_organizational) ? 1 : 0 name = var.name @@ -88,5 +88,5 @@ resource "aws_iam_policy_attachment" "scanning" { name = var.name roles = [aws_iam_role.scanning[0].name] - policy_arn = aws_iam_policy.scanning[0].arn + policy_arn = aws_iam_policy.ecr_scanning[0].arn } diff --git a/modules/services/workload-scanning/organizational.tf b/modules/services/workload-scanning/organizational.tf index f3ee675..01efd34 100644 --- a/modules/services/workload-scanning/organizational.tf +++ b/modules/services/workload-scanning/organizational.tf @@ -41,7 +41,7 @@ resource "aws_cloudformation_stack_set" "scanning_role_stackset" { template_body = <
"product": "sysdig-secure-for-cloud"
}