Compare commits

...
Author SHA1 Message Date
César Arroba 45d69245d1 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.
2026-05-28 19:02:39 +02:00
10 changed files with 478 additions and 1 deletions
@@ -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"
+15
View File
@@ -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.
@@ -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
}
@@ -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
}
@@ -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."
}
}
@@ -0,0 +1,3 @@
terraform {
required_version = ">= 1.5"
}
+19 -1
View File
@@ -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
}
@@ -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)
}
@@ -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"
@@ -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
}