Skip to content

Commit a0d2233

Browse files
authored
feat(response actions) Full validation [SSPROD-64190] (#106)
* Preliminary work for management account. * Attempt to use stackset to deploy lambdas to all specified regions. * Roles extracted from cloudformation stack as they are not regional. * Some fixes * Adopting the strategy of fetching lambdas from Sysdig public S3 buckets. * WORKING * Added the possibility to disable some actions. * Preliminary support for subaccounts * Fixed organizational stuff * Other fixes. * Some comments * Short names in tags * Simple names as tags in resources * Removing unnecessary stuff * Initial change to support sysdig components * Other fixes. * Fixed version. * Added components outputs * Creating S3 storage and lambda to upload lambda code * Fix indent * Fixed. * Set new terraform plugin * Fixed readme * Some cleanup * Removed versioning and normalizing bucket names * Aligning terraform plugin * Added `wait_for_basic` * Linting * Provider data * Using customer id hash as external id * Fixed bug * Preliminary support for validation: adding validation role. TODO test * Locally testing * Fix * New version * New sysdig provider * Version embedded in tf, not using external input * Attempting to be sure to update lambdas
1 parent 5c3a4fa commit a0d2233

9 files changed

Lines changed: 169 additions & 29 deletions

File tree

modules/response-actions/locals.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ data "aws_organizations_organization" "org" {
2525
}
2626

2727
locals {
28+
response_actions_version = "1.0.2"
29+
2830
# fetch the AWS Root OU under org
2931
# As per https://docs.aws.amazon.com/organizations/latest/userguide/orgs_getting-started_concepts.html#organization-structure, there can be only one root
3032
root_org_unit = var.is_organizational ? [for root in data.aws_organizations_organization.org[0].roots : root.id] : []

modules/response-actions/main.tf

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,80 @@ resource "aws_iam_role_policy" "shared_lambda_invoke_policy" {
327327
})
328328
}
329329

330+
#-----------------------------------------------------------------------------------------------------------------------------------------
331+
# This resource creates an IAM role for validating the Response Actions deployment.
332+
# This role is assumed by Sysdig's cloud identity to read and validate Lambda functions, IAM roles, and policies
333+
# created by this module.
334+
#
335+
# The role allows:
336+
# 1. Sysdig's trusted identity to assume the role using the tenant-specific external ID for security
337+
# 2. Reading Lambda functions matching the resource name pattern
338+
# 3. Reading IAM roles and their attached/inline policies matching the resource name pattern
339+
#-----------------------------------------------------------------------------------------------------------------------------------------
340+
resource "aws_iam_role" "validation_role" {
341+
name = "${local.ra_resource_name}-validation-role"
342+
343+
assume_role_policy = jsonencode({
344+
Version = "2012-10-17"
345+
Statement = [
346+
{
347+
Action = "sts:AssumeRole"
348+
Effect = "Allow"
349+
Principal = {
350+
AWS = local.trusted_identity
351+
}
352+
Condition = {
353+
StringEquals = {
354+
"sts:ExternalId" = data.sysdig_secure_tenant_external_id.external_id.external_id
355+
}
356+
}
357+
}
358+
]
359+
})
360+
361+
tags = {
362+
Name = "${local.ra_resource_name}-validation-role"
363+
"sysdig.com/response-actions/resource-name" = "validation-role"
364+
}
365+
}
366+
367+
#-----------------------------------------------------------------------------------------------------------------------------------------
368+
# This policy grants read-only permissions for the validation role to:
369+
# 1. Read Lambda functions, their configurations, and policies created by this module
370+
# 2. Read IAM roles and their attached/inline policies created by this module
371+
#
372+
# Permissions are scoped to only resources matching the resource name pattern (${local.ra_resource_name}-*)
373+
#-----------------------------------------------------------------------------------------------------------------------------------------
374+
resource "aws_iam_role_policy" "validation_policy" {
375+
name = "${local.ra_resource_name}-validation-policy"
376+
role = aws_iam_role.validation_role.id
377+
378+
policy = jsonencode({
379+
Version = "2012-10-17"
380+
Statement = [
381+
{
382+
Effect = "Allow"
383+
Action = [
384+
"lambda:GetFunction",
385+
"lambda:GetPolicy",
386+
"lambda:ListFunctions"
387+
]
388+
Resource = "${local.arn_prefix}:lambda:*:${data.aws_caller_identity.current.account_id}:function:${local.ra_resource_name}-*"
389+
},
390+
{
391+
Effect = "Allow"
392+
Action = [
393+
"iam:GetRole",
394+
"iam:GetRolePolicy",
395+
"iam:ListRolePolicies",
396+
"iam:ListAttachedRolePolicies"
397+
]
398+
Resource = "${local.arn_prefix}:iam::${data.aws_caller_identity.current.account_id}:role/${local.ra_resource_name}-*"
399+
}
400+
]
401+
})
402+
}
403+
330404
#-----------------------------------------------------------------------------------------------------------------------------------------
331405
# IAM Roles for Lambda Functions (Created Once in Management Account)
332406
#
@@ -704,7 +778,7 @@ resource "aws_cloudformation_stack_set" "lambda_functions" {
704778
ConfigureResourceAccessRoleName = local.enable_make_private ? aws_iam_role.configure_resource_access_role[0].name : ""
705779
CreateVolumeSnapshotsRoleName = local.enable_create_volume_snapshot ? aws_iam_role.create_volume_snapshots_role[0].name : ""
706780
DeleteVolumeSnapshotsRoleName = local.enable_create_volume_snapshot ? aws_iam_role.delete_volume_snapshots_role[0].name : ""
707-
ResponseActionsVersion = var.response_actions_version
781+
ResponseActionsVersion = local.response_actions_version
708782
LambdaPackagesBaseUrl = var.lambda_packages_base_url
709783
EnableQuarantineUser = local.enable_quarantine_user ? "true" : "false"
710784
EnableFetchCloudLogs = local.enable_fetch_cloud_logs ? "true" : "false"

modules/response-actions/organizational.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ resource "aws_cloudformation_stack_set" "ra_delegate_roles" {
5050
CreateVolumeSnapshotsRoleName = local.enable_create_volume_snapshot ? aws_iam_role.create_volume_snapshots_role[0].name : ""
5151
DeleteVolumeSnapshotsLambdaRoleArn = local.enable_create_volume_snapshot ? aws_iam_role.delete_volume_snapshots_role[0].arn : ""
5252
DeleteVolumeSnapshotsRoleName = local.enable_create_volume_snapshot ? aws_iam_role.delete_volume_snapshots_role[0].name : ""
53+
ValidationRoleName = "${local.ra_resource_name}-validation-role"
54+
TrustedIdentity = local.trusted_identity
55+
ExternalId = data.sysdig_secure_tenant_external_id.external_id.external_id
56+
ResourceNamePrefix = local.ra_resource_name
5357
EnableQuarantineUser = local.enable_quarantine_user ? "true" : "false"
5458
EnableFetchCloudLogs = local.enable_fetch_cloud_logs ? "true" : "false"
5559
EnableMakePrivate = local.enable_make_private ? "true" : "false"

modules/response-actions/outputs.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,8 @@ output "wait_after_basic" {
7272
value = var.wait_after_basic_seconds > 0 ? time_sleep.wait_after_ciem_basic : null
7373
description = "Wait handle to delay downstream operations after basic by the configured seconds."
7474
}
75+
76+
output "validation_role_arn" {
77+
value = aws_iam_role.validation_role.arn
78+
description = "ARN of the validation role for reading Response Actions resources"
79+
}

modules/response-actions/sysdig_components.tf

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,36 @@ resource "sysdig_secure_cloud_auth_account_component" "aws_responder" {
88
account_id = var.sysdig_secure_account_id
99
type = local.responder_component_type
1010
instance = "cloud-responder"
11-
version = "v${var.response_actions_version}"
11+
version = "v${local.response_actions_version}"
1212
cloud_responder_metadata = jsonencode({
1313
aws = {
1414
responder_lambdas = {
15-
lambda_names = local.enabled_lambda_names
16-
regions = local.region_set
17-
delegate_role_name = aws_iam_role.shared_cross_account_lambda_invoker.name
15+
lambda_names = local.enabled_lambda_names
16+
regions = local.region_set
17+
delegate_role_name = aws_iam_role.shared_cross_account_lambda_invoker.name
18+
validation_role_name = aws_iam_role.validation_role.name
1819
}
1920
}
2021
})
2122

2223
depends_on = [
2324
aws_cloudformation_stack_set_instance.lambda_functions,
24-
aws_iam_role.shared_cross_account_lambda_invoker
25+
aws_iam_role.shared_cross_account_lambda_invoker,
26+
aws_iam_role.validation_role
2527
]
2628
}
2729

2830
resource "sysdig_secure_cloud_auth_account_component" "aws_responder_roles" {
2931
account_id = var.sysdig_secure_account_id
3032
type = local.roles_component_type
3133
instance = "cloud-responder"
32-
version = "v${var.response_actions_version}"
34+
version = "v${local.response_actions_version}"
3335
cloud_responder_roles_metadata = jsonencode({
36+
validation_role = {
37+
aws = {
38+
role_name = aws_iam_role.validation_role.name
39+
}
40+
}
3441
roles = concat(
3542
local.enable_quarantine_user ? [
3643
{
@@ -80,6 +87,7 @@ resource "sysdig_secure_cloud_auth_account_component" "aws_responder_roles" {
8087
aws_iam_role.fetch_cloud_logs_role,
8188
aws_iam_role.configure_resource_access_role,
8289
aws_iam_role.create_volume_snapshots_role,
83-
aws_iam_role.delete_volume_snapshots_role
90+
aws_iam_role.delete_volume_snapshots_role,
91+
aws_iam_role.validation_role
8492
]
8593
}

modules/response-actions/templates/delegate_roles_stackset.tpl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ Parameters:
6262
Default: "true"
6363
AllowedValues: ["true", "false"]
6464
Description: Enable create and delete volume snapshot delegate roles
65+
ValidationRoleName:
66+
Type: String
67+
Description: Name for the validation role
68+
TrustedIdentity:
69+
Type: String
70+
Description: ARN of the Sysdig trusted identity
71+
ExternalId:
72+
Type: String
73+
Description: External ID for assuming the validation role
74+
ResourceNamePrefix:
75+
Type: String
76+
Description: Prefix for resource names to scope IAM permissions
6577

6678
Conditions:
6779
CreateQuarantineUserResources: !Equals [!Ref EnableQuarantineUser, "true"]
@@ -320,6 +332,43 @@ Resources:
320332
- Key: 'sysdig.com/response-actions/resource-name'
321333
Value: 'remove-policy-delegate-role'
322334

335+
# Validation Role: Read-only access for validating Response Actions deployment
336+
ValidationDelegateRole:
337+
Type: AWS::IAM::Role
338+
Properties:
339+
RoleName: !Ref ValidationRoleName
340+
Description: Validation role for reading Response Actions resources
341+
MaxSessionDuration: 3600
342+
AssumeRolePolicyDocument:
343+
Version: '2012-10-17'
344+
Statement:
345+
- Effect: Allow
346+
Principal:
347+
AWS: !Ref TrustedIdentity
348+
Action: 'sts:AssumeRole'
349+
Condition:
350+
StringEquals:
351+
'sts:ExternalId': !Ref ExternalId
352+
Policies:
353+
- PolicyName: response-actions-validation
354+
PolicyDocument:
355+
Version: '2012-10-17'
356+
Statement:
357+
- Effect: Allow
358+
Action:
359+
- 'iam:GetRole'
360+
- 'iam:GetRolePolicy'
361+
- 'iam:ListRolePolicies'
362+
- 'iam:ListAttachedRolePolicies'
363+
Resource: !Sub 'arn:aws:iam::${AWS::AccountId}:role/${ResourceNamePrefix}-*'
364+
Tags:
365+
- Key: ManagedBy
366+
Value: Terraform
367+
- Key: Purpose
368+
Value: ResponseActions
369+
- Key: 'sysdig.com/response-actions/resource-name'
370+
Value: 'validation-delegate-role'
371+
323372
Outputs:
324373
ConfigureAccessDelegateRoleName:
325374
Condition: CreateMakePrivateResources
@@ -345,3 +394,6 @@ Outputs:
345394
Condition: CreateQuarantineUserResources
346395
Value: !Ref RemovePolicyDelegateRole
347396
Description: Name of the remove policy delegate role
397+
ValidationDelegateRoleName:
398+
Value: !Ref ValidationDelegateRole
399+
Description: Name of the validation delegate role

modules/response-actions/templates/lambda-stackset.tpl

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -324,15 +324,14 @@ Resources:
324324
Condition: CreateQuarantineUserResources
325325
DependsOn:
326326
- QuarantineUserLogGroup
327-
- QuarantineUserPackage
328327
Properties:
329328
FunctionName: !Sub '${ResourceName}-quarantine-user'
330329
Runtime: python3.12
331330
Handler: app.index.handler
332331
Role: !Ref QuarantineUserRoleArn
333332
Code:
334-
S3Bucket: !Ref LambdaPackagesBucket
335-
S3Key: !Sub '${ResponseActionsVersion}/quarantine_user.zip'
333+
S3Bucket: !GetAtt QuarantineUserPackage.Bucket
334+
S3Key: !GetAtt QuarantineUserPackage.Key
336335
Timeout: 300
337336
MemorySize: 128
338337
Environment:
@@ -352,15 +351,14 @@ Resources:
352351
Condition: CreateFetchCloudLogsResources
353352
DependsOn:
354353
- FetchCloudLogsLogGroup
355-
- FetchCloudLogsPackage
356354
Properties:
357355
FunctionName: !Sub '${ResourceName}-fetch-cloud-logs'
358356
Runtime: python3.12
359357
Handler: app.index.handler
360358
Role: !Ref FetchCloudLogsRoleArn
361359
Code:
362-
S3Bucket: !Ref LambdaPackagesBucket
363-
S3Key: !Sub '${ResponseActionsVersion}/fetch_cloud_logs.zip'
360+
S3Bucket: !GetAtt FetchCloudLogsPackage.Bucket
361+
S3Key: !GetAtt FetchCloudLogsPackage.Key
364362
Timeout: 300
365363
MemorySize: 128
366364
Environment:
@@ -380,15 +378,14 @@ Resources:
380378
Condition: CreateQuarantineUserResources
381379
DependsOn:
382380
- RemovePolicyLogGroup
383-
- RemovePolicyPackage
384381
Properties:
385382
FunctionName: !Sub '${ResourceName}-remove-policy'
386383
Runtime: python3.12
387384
Handler: app.index.handler
388385
Role: !Ref RemovePolicyRoleArn
389386
Code:
390-
S3Bucket: !Ref LambdaPackagesBucket
391-
S3Key: !Sub '${ResponseActionsVersion}/remove_policy.zip'
387+
S3Bucket: !GetAtt RemovePolicyPackage.Bucket
388+
S3Key: !GetAtt RemovePolicyPackage.Key
392389
Timeout: 300
393390
MemorySize: 128
394391
Environment:
@@ -408,15 +405,14 @@ Resources:
408405
Condition: CreateMakePrivateResources
409406
DependsOn:
410407
- ConfigureResourceAccessLogGroup
411-
- ConfigureResourceAccessPackage
412408
Properties:
413409
FunctionName: !Sub '${ResourceName}-configure-resource-access'
414410
Runtime: python3.12
415411
Handler: app.index.handler
416412
Role: !Ref ConfigureResourceAccessRoleArn
417413
Code:
418-
S3Bucket: !Ref LambdaPackagesBucket
419-
S3Key: !Sub '${ResponseActionsVersion}/configure_resource_access.zip'
414+
S3Bucket: !GetAtt ConfigureResourceAccessPackage.Bucket
415+
S3Key: !GetAtt ConfigureResourceAccessPackage.Key
420416
Timeout: 300
421417
MemorySize: 128
422418
Environment:
@@ -436,15 +432,14 @@ Resources:
436432
Condition: CreateVolumeSnapshotResources
437433
DependsOn:
438434
- CreateVolumeSnapshotsLogGroup
439-
- CreateVolumeSnapshotsPackage
440435
Properties:
441436
FunctionName: !Sub '${ResourceName}-create-volume-snapshots'
442437
Runtime: python3.12
443438
Handler: app.index.handler
444439
Role: !Ref CreateVolumeSnapshotsRoleArn
445440
Code:
446-
S3Bucket: !Ref LambdaPackagesBucket
447-
S3Key: !Sub '${ResponseActionsVersion}/create_volume_snapshots.zip'
441+
S3Bucket: !GetAtt CreateVolumeSnapshotsPackage.Bucket
442+
S3Key: !GetAtt CreateVolumeSnapshotsPackage.Key
448443
Timeout: 300
449444
MemorySize: 128
450445
Environment:
@@ -464,15 +459,14 @@ Resources:
464459
Condition: CreateVolumeSnapshotResources
465460
DependsOn:
466461
- DeleteVolumeSnapshotsLogGroup
467-
- DeleteVolumeSnapshotsPackage
468462
Properties:
469463
FunctionName: !Sub '${ResourceName}-delete-volume-snapshots'
470464
Runtime: python3.12
471465
Handler: app.index.handler
472466
Role: !Ref DeleteVolumeSnapshotsRoleArn
473467
Code:
474-
S3Bucket: !Ref LambdaPackagesBucket
475-
S3Key: !Sub '${ResponseActionsVersion}/delete_volume_snapshots.zip'
468+
S3Bucket: !GetAtt DeleteVolumeSnapshotsPackage.Bucket
469+
S3Key: !GetAtt DeleteVolumeSnapshotsPackage.Key
476470
Timeout: 300
477471
MemorySize: 128
478472
Environment:

modules/response-actions/variables.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,10 @@ variable "api_base_url" {
9595
}
9696

9797
variable "response_actions_version" {
98-
description = "Response Actions version"
98+
description = "Deprecated: this variable is no longer used. The version is hardcoded to 1.0.2."
9999
type = string
100-
default = "0.0.16"
100+
default = null
101+
nullable = true
101102
}
102103

103104
variable "lambda_packages_base_url" {

modules/response-actions/versions.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ terraform {
1111
}
1212
sysdig = {
1313
source = "sysdiglabs/sysdig"
14-
version = "~> 3.3"
14+
version = "~> 3.5"
1515
}
1616
time = {
1717
source = "hashicorp/time"

0 commit comments

Comments
 (0)