From 45d69245d1cbfed8a6223c9e34312be3b850530b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Arroba?= Date: Thu, 28 May 2026 19:02:39 +0200 Subject: [PATCH] feat(permissions): add real-time event detection integration Adds an optional EventBridge API Destination integration to both the CloudFormation and Terraform templates customers use to onboard their AWS account. When enabled, selected CloudTrail management events (Security Groups, IAM, S3, CloudTrail, KMS, RDS, Lambda) are forwarded to Prowler Cloud for real-time reasoning by Lighthouse AI. Toggled via: - CFN: EnableEventsIntegration parameter - TF: enable_events_integration variable Follows the same opt-in pattern as the existing S3 integration. --- .../cloudformation/prowler-scan-role.yml | 193 ++++++++++++++++++ permissions/templates/terraform/README.md | 15 ++ .../terraform/events-integration/main.tf | 164 +++++++++++++++ .../terraform/events-integration/outputs.tf | 14 ++ .../terraform/events-integration/variables.tf | 20 ++ .../terraform/events-integration/versions.tf | 3 + permissions/templates/terraform/main.tf | 20 +- permissions/templates/terraform/outputs.tf | 15 ++ .../terraform/terraform.tfvars.example | 16 ++ permissions/templates/terraform/variables.tf | 19 ++ 10 files changed, 478 insertions(+), 1 deletion(-) create mode 100644 permissions/templates/terraform/events-integration/main.tf create mode 100644 permissions/templates/terraform/events-integration/outputs.tf create mode 100644 permissions/templates/terraform/events-integration/variables.tf create mode 100644 permissions/templates/terraform/events-integration/versions.tf diff --git a/permissions/templates/cloudformation/prowler-scan-role.yml b/permissions/templates/cloudformation/prowler-scan-role.yml index 395fed6424..508e4848e1 100644 --- a/permissions/templates/cloudformation/prowler-scan-role.yml +++ b/permissions/templates/cloudformation/prowler-scan-role.yml @@ -63,10 +63,33 @@ Parameters: The AWS Account ID owner of the S3 Bucket. Type: String Default: "" + EnableEventsIntegration: + Description: | + Enable real-time event detection. Forwards selected CloudTrail management events + (Security Group, IAM, S3 changes) to Prowler Cloud via EventBridge API Destination. + Type: String + Default: false + AllowedValues: + - true + - false + ProwlerWebhookUrl: + Description: | + Prowler Cloud webhook URL that will receive events. Provided in Prowler Cloud onboarding. + Required if EnableEventsIntegration is true. + Type: String + Default: "" + ProwlerApiKey: + Description: | + Per-tenant API key used to authenticate events sent to Prowler Cloud. + Provided in Prowler Cloud onboarding. Required if EnableEventsIntegration is true. + Type: String + Default: "" + NoEcho: true Conditions: OrganizationsEnabled: !Equals [!Ref EnableOrganizations, true] S3IntegrationEnabled: !Equals [!Ref EnableS3Integration, true] + EventsIntegrationEnabled: !Equals [!Ref EnableEventsIntegration, true] Resources: @@ -215,6 +238,161 @@ Resources: - Key: "Name" Value: "ProwlerScan" + ProwlerEventsConnection: + Type: AWS::Events::Connection + Condition: EventsIntegrationEnabled + Properties: + Name: ProwlerEventsConnection + AuthorizationType: API_KEY + AuthParameters: + ApiKeyAuthParameters: + ApiKeyName: x-api-key + ApiKeyValue: !Ref ProwlerApiKey + + ProwlerEventsApiDestination: + Type: AWS::Events::ApiDestination + Condition: EventsIntegrationEnabled + Properties: + Name: ProwlerEventsApiDestination + ConnectionArn: !GetAtt ProwlerEventsConnection.Arn + InvocationEndpoint: !Ref ProwlerWebhookUrl + HttpMethod: POST + + ProwlerEventsInvokeRole: + Type: AWS::IAM::Role + Condition: EventsIntegrationEnabled + Properties: + RoleName: ProwlerEventsInvoke + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: events.amazonaws.com + Action: "sts:AssumeRole" + Policies: + - PolicyName: InvokeApiDestination + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: "events:InvokeApiDestination" + Resource: !GetAtt ProwlerEventsApiDestination.Arn + Tags: + - Key: "Service" + Value: "https://prowler.com" + - Key: "Support" + Value: "support@prowler.com" + - Key: "CloudFormation" + Value: "true" + - Key: "Name" + Value: "ProwlerEventsInvoke" + + ProwlerSecurityChangesRule: + Type: AWS::Events::Rule + Condition: EventsIntegrationEnabled + Properties: + Name: ProwlerSecurityChanges + Description: Forwards CloudTrail management events (SG, IAM, S3) to Prowler Cloud. + State: ENABLED + EventPattern: + detail-type: + - "AWS API Call via CloudTrail" + detail: + eventSource: + - ec2.amazonaws.com + - iam.amazonaws.com + - s3.amazonaws.com + - s3control.amazonaws.com + - cloudtrail.amazonaws.com + - kms.amazonaws.com + - rds.amazonaws.com + - lambda.amazonaws.com + eventName: + # EC2 / Security Groups + - AuthorizeSecurityGroupIngress + - AuthorizeSecurityGroupEgress + - RevokeSecurityGroupIngress + - RevokeSecurityGroupEgress + - CreateSecurityGroup + - DeleteSecurityGroup + - ModifySecurityGroupRules + - ModifySnapshotAttribute + - ModifyImageAttribute + # IAM + - AttachRolePolicy + - DetachRolePolicy + - PutRolePolicy + - DeleteRolePolicy + - AttachUserPolicy + - DetachUserPolicy + - PutUserPolicy + - DeleteUserPolicy + - AttachGroupPolicy + - DetachGroupPolicy + - PutGroupPolicy + - DeleteGroupPolicy + - CreatePolicy + - CreatePolicyVersion + - DeletePolicy + - UpdateAssumeRolePolicy + - CreateRole + - DeleteRole + - CreateUser + - DeleteUser + - CreateAccessKey + - DeleteAccessKey + - CreateLoginProfile + - UpdateLoginProfile + - DeleteLoginProfile + - DeactivateMFADevice + - EnableMFADevice + - UpdateAccountPasswordPolicy + - DeleteAccountPasswordPolicy + # S3 (bucket-level + account-level via s3control) + - PutBucketPolicy + - DeleteBucketPolicy + - PutBucketEncryption + - DeleteBucketEncryption + - PutBucketPublicAccessBlock + - DeleteBucketPublicAccessBlock + - PutBucketAcl + - PutBucketVersioning + - PutBucketLogging + - CreateBucket + - DeleteBucket + - PutPublicAccessBlock + - DeletePublicAccessBlock + # CloudTrail tampering + - StopLogging + - DeleteTrail + - UpdateTrail + - PutEventSelectors + # KMS + - DisableKey + - ScheduleKeyDeletion + - CancelKeyDeletion + - PutKeyPolicy + - CreateGrant + # RDS + - ModifyDBInstance + - ModifyDBCluster + - CreateDBInstance + - DeleteDBInstance + - DeleteDBCluster + - AuthorizeDBSecurityGroupIngress + - ModifyDBSnapshotAttribute + # Lambda + - AddPermission + - RemovePermission + - CreateFunctionUrlConfig + - UpdateFunctionUrlConfig + - DeleteFunctionUrlConfig + Targets: + - Id: ProwlerCloud + Arn: !GetAtt ProwlerEventsApiDestination.Arn + RoleArn: !GetAtt ProwlerEventsInvokeRole.Arn + Metadata: AWS::CloudFormation::StackName: "Prowler" AWS::CloudFormation::Interface: @@ -227,11 +405,14 @@ Metadata: - IAMPrincipal - EnableOrganizations - EnableS3Integration + - EnableEventsIntegration - Label: default: Optional Parameters: - S3IntegrationBucketName - S3IntegrationBucketAccountId + - ProwlerWebhookUrl + - ProwlerApiKey Outputs: ProwlerScanRoleArn: @@ -239,3 +420,15 @@ Outputs: Value: !GetAtt ProwlerScan.Arn Export: Name: !Sub "${AWS::StackName}-ProwlerScanRoleArn" + ProwlerEventsRuleArn: + Description: "ARN of the EventBridge rule forwarding events to Prowler Cloud" + Condition: EventsIntegrationEnabled + Value: !GetAtt ProwlerSecurityChangesRule.Arn + Export: + Name: !Sub "${AWS::StackName}-ProwlerEventsRuleArn" + ProwlerEventsApiDestinationArn: + Description: "ARN of the EventBridge API Destination targeting Prowler Cloud" + Condition: EventsIntegrationEnabled + Value: !GetAtt ProwlerEventsApiDestination.Arn + Export: + Name: !Sub "${AWS::StackName}-ProwlerEventsApiDestinationArn" diff --git a/permissions/templates/terraform/README.md b/permissions/templates/terraform/README.md index 1bb037daa2..c0141b5a7d 100644 --- a/permissions/templates/terraform/README.md +++ b/permissions/templates/terraform/README.md @@ -25,6 +25,9 @@ This Terraform configuration creates the necessary IAM role and policies to allo - `enable_s3_integration` (optional): Enable S3 integration for storing scan reports (default: false) - `s3_integration_bucket_name` (conditional): S3 bucket name for reports (required if `enable_s3_integration` is true) - `s3_integration_bucket_account_id` (conditional): S3 bucket owner account ID (required if `enable_s3_integration` is true) +- `enable_events_integration` (optional): Forward CloudTrail management events to Prowler Cloud in near real-time via EventBridge API Destination (default: false) +- `prowler_webhook_url` (conditional): Prowler Cloud webhook URL (required if `enable_events_integration` is true) +- `prowler_api_key` (conditional): Per-tenant API key (required if `enable_events_integration` is true) ### Usage Examples @@ -42,6 +45,15 @@ terraform apply \ -var="s3_integration_bucket_account_id=123456789012" ``` +#### With real-time events integration enabled +```bash +terraform apply \ + -var="external_id=your-external-id-here" \ + -var="enable_events_integration=true" \ + -var="prowler_webhook_url=https://api.prowler.com/v1/events/ingest" \ + -var="prowler_api_key=your-prowler-api-key-here" +``` + #### Using terraform.tfvars file (Recommended) ```bash cp terraform.tfvars.example terraform.tfvars @@ -60,5 +72,8 @@ After successful deployment, you'll get: - `prowler_role_arn`: The ARN of the created IAM role (use this in Prowler App) - `prowler_role_name`: The name of the IAM role - `s3_integration_enabled`: Whether S3 integration is enabled +- `events_integration_enabled`: Whether real-time events integration is enabled +- `prowler_events_rule_arn`: ARN of the EventBridge rule (null if events integration disabled) +- `prowler_events_api_destination_arn`: ARN of the EventBridge API Destination (null if events integration disabled) > **Note:** Terraform will use the AWS credentials of your default profile or AWS_PROFILE environment variable. diff --git a/permissions/templates/terraform/events-integration/main.tf b/permissions/templates/terraform/events-integration/main.tf new file mode 100644 index 0000000000..1ee678178b --- /dev/null +++ b/permissions/templates/terraform/events-integration/main.tf @@ -0,0 +1,164 @@ +# EventBridge Connection (stores the API key in Secrets Manager under the hood) +################################### +resource "aws_cloudwatch_event_connection" "prowler" { + name = "ProwlerEventsConnection" + authorization_type = "API_KEY" + + auth_parameters { + api_key { + key = "x-api-key" + value = var.prowler_api_key + } + } +} + +# EventBridge API Destination (the HTTPS target) +################################### +resource "aws_cloudwatch_event_api_destination" "prowler" { + name = "ProwlerEventsApiDestination" + invocation_endpoint = var.prowler_webhook_url + http_method = "POST" + connection_arn = aws_cloudwatch_event_connection.prowler.arn +} + +# IAM Role assumed by EventBridge to call the API Destination +################################### +data "aws_iam_policy_document" "events_assume_role" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "Service" + identifiers = ["events.amazonaws.com"] + } + } +} + +resource "aws_iam_role" "prowler_events_invoke" { + name = "ProwlerEventsInvoke" + assume_role_policy = data.aws_iam_policy_document.events_assume_role.json +} + +resource "aws_iam_role_policy" "prowler_events_invoke" { + name = "InvokeApiDestination" + role = aws_iam_role.prowler_events_invoke.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Action = "events:InvokeApiDestination" + Resource = aws_cloudwatch_event_api_destination.prowler.arn + }] + }) +} + +# Rule: forward selected CloudTrail management events to Prowler Cloud +################################### +resource "aws_cloudwatch_event_rule" "prowler_security_changes" { + name = "ProwlerSecurityChanges" + description = "Forwards CloudTrail management events (SG, IAM, S3, CloudTrail, KMS, RDS, Lambda) to Prowler Cloud." + state = "ENABLED" + + event_pattern = jsonencode({ + "detail-type" = ["AWS API Call via CloudTrail"] + detail = { + eventSource = [ + "ec2.amazonaws.com", + "iam.amazonaws.com", + "s3.amazonaws.com", + "s3control.amazonaws.com", + "cloudtrail.amazonaws.com", + "kms.amazonaws.com", + "rds.amazonaws.com", + "lambda.amazonaws.com", + ] + eventName = [ + # EC2 / Security Groups + "AuthorizeSecurityGroupIngress", + "AuthorizeSecurityGroupEgress", + "RevokeSecurityGroupIngress", + "RevokeSecurityGroupEgress", + "CreateSecurityGroup", + "DeleteSecurityGroup", + "ModifySecurityGroupRules", + "ModifySnapshotAttribute", + "ModifyImageAttribute", + # IAM + "AttachRolePolicy", + "DetachRolePolicy", + "PutRolePolicy", + "DeleteRolePolicy", + "AttachUserPolicy", + "DetachUserPolicy", + "PutUserPolicy", + "DeleteUserPolicy", + "AttachGroupPolicy", + "DetachGroupPolicy", + "PutGroupPolicy", + "DeleteGroupPolicy", + "CreatePolicy", + "CreatePolicyVersion", + "DeletePolicy", + "UpdateAssumeRolePolicy", + "CreateRole", + "DeleteRole", + "CreateUser", + "DeleteUser", + "CreateAccessKey", + "DeleteAccessKey", + "CreateLoginProfile", + "UpdateLoginProfile", + "DeleteLoginProfile", + "DeactivateMFADevice", + "EnableMFADevice", + "UpdateAccountPasswordPolicy", + "DeleteAccountPasswordPolicy", + # S3 (bucket-level + account-level via s3control) + "PutBucketPolicy", + "DeleteBucketPolicy", + "PutBucketEncryption", + "DeleteBucketEncryption", + "PutBucketPublicAccessBlock", + "DeleteBucketPublicAccessBlock", + "PutBucketAcl", + "PutBucketVersioning", + "PutBucketLogging", + "CreateBucket", + "DeleteBucket", + "PutPublicAccessBlock", + "DeletePublicAccessBlock", + # CloudTrail tampering + "StopLogging", + "DeleteTrail", + "UpdateTrail", + "PutEventSelectors", + # KMS + "DisableKey", + "ScheduleKeyDeletion", + "CancelKeyDeletion", + "PutKeyPolicy", + "CreateGrant", + # RDS + "ModifyDBInstance", + "ModifyDBCluster", + "CreateDBInstance", + "DeleteDBInstance", + "DeleteDBCluster", + "AuthorizeDBSecurityGroupIngress", + "ModifyDBSnapshotAttribute", + # Lambda + "AddPermission", + "RemovePermission", + "CreateFunctionUrlConfig", + "UpdateFunctionUrlConfig", + "DeleteFunctionUrlConfig", + ] + } + }) +} + +resource "aws_cloudwatch_event_target" "to_prowler" { + rule = aws_cloudwatch_event_rule.prowler_security_changes.name + arn = aws_cloudwatch_event_api_destination.prowler.arn + role_arn = aws_iam_role.prowler_events_invoke.arn +} diff --git a/permissions/templates/terraform/events-integration/outputs.tf b/permissions/templates/terraform/events-integration/outputs.tf new file mode 100644 index 0000000000..575eba2eaa --- /dev/null +++ b/permissions/templates/terraform/events-integration/outputs.tf @@ -0,0 +1,14 @@ +output "prowler_events_rule_arn" { + description = "ARN of the EventBridge rule forwarding events to Prowler Cloud" + value = aws_cloudwatch_event_rule.prowler_security_changes.arn +} + +output "prowler_events_api_destination_arn" { + description = "ARN of the EventBridge API Destination targeting Prowler Cloud" + value = aws_cloudwatch_event_api_destination.prowler.arn +} + +output "prowler_events_invoke_role_arn" { + description = "ARN of the IAM role assumed by EventBridge to invoke the API Destination" + value = aws_iam_role.prowler_events_invoke.arn +} diff --git a/permissions/templates/terraform/events-integration/variables.tf b/permissions/templates/terraform/events-integration/variables.tf new file mode 100644 index 0000000000..8b389d3a00 --- /dev/null +++ b/permissions/templates/terraform/events-integration/variables.tf @@ -0,0 +1,20 @@ +variable "prowler_webhook_url" { + type = string + description = "Prowler Cloud webhook URL that will receive events. Provided in Prowler Cloud onboarding." + + validation { + condition = can(regex("^https://", var.prowler_webhook_url)) + error_message = "prowler_webhook_url must be an HTTPS URL." + } +} + +variable "prowler_api_key" { + type = string + description = "Per-tenant API key used to authenticate events sent to Prowler Cloud. Provided in Prowler Cloud onboarding." + sensitive = true + + validation { + condition = length(var.prowler_api_key) > 0 + error_message = "prowler_api_key must not be empty." + } +} diff --git a/permissions/templates/terraform/events-integration/versions.tf b/permissions/templates/terraform/events-integration/versions.tf new file mode 100644 index 0000000000..f5a0ad7ccb --- /dev/null +++ b/permissions/templates/terraform/events-integration/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.5" +} diff --git a/permissions/templates/terraform/main.tf b/permissions/templates/terraform/main.tf index 1f1306c4ce..6e7132141c 100644 --- a/permissions/templates/terraform/main.tf +++ b/permissions/templates/terraform/main.tf @@ -15,6 +15,13 @@ check "s3_integration_requirements" { } } +check "events_integration_requirements" { + assert { + condition = !var.enable_events_integration || (var.prowler_webhook_url != "" && var.prowler_api_key != "") + error_message = "When enable_events_integration is true, both prowler_webhook_url and prowler_api_key must be provided and non-empty." + } +} + # IAM Role ################################### data "aws_iam_policy_document" "prowler_assume_role_policy" { @@ -113,8 +120,19 @@ module "s3_integration" { source = "./s3-integration" - s3_integration_bucket_name = var.s3_integration_bucket_name + s3_integration_bucket_name = var.s3_integration_bucket_name s3_integration_bucket_account_id = var.s3_integration_bucket_account_id prowler_role_name = aws_iam_role.prowler_scan.name } + +# Events Integration Module +################################### +module "events_integration" { + count = var.enable_events_integration ? 1 : 0 + + source = "./events-integration" + + prowler_webhook_url = var.prowler_webhook_url + prowler_api_key = var.prowler_api_key +} diff --git a/permissions/templates/terraform/outputs.tf b/permissions/templates/terraform/outputs.tf index e1a971dcde..a0692640b4 100644 --- a/permissions/templates/terraform/outputs.tf +++ b/permissions/templates/terraform/outputs.tf @@ -20,3 +20,18 @@ output "s3_integration_enabled" { description = "Whether S3 integration is enabled" value = var.enable_s3_integration } + +output "events_integration_enabled" { + description = "Whether real-time events integration is enabled" + value = var.enable_events_integration +} + +output "prowler_events_rule_arn" { + description = "ARN of the EventBridge rule forwarding events to Prowler Cloud (null if disabled)" + value = try(module.events_integration[0].prowler_events_rule_arn, null) +} + +output "prowler_events_api_destination_arn" { + description = "ARN of the EventBridge API Destination targeting Prowler Cloud (null if disabled)" + value = try(module.events_integration[0].prowler_events_api_destination_arn, null) +} diff --git a/permissions/templates/terraform/terraform.tfvars.example b/permissions/templates/terraform/terraform.tfvars.example index 71561f5574..5916032ca5 100644 --- a/permissions/templates/terraform/terraform.tfvars.example +++ b/permissions/templates/terraform/terraform.tfvars.example @@ -29,3 +29,19 @@ external_id = "your-unique-external-id-here" # AWS Account ID that owns the S3 bucket (usually your account) # s3_integration_bucket_account_id = "123456789012" + +# ============================================================================= +# Real-Time Events Integration Configuration +# ============================================================================= +# Uncomment the following lines to forward selected CloudTrail management +# events (SG, IAM, S3, CloudTrail, KMS, RDS, Lambda) to Prowler Cloud in +# near real-time via EventBridge API Destination. + +# Enable real-time events integration +# enable_events_integration = true + +# Prowler Cloud webhook URL (provided in Prowler Cloud onboarding) +# prowler_webhook_url = "https://api.prowler.com/v1/events/ingest" + +# Per-tenant API key (provided in Prowler Cloud onboarding) +# prowler_api_key = "your-prowler-api-key-here" diff --git a/permissions/templates/terraform/variables.tf b/permissions/templates/terraform/variables.tf index 832171b62a..9fa13463f4 100644 --- a/permissions/templates/terraform/variables.tf +++ b/permissions/templates/terraform/variables.tf @@ -60,3 +60,22 @@ variable "s3_integration_bucket_account_id" { error_message = "s3_integration_bucket_account_id must be a valid 12-digit AWS Account ID or empty." } } + +variable "enable_events_integration" { + type = bool + description = "Enable real-time event detection. Forwards selected CloudTrail management events to Prowler Cloud via EventBridge API Destination." + default = false +} + +variable "prowler_webhook_url" { + type = string + description = "Prowler Cloud webhook URL that will receive events. Provided in Prowler Cloud onboarding. Required if enable_events_integration is true." + default = "" +} + +variable "prowler_api_key" { + type = string + description = "Per-tenant API key used to authenticate events sent to Prowler Cloud. Provided in Prowler Cloud onboarding. Required if enable_events_integration is true." + default = "" + sensitive = true +}