Skip to content

Commit

Permalink
Support for Modular Event Bridge for Log Ingestion (org) (#6)
Browse files Browse the repository at this point in the history
* Support for Modular Event Bridge for Log Ingestion (org)

Change summary:
----------------
- Added the respective tf file for Org onboarding case
- Added test example for event-bridge integration (org)
- Updated the self-managed stacksets during org onboarding
  to skip creation with delegated_admin conditional check
- Updated the README

Testing:
Validated the changes with org onboarding of actual OU with
mgmt account on AWS.

* Simplify stackset resource naming
  • Loading branch information
ravinadhruve10 authored Aug 20, 2024
1 parent 4859850 commit 11b0946
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 23 deletions.
16 changes: 10 additions & 6 deletions modules/integrations/event-bridge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ The following resources will be created in each instrumented account through Clo
- An `EventBridge Target` that sends these events to an EventBridge Bus is Sysdig's AWS Account
- An `IAM Role` and associated policies that gives the EventBridge Bus in the source account permission to call PutEvent on the EventBridge Bus in Sysdig's Account.

When run in Organizational mode, this module will be deployed via CloudFormation StackSets that should be created in the management account. They will create the above resources in each account in the organization,
and automatically in any member accounts that are later added to the organization. If a delegated admin account is used, only SERVICE_MANAGED stacksets will be created in the delegated admin account,
responsible for creating the above resources in each account in the organization.
When run in Organizational mode, this module will be deployed via CloudFormation StackSets that should be created in the management account. They will create the above resources in each account in the organization, and automatically in any member accounts that are later added to the organization. If a delegated admin account is used, only
SERVICE_MANAGED stacksets will be created in the delegated admin account, responsible for creating the above resources in each account in the organization.

This module will also deploy an Event Bridge Component in Sysdig Backend for onboarded Sysdig Cloud Account.

Expand Down Expand Up @@ -42,14 +41,19 @@ No modules.
| [aws_iam_role.event_bus_invoke_remote_event_bus](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.event_bus_stackset_admin_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.event_bus_stackset_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_cloudformation_stack_set.single-acc-stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource |
| [aws_cloudformation_stack_set_instance.single_acc_stackset_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource |
| [aws_cloudformation_stack_set.acc-stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource |
| [aws_cloudformation_stack_set_instance.acc_stackset_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource |
| [sysdig_secure_cloud_auth_account_component.aws_event_bridge](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/resources/secure_cloud_auth_account_component) | resource |
| [aws_cloudformation_stack_set.eb-rule-stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource |
| [aws_cloudformation_stack_set.eb-role-stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource |
| [aws_cloudformation_stack_set_instance.eb_rule_stackset_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource |
| [aws_cloudformation_stack_set_instance.eb_role_stackset_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource |
| [aws_iam_policy_document.cloud_trail_events](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [sysdig_secure_cloud_ingestion_assets.assets](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_cloud_ingestion_assets) | 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_tenant_external_id.external_id](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_tenant_external_id) | data source |
| [aws_organizations_organization.org](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source |

## Inputs

Expand All @@ -63,7 +67,7 @@ No modules.
| <a name="input_org_units"></a> [org\_units](#input\_org\_units) | (Optional) List of Organization Unit IDs in which to setup EventBridge. By default, EventBridge will be setup in all accounts within the Organization. This field is ignored if `is_organizational = false` | `set(string)` | `[]` | no |
| <a name="input_regions"></a> [regions](#input\_regions) | (Optional) List of regions in which to setup EventBridge. By default, current region is selected | `set(string)` | `[]` | no |
| <a name="input_rule_state"></a> [rule\_state](#input\_rule\_state) | State of the rule. When state is ENABLED, the rule is enabled for all events except those delivered by CloudTrail. To also enable the rule for events delivered by CloudTrail, set state to ENABLED\_WITH\_ALL\_CLOUDTRAIL\_MANAGEMENT\_EVENTS. | `string` | `"ENABLED_WITH_ALL_CLOUDTRAIL_MANAGEMENT_EVENTS"` | no |
| <a name="input_stackset_admin_role_arn"></a> [stackset\_admin\_role\_arn](#input\_stackset\_admin\_role\_arn) | (Optional) stackset admin role to run SELF\_MANAGED stackset | `string` | `""` | no |
| <a name="input_stackset_admin_role_arn"></a> [stackset\_admin\_role\_arn](#input\_stackset\_admin\_role\_arn) | (Optional) stackset admin role arn to run SELF\_MANAGED stackset | `string` | `""` | no |
| <a name="input_stackset_execution_role_name"></a> [stackset\_execution\_role\_name](#input\_stackset\_execution\_role\_name) | (Optional) stackset execution role name to run SELF\_MANAGED stackset | `string` | `""` | no |
| <a name="auto_create_stackset_roles"></a> [auto\_create\_stackset\_roles](#input\_auto\_create\_stackset\_roles) | Whether to auto create the custom stackset roles to run SELF_MANAGED stackset | `bool` | `true` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | (Optional) Tags to be attached to all Sysdig resources. | `map(string)` | <pre>{<br> "product": "sysdig-secure-for-cloud"<br>}</pre> | no |
Expand Down
47 changes: 30 additions & 17 deletions modules/integrations/event-bridge/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
# member account. (delegated admin account is a noop here for single installs)
#
# For organizational installs, resources in this file get created for management account only. (because service-managed stacksets do not
# include the management account they are create in, even if this account is within the target Organization).
# If a delegated admin account is used (determined via delegated_admin flag), resources will skip creation. This is because we don't want
# to create these stacksets if user provides a delegated admin account instead of management account.
# include the management account they are created in, even if this account is within the target Organization).
# If a delegated admin account is used instead (determined via delegated_admin flag), resources will skip creation. This is because we
# don't want to create these stacksets if user provides a delegated admin account instead of management account. (because service-managed
# stacksets include the delegated admin account already)
#-----------------------------------------------------------------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------------
Expand Down Expand Up @@ -56,9 +57,11 @@ resource "random_id" "suffix" {
#-----------------------------------------------------------------------------------------------------------------------------------------

resource "aws_iam_role" "event_bus_stackset_admin_role" {
count = !var.auto_create_stackset_roles ? 0 : 1
name = "AWSCloudFormationStackSetAdministrationRoleForEB"
tags = var.tags
# skip resource creation in org case if delegated_admin is used
count = (var.is_organizational && var.delegated_admin) || !var.auto_create_stackset_roles ? 0 : 1

name = "AWSCloudFormationStackSetAdministrationRoleForEB"
tags = var.tags

assume_role_policy = <<EOF
{
Expand Down Expand Up @@ -86,9 +89,11 @@ EOF
#-----------------------------------------------------------------------------------------------------------------------------------------

resource "aws_iam_role" "event_bus_stackset_execution_role" {
count = !var.auto_create_stackset_roles ? 0 : 1
name = "AWSCloudFormationStackSetExecutionRoleForEB"
tags = var.tags
# skip resource creation in org case if delegated_admin is used
count = (var.is_organizational && var.delegated_admin) || !var.auto_create_stackset_roles ? 0 : 1

name = "AWSCloudFormationStackSetExecutionRoleForEB"
tags = var.tags

assume_role_policy = <<EOF
{
Expand Down Expand Up @@ -120,8 +125,11 @@ EOF
#-----------------------------------------------------------------------------------------------------------------------------------------

resource "aws_iam_role" "event_bus_invoke_remote_event_bus" {
name = local.eb_resource_name
tags = var.tags
# skip resource creation in org case if delegated_admin is used
count = var.is_organizational && var.delegated_admin ? 0 : 1

name = local.eb_resource_name
tags = var.tags

assume_role_policy = <<EOF
{
Expand Down Expand Up @@ -199,8 +207,12 @@ data "aws_iam_policy_document" "cloud_trail_events" {
# Note: self-managed stacksets require pair of StackSetAdministrationRole & StackSetExecutionRole IAM roles with self-managed permissions
#-----------------------------------------------------------------------------------------------------------------------------------------

resource "aws_cloudformation_stack_set" "single-acc-stackset" {
name = join("-", [local.eb_resource_name, "EBRuleSingleAcc"])
resource "aws_cloudformation_stack_set" "primary-acc-stackset" {
# skip self managed stacksets in org case if delegated_admin is used
count = var.is_organizational && var.delegated_admin ? 0 : 1

# for single installs, primary account is the singleton account provided. for org installs, it is the mgmt account
name = join("-", [local.eb_resource_name, "EBRulePrimaryAcc"])
tags = var.tags
permission_model = "SELF_MANAGED"
capabilities = ["CAPABILITY_NAMED_IAM"]
Expand Down Expand Up @@ -229,11 +241,12 @@ resource "aws_cloudformation_stack_set" "single-acc-stackset" {
]
}

// stackset instance to deploy rule in all regions of single account
resource "aws_cloudformation_stack_set_instance" "single_acc_stackset_instance" {
for_each = local.region_set
// stackset instance to deploy rule in all regions of given account
resource "aws_cloudformation_stack_set_instance" "primary_acc_stackset_instance" {
# skip self managed stackset instances in org case if delegated_admin is used
for_each = var.is_organizational && var.delegated_admin ? toset([]) : local.region_set
region = each.key
stack_set_name = aws_cloudformation_stack_set.single-acc-stackset[0].name
stack_set_name = aws_cloudformation_stack_set.primary-acc-stackset[0].name

operation_preferences {
max_concurrent_percentage = 100
Expand Down
157 changes: 157 additions & 0 deletions modules/integrations/event-bridge/organizational.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#-----------------------------------------------------------------------------------------------------------------------
# These resources set up an EventBridge Rule and Target to forward all CloudTrail events from the source account to
# Sysdig in all accounts in an AWS Organization via service-managed CloudFormation StackSets.
# For a single account installation, see main.tf.
#
# If a delegated admin account is used (determined via delegated_admin flag), service-managed stacksets will be created
# acting as delegated_admin to deploy resources in all acocunts within AWS Organization.
#-----------------------------------------------------------------------------------------------------------------------

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)
}

# stackset to deploy eventbridge rule in organization unit
resource "aws_cloudformation_stack_set" "eb-rule-stackset" {
count = var.is_organizational ? 1 : 0

name = join("-", [local.eb_resource_name, "EBRuleOrg"])
tags = var.tags
permission_model = "SERVICE_MANAGED"
capabilities = ["CAPABILITY_NAMED_IAM"]

managed_execution {
active = true
}

auto_deployment {
enabled = true
retain_stacks_on_account_removal = false
}

lifecycle {
ignore_changes = [administration_role_arn]
}

call_as = var.delegated_admin ? "DELEGATED_ADMIN" : "SELF"

template_body = templatefile("${path.module}/stackset_template_body.tpl", {
name = local.eb_resource_name
event_pattern = var.event_pattern
rule_state = var.rule_state
target_event_bus_arn = data.sysdig_secure_cloud_ingestion_assets.assets.aws.eventBusARN
})
}

# stackset to deploy eventbridge role in organization unit
resource "aws_cloudformation_stack_set" "eb-role-stackset" {
count = var.is_organizational ? 1 : 0

name = join("-", [local.eb_resource_name, "EBRoleOrg"])
tags = var.tags
permission_model = "SERVICE_MANAGED"
capabilities = ["CAPABILITY_NAMED_IAM"]

managed_execution {
active = true
}

auto_deployment {
enabled = true
retain_stacks_on_account_removal = false
}

lifecycle {
ignore_changes = [administration_role_arn]
}

call_as = var.delegated_admin ? "DELEGATED_ADMIN" : "SELF"

template_body = <<TEMPLATE
Resources:
EventBridgeRole:
Type: AWS::IAM::Role
Properties:
RoleName: ${local.eb_resource_name}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: 'sts:AssumeRole'
- Effect: "Allow"
Principal:
AWS: "${data.sysdig_secure_trusted_cloud_identity.trusted_identity.identity}"
Action: "sts:AssumeRole"
Condition:
StringEquals:
sts:ExternalId: "${data.sysdig_secure_tenant_external_id.external_id.external_id}"
Policies:
- PolicyName: ${local.eb_resource_name}
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: 'events:PutEvents'
Resource: ${data.sysdig_secure_cloud_ingestion_assets.assets.aws.eventBusARN}
- Effect: Allow
Action:
- "events:DescribeRule"
- "events:ListTargetsByRule"
Resource: "arn:aws:events:*:*:rule/${local.eb_resource_name}"
TEMPLATE
}

// stackset instance to deploy rule in all organization units
resource "aws_cloudformation_stack_set_instance" "eb_rule_stackset_instance" {
for_each = var.is_organizational ? local.region_set : toset([])
region = each.key

stack_set_name = aws_cloudformation_stack_set.eb-rule-stackset[0].name
deployment_targets {
organizational_unit_ids = local.organizational_unit_ids
}
operation_preferences {
max_concurrent_percentage = 100
failure_tolerance_percentage = var.failure_tolerance_percentage
concurrency_mode = "SOFT_FAILURE_TOLERANCE"
region_concurrency_type = "PARALLEL"
}

call_as = var.delegated_admin ? "DELEGATED_ADMIN" : "SELF"

timeouts {
create = var.timeout
update = var.timeout
delete = var.timeout
}
}

// stackset instance to deploy role in all organization units
resource "aws_cloudformation_stack_set_instance" "eb_role_stackset_instance" {
count = var.is_organizational ? 1 : 0

stack_set_name = aws_cloudformation_stack_set.eb-role-stackset[0].name
deployment_targets {
organizational_unit_ids = local.organizational_unit_ids
}
operation_preferences {
max_concurrent_percentage = 100
failure_tolerance_percentage = var.failure_tolerance_percentage
concurrency_mode = "SOFT_FAILURE_TOLERANCE"
# Roles are not regional and hence do not need regional parallelism
}

call_as = var.delegated_admin ? "DELEGATED_ADMIN" : "SELF"

timeouts {
create = var.timeout
update = var.timeout
delete = var.timeout
}
}
28 changes: 28 additions & 0 deletions test/examples/organization/event_bridge.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#---------------------------------------------------------------------------------------------
# Ensure installation flow for foundational onboarding has been completed before
# installing additional Sysdig features.
#---------------------------------------------------------------------------------------------

module "event-bridge" {
source = "../../../modules/integrations/event-bridge"
regions = ["us-east-1", "us-west-1", "us-west-2"]
sysdig_secure_account_id = module.onboarding.sysdig_secure_account_id
is_organizational = module.onboarding.is_organizational
org_units = module.onboarding.organizational_unit_ids
}

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.event-bridge.event_bridge_component_id]
depends_on = [ module.event-bridge ]
}

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.config-posture.config_posture_component_id, module.event-bridge.event_bridge_component_id]
depends_on = [sysdig_secure_cloud_auth_account_feature.config_posture, sysdig_secure_cloud_auth_account_feature.threat_detection]
}

0 comments on commit 11b0946

Please sign in to comment.