Skip to content

Commit 1aea634

Browse files
authored
fix(event-bridge): restore legacy event bridge for gov cloud support (SSPROD-58545) (#78)
* add legacy event bridge for gov cloud support * newlines * rename package * remove is_gov_cloud_onboarding cause it's always gov
1 parent 323380c commit 1aea634

File tree

8 files changed

+891
-0
lines changed

8 files changed

+891
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# AWS Event Bridge Module
2+
3+
This Module creates the resources required to send CloudTrail logs to Sysdig via AWS EventBridge for Log Ingestion. These resources enable Threat Detection in the given single account, or AWS Organization.
4+
5+
The following resources will be created in each instrumented account through CloudFormation StackSet in provided regions:
6+
- An `EventBridge Rule` that captures all CloudTrail events from the defaul EventBridge Bus
7+
- An `EventBridge Target` that sends these events to an EventBridge Bus is Sysdig's AWS Account
8+
- 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.
9+
10+
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.
11+
12+
This module will also deploy an Event Bridge Component in Sysdig Backend for onboarded Sysdig Cloud Account.
13+
14+
If instrumenting an AWS Gov account/organization, IAM policies and event bridge resources will be created in `aws-us-gov` region.
15+
16+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
17+
## Requirements
18+
19+
| Name | Version |
20+
|------|---------|
21+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
22+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.60.0 |
23+
| <a name="requirement_random"></a> [random](#requirement\_random) | >= 3.1 |
24+
| <a name="requirement_sysdig"></a> [sysdig](#requirement\_sysdig) | ~> 1.48 |
25+
26+
## Providers
27+
28+
| Name | Version |
29+
|------|---------|
30+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 5.60.0 |
31+
| <a name="provider_random"></a> [random](#provider\_random) | >= 3.1 |
32+
| <a name="provider_sysdig"></a> [sysdig](#provider\_sysdig) | ~> 1.48 |
33+
34+
## Modules
35+
36+
No modules.
37+
38+
## Resources
39+
40+
| Name | Type |
41+
|------|------|
42+
| [aws_cloudformation_stack_set.eb-role-stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource |
43+
| [aws_cloudformation_stack_set.eb-rule-stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource |
44+
| [aws_cloudformation_stack_set.primary-acc-stackset](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set) | resource |
45+
| [aws_cloudformation_stack_set_instance.eb_role_stackset_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource |
46+
| [aws_cloudformation_stack_set_instance.eb_rule_stackset_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource |
47+
| [aws_cloudformation_stack_set_instance.primary_acc_stackset_instance](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudformation_stack_set_instance) | resource |
48+
| [aws_iam_role.event_bus_invoke_remote_event_bus](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
49+
| [aws_iam_role.event_bus_stackset_admin_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
50+
| [aws_iam_role.event_bus_stackset_execution_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
51+
| [aws_iam_role_policy.event_bus_invoke_remote_event_bus_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
52+
| [aws_iam_role_policy_attachments_exclusive.event_bus_stackset_admin_role_managed_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachments_exclusive) | resource |
53+
| [aws_iam_role_policy_attachments_exclusive.event_bus_stackset_execution_role_managed_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachments_exclusive) | resource |
54+
| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource |
55+
| [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 |
56+
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
57+
| [aws_organizations_organization.org](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source |
58+
| [aws_organizations_organizational_unit_descendant_accounts.ou_accounts_to_exclude](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organizational_unit_descendant_accounts) | data source |
59+
| [sysdig_secure_cloud_ingestion_assets.assets](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_cloud_ingestion_assets) | data source |
60+
| [sysdig_secure_tenant_external_id.external_id](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_tenant_external_id) | data source |
61+
| [sysdig_secure_trusted_cloud_identity.trusted_identity](https://registry.terraform.io/providers/sysdiglabs/sysdig/latest/docs/data-sources/secure_trusted_cloud_identity) | data source |
62+
63+
## Inputs
64+
65+
| Name | Description | Type | Default | Required |
66+
|------|-------------|------|---------|:--------:|
67+
| <a name="input_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. Default is true | `bool` | `true` | no |
68+
| <a name="input_event_pattern"></a> [event\_pattern](#input\_event\_pattern) | Event pattern for CloudWatch Event Rule | `string` | `"{\n \"detail-type\": [\n \"AWS API Call via CloudTrail\",\n \"AWS Console Sign In via CloudTrail\",\n \"AWS Service Event via CloudTrail\",\n \"Object Access Tier Changed\",\n \"Object ACL Updated\",\n \"Object Created\",\n \"Object Deleted\",\n \"Object Restore Completed\",\n \"Object Restore Expired\",\n \"Object Restore Initiated\",\n \"Object Storage Class Changed\",\n \"Object Tags Added\",\n \"Object Tags Deleted\",\n \"GuardDuty Finding\"\n ]\n}\n"` | no |
69+
| <a name="input_exclude_accounts"></a> [exclude\_accounts](#input\_exclude\_accounts) | (Optional) accounts to exclude for organization | `set(string)` | `[]` | no |
70+
| <a name="input_exclude_ouids"></a> [exclude\_ouids](#input\_exclude\_ouids) | (Optional) ouids to exclude for organization | `set(string)` | `[]` | no |
71+
| <a name="input_failure_tolerance_percentage"></a> [failure\_tolerance\_percentage](#input\_failure\_tolerance\_percentage) | The percentage of accounts, per Region, for which stack operations can fail before AWS CloudFormation stops the operation in that Region | `number` | `90` | no |
72+
| <a name="input_include_accounts"></a> [include\_accounts](#input\_include\_accounts) | (Optional) accounts to include for organization | `set(string)` | `[]` | no |
73+
| <a name="input_include_ouids"></a> [include\_ouids](#input\_include\_ouids) | (Optional) ouids to include for organization | `set(string)` | `[]` | no |
74+
| <a name="input_is_gov_cloud_onboarding"></a> [is\_gov\_cloud\_onboarding](#input\_is\_gov\_cloud\_onboarding) | true/false whether EventBridge should be deployed in a govcloud account/org or not | `bool` | `false` | no |
75+
| <a name="input_is_organizational"></a> [is\_organizational](#input\_is\_organizational) | (Optional) Set this field to 'true' to deploy EventBridge to an AWS Organization (Or specific OUs) | `bool` | `false` | no |
76+
| <a name="input_mgt_stackset"></a> [mgt\_stackset](#input\_mgt\_stackset) | (Optional) Indicates if the management stackset should be deployed | `bool` | `true` | no |
77+
| <a name="input_name"></a> [name](#input\_name) | (Optional) Name to be assigned to all child resources. A suffix may be added internally when required. Use default value unless you need to install multiple instances | `string` | `"sysdig-secure-events"` | no |
78+
| <a name="input_org_units"></a> [org\_units](#input\_org\_units) | TO BE DEPRECATED: Please work with Sysdig to migrate to using `include_ouids` instead.<br>When set, list of Organization Unit IDs in which to setup EventBridge. By default, EventBridge will be setup in all accounts within the Organization." | `set(string)` | `[]` | no |
79+
| <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 |
80+
| <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 |
81+
| <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 |
82+
| <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 |
83+
| <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 Event Bridge integration for (incase of organization, ID of the Sysdig management account) | `string` | n/a | yes |
84+
| <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 |
85+
| <a name="input_timeout"></a> [timeout](#input\_timeout) | Default timeout values for create, update, and delete operations | `string` | `"30m"` | no |
86+
87+
## Outputs
88+
89+
| Name | Description |
90+
|------|-------------|
91+
| <a name="output_event_bridge_component_id"></a> [event\_bridge\_component\_id](#output\_event\_bridge\_component\_id) | Component identifier of Event Bridge integration created in Sysdig Backend for Log Ingestion |
92+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
93+
94+
## Authors
95+
96+
Module is maintained by [Sysdig](https://sysdig.com).
97+
98+
## License
99+
100+
Apache 2 Licensed. See LICENSE for full details.
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#----------------------------------------------------------
2+
# Fetch & compute required data for organizational install
3+
#----------------------------------------------------------
4+
5+
data "aws_organizations_organization" "org" {
6+
count = var.is_organizational ? 1 : 0
7+
}
8+
9+
locals {
10+
# check if both old and new org parameters are provided, we fail early
11+
both_org_configuration_params = var.is_organizational && length(var.org_units) > 0 && (
12+
length(var.include_ouids) > 0 ||
13+
length(var.exclude_ouids) > 0 ||
14+
length(var.include_accounts) > 0 ||
15+
length(var.exclude_accounts) > 0
16+
)
17+
18+
# check if old org_units parameter is provided, for backwards compatibility we will always give preference to it
19+
check_old_ouid_param = var.is_organizational && length(var.org_units) > 0
20+
21+
# fetch the AWS Root OU under org
22+
# As per https://docs.aws.amazon.com/organizations/latest/userguide/orgs_getting-started_concepts.html#organization-structure, there can be only one root
23+
root_org_unit = var.is_organizational ? [for root in data.aws_organizations_organization.org[0].roots : root.id] : []
24+
}
25+
26+
check "validate_org_configuration_params" {
27+
assert {
28+
condition = length(var.org_units) == 0 # if this condition is false we throw warning
29+
error_message = <<-EOT
30+
WARNING: TO BE DEPRECATED 'org_units': Please work with Sysdig to migrate your Terraform installs to use 'include_ouids' instead.
31+
EOT
32+
}
33+
34+
assert {
35+
condition = !local.both_org_configuration_params # if this condition is false we throw error
36+
error_message = <<-EOT
37+
ERROR: If both org_units and include_ouids/exclude_ouids/include_accounts/exclude_accounts variables are populated,
38+
ONLY org_units will be considered. Please use only one of the two methods.
39+
40+
Note: org_units is going to be DEPRECATED soon, please work with Sysdig to migrate your Terraform installs.
41+
EOT
42+
}
43+
}
44+
45+
# *****************************************************************************************************************************************************
46+
# INCLUDE/EXCLUDE CONFIGURATION SUPPORT
47+
#
48+
# 1. Inclusions will always be handled for TF cloud provisioning.
49+
# NOTE:
50+
# Till AWS issue with UNION filter (https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-cloudformation/issues/100)
51+
# is fixed, we can't deploy using UNION filters for inclusions. As a workaround to ensure we don't skip any accounts, we deploy it to entire org.
52+
#
53+
# 2. We handle exclusions when only exclusion parameters are provided i.e out of all 4 configuration inputs,
54+
# a. only exclude_ouids are provided, OR
55+
# b. only exclude_accounts are provided, OR
56+
# c. only exclude_ouids AND exclude_accounts are provided
57+
# Else we ignore exclusions during cloud resource provisioning through TF. This is because AWS does not allow both operations - to include some
58+
# accounts and to exclude some. Hence, we will always prioritize include over exclude.
59+
#
60+
# 3. Sysdig however will honor all combinations of configuration inputs exactly as desired.
61+
# *****************************************************************************************************************************************************
62+
63+
#------------------------------------------------------------
64+
# Manage configurations to determine OU targets to deploy in
65+
#------------------------------------------------------------
66+
67+
locals {
68+
# OU CONFIGURATION (determine user provided org configuration)
69+
org_configuration = (
70+
# case1 - if old method is used where ONLY org_units is provided, use those
71+
local.check_old_ouid_param ? (
72+
"old_ouid_param"
73+
) : (
74+
# case2 - if no include/exclude ous provided, include entire org
75+
var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) == 0 ? (
76+
"entire_org"
77+
) : (
78+
# case3 - if only included ouids provided, include those ous only
79+
var.is_organizational && length(var.include_ouids) > 0 && length(var.exclude_ouids) == 0 ? (
80+
"included_ous_only"
81+
) : (
82+
# case4 - if only excluded ouids provided, exclude their accounts from rest of org
83+
var.is_organizational && length(var.include_ouids) == 0 && length(var.exclude_ouids) > 0 ? (
84+
"excluded_ous_only"
85+
) : (
86+
# case5 - if both include and exclude ouids are provided, includes override excludes
87+
var.is_organizational && length(var.include_ouids) > 0 && length(var.exclude_ouids) > 0 ? (
88+
"mixed_ous"
89+
) : ""
90+
)
91+
)
92+
)
93+
)
94+
)
95+
96+
# switch cases for various user provided org configuration to be onboarded
97+
deployment_options = {
98+
old_ouid_param = {
99+
org_units_to_deploy = var.org_units
100+
}
101+
entire_org = {
102+
org_units_to_deploy = local.root_org_unit
103+
}
104+
included_ous_only = {
105+
org_units_to_deploy = var.include_ouids
106+
}
107+
excluded_ous_only = {
108+
# onboard entire org and filter out all accounts in excluded OUs using account filter
109+
org_units_to_deploy = local.root_org_unit
110+
}
111+
mixed_ous = {
112+
# if both include and exclude ouids are provided, includes override excludes
113+
org_units_to_deploy = var.include_ouids
114+
}
115+
default = {
116+
org_units_to_deploy = local.root_org_unit
117+
}
118+
}
119+
120+
# final targets to deploy organizational resources in
121+
deployment_targets_ous = lookup(local.deployment_options, local.org_configuration, local.deployment_options.default)
122+
123+
exclude_root_ou = length(local.root_org_unit) > 0 ? contains(var.exclude_ouids, local.root_org_unit[0]) : false
124+
}
125+
126+
#-----------------------------------------------------------------
127+
# Manage configurations to determine account targets to deploy in
128+
#-----------------------------------------------------------------
129+
130+
# if only exclude_ouids are provided and as long as it isn't Root OU, fetch all their child accounts to filter exclusions
131+
data "aws_organizations_organizational_unit_descendant_accounts" "ou_accounts_to_exclude" {
132+
for_each = local.org_configuration == "excluded_ous_only" && !local.exclude_root_ou ? var.exclude_ouids : []
133+
parent_id = each.key
134+
}
135+
136+
locals {
137+
# ACCOUNTS CONFIGURATION (determine user provided accounts configuration)
138+
accounts_configuration = (
139+
# case1 - if old method is used where ONLY org_units is provided, this configuration is a noop
140+
local.check_old_ouid_param ? (
141+
"NONE"
142+
) : (
143+
# case2 - if only included accounts provided, include those accts as well
144+
var.is_organizational && length(var.include_accounts) > 0 && length(var.exclude_accounts) == 0 ? (
145+
"UNION"
146+
) : (
147+
# case3 - if only excluded accounts or only excluded ouids provided, exclude those accounts
148+
var.is_organizational && length(var.include_accounts) == 0 && ( length(var.exclude_accounts) > 0 || local.org_configuration == "excluded_ous_only" ) ? (
149+
"DIFFERENCE"
150+
) : (
151+
# case4 - if both include and exclude accounts are provided, includes override excludes
152+
var.is_organizational && length(var.include_accounts) > 0 && length(var.exclude_accounts) > 0 ? (
153+
"MIXED"
154+
) : ""
155+
)
156+
)
157+
)
158+
)
159+
160+
ou_accounts_to_exclude = flatten([ for ou_accounts in data.aws_organizations_organizational_unit_descendant_accounts.ou_accounts_to_exclude: [ ou_accounts.accounts[*].id ] ])
161+
accounts_to_exclude = setunion(local.ou_accounts_to_exclude, var.exclude_accounts)
162+
163+
# switch cases for various user provided accounts configuration to be onboarded
164+
deployment_account_options = {
165+
NONE = {
166+
accounts_to_deploy = []
167+
account_filter_type = "NONE"
168+
}
169+
UNION = {
170+
accounts_to_deploy = var.include_accounts
171+
account_filter_type = "UNION"
172+
}
173+
DIFFERENCE = {
174+
accounts_to_deploy = local.accounts_to_exclude
175+
account_filter_type = "DIFFERENCE"
176+
}
177+
MIXED = {
178+
accounts_to_deploy = var.include_accounts
179+
account_filter_type = "UNION"
180+
}
181+
default = {
182+
# default when neither of include/exclude accounts are provided
183+
accounts_to_deploy = []
184+
account_filter_type = "NONE"
185+
}
186+
}
187+
188+
# list of accounts to deploy organizational resources in
189+
deployment_targets_accounts = lookup(local.deployment_account_options, local.accounts_configuration, local.deployment_account_options.default)
190+
}
191+
192+
# -----------------------------------------------------------------------------------------------------
193+
# Remove below conditional once AWS issue is fixed -
194+
# https://github.com/aws-cloudformation/aws-cloudformation-resource-providers-cloudformation/issues/100
195+
# -----------------------------------------------------------------------------------------------------
196+
locals {
197+
# XXX: due to AWS bug of not having UNION filter fully working, there is no way to add those extra accounts requested.
198+
# to not miss out on those extra accounts, deploy the cloud resources across entire org and noop the UNION filter.
199+
# i.e till we can't deploy UNION, we deploy it all
200+
deployment_targets_org_units = local.deployment_targets_accounts.account_filter_type == "UNION" ? local.root_org_unit : local.deployment_targets_ous.org_units_to_deploy
201+
deployment_targets_accounts_filter = local.deployment_targets_accounts.account_filter_type == "UNION" ? "NONE" : local.deployment_targets_accounts.account_filter_type
202+
}

0 commit comments

Comments
 (0)