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