feat(s3): templates for permissions (#8395)

This commit is contained in:
Pepe Fagoaga
2025-08-05 13:51:04 +02:00
committed by GitHub
parent 903e4f8b9f
commit 92bc992e7f
14 changed files with 461 additions and 58 deletions

View File

@@ -0,0 +1,182 @@
AWSTemplateFormatVersion: "2010-09-09"
# You can invoke CloudFormation and pass the parameters from a command line like this:
# aws cloudformation create-stack \
# --capabilities CAPABILITY_IAM --capabilities CAPABILITY_NAMED_IAM \
# --template-body "file://prowler-scan-role-with-s3-integration.yml" \
# --stack-name "ProwlerScanRoleWithS3Integration" \
# --parameters "ParameterKey=ExternalId,ParameterValue=your-external-id-here" \
# "ParameterKey=S3IntegrationBucketName,ParameterValue=your-bucket-name" \
# "ParameterKey=S3IntegrationBucketAccount,ParameterValue=123456789012"
Description: |
This template creates the ProwlerScan IAM Role in this account with
all read-only permissions to scan your account for security issues.
Contains two AWS managed policies (SecurityAudit and ViewOnlyAccess) and inline policies.
It sets the trust policy on that IAM Role to permit Prowler to assume that role.
Additionally, this template includes S3 integration permissions that allow Prowler
to store scan reports in your specified S3 bucket with appropriate security controls.
This template can also be used to update an existing ProwlerScan role deployment
since it only adds S3 permissions.
This template is designed to be used in Prowler Cloud, but can also be used in other Prowler deployments.
If you are deploying this template to be used in Prowler Cloud please do not edit the AccountId, IAMPrincipal and ExternalId parameters.
Parameters:
ExternalId:
Description: |
This is the External ID that Prowler will use to assume the role ProwlerScan IAM Role.
Type: String
MinLength: 1
AllowedPattern: ".+"
ConstraintDescription: "ExternalId must not be empty."
AccountId:
Description: |
AWS Account ID that will assume the role created, if you are deploying this template to be used in Prowler Cloud please do not edit this.
Type: String
Default: "232136659152"
MinLength: 12
MaxLength: 12
AllowedPattern: "[0-9]{12}"
ConstraintDescription: "AccountId must be a valid AWS Account ID."
IAMPrincipal:
Description: |
The IAM principal type and name that will be allowed to assume the role created, leave an * for all the IAM principals in your AWS account. If you are deploying this template to be used in Prowler Cloud please do not edit this.
Type: String
Default: role/prowler*
S3IntegrationBucketName:
Description: |
The S3 bucket name where Prowler will store scan reports for your cloud providers.
Type: String
S3IntegrationBucketAccount:
Description: |
The AWS Account ID owner of the S3 Bucket.
Type: String
MinLength: 12
MaxLength: 12
AllowedPattern: "[0-9]{12}"
ConstraintDescription: "S3IntegrationBucketAccount must be a valid 12-digit AWS Account ID."
Resources:
ProwlerScan:
Type: AWS::IAM::Role
Properties:
RoleName: ProwlerScan
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS: !Sub "arn:${AWS::Partition}:iam::${AccountId}:root"
Action: "sts:AssumeRole"
Condition:
StringEquals:
"sts:ExternalId": !Sub ${ExternalId}
StringLike:
"aws:PrincipalArn": !Sub "arn:${AWS::Partition}:iam::${AccountId}:${IAMPrincipal}"
MaxSessionDuration: 3600
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/SecurityAudit"
- "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess"
Policies:
- PolicyName: ProwlerScan
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowMoreReadOnly
Effect: Allow
Action:
- "account:Get*"
- "appstream:Describe*"
- "appstream:List*"
- "backup:List*"
- "bedrock:List*"
- "bedrock:Get*"
- "cloudtrail:GetInsightSelectors"
- "codeartifact:List*"
- "codebuild:BatchGet*"
- "codebuild:ListReportGroups"
- "cognito-idp:GetUserPoolMfaConfig"
- "dlm:Get*"
- "drs:Describe*"
- "ds:Get*"
- "ds:Describe*"
- "ds:List*"
- "dynamodb:GetResourcePolicy"
- "ec2:GetEbsEncryptionByDefault"
- "ec2:GetSnapshotBlockPublicAccessState"
- "ec2:GetInstanceMetadataDefaults"
- "ecr:Describe*"
- "ecr:GetRegistryScanningConfiguration"
- "elasticfilesystem:DescribeBackupPolicy"
- "glue:GetConnections"
- "glue:GetSecurityConfiguration*"
- "glue:SearchTables"
- "lambda:GetFunction*"
- "logs:FilterLogEvents"
- "lightsail:GetRelationalDatabases"
- "macie2:GetMacieSession"
- "macie2:GetAutomatedDiscoveryConfiguration"
- "s3:GetAccountPublicAccessBlock"
- "shield:DescribeProtection"
- "shield:GetSubscriptionState"
- "securityhub:BatchImportFindings"
- "securityhub:GetFindings"
- "servicecatalog:Describe*"
- "servicecatalog:List*"
- "ssm:GetDocument"
- "ssm-incidents:List*"
- "states:ListTagsForResource"
- "support:Describe*"
- "tag:GetTagKeys"
- "wellarchitected:List*"
Resource: "*"
- Sid: AllowAPIGatewayReadOnly
Effect: Allow
Action:
- "apigateway:GET"
Resource:
- "arn:*:apigateway:*::/restapis/*"
- "arn:*:apigateway:*::/apis/*"
- PolicyName: ProwlerS3Integration
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:PutObject"
Resource:
- !Sub "arn:${AWS::Partition}:s3:::${S3IntegrationBucketName}/*"
Condition:
StringEquals:
"s3:ResourceAccount": !Sub ${S3IntegrationBucketAccount}
- Effect: Allow
Action:
- "s3:GetBucketLocation"
Resource:
- !Sub "arn:${AWS::Partition}:s3:::${S3IntegrationBucketName}"
Condition:
StringEquals:
"s3:ResourceAccount": !Sub ${S3IntegrationBucketAccount}
- Effect: Allow
Action:
- "s3:DeleteObject"
Resource:
- !Sub "arn:${AWS::Partition}:s3:::${S3IntegrationBucketName}/*test-prowler-connection.txt"
Condition:
StringEquals:
"s3:ResourceAccount": !Sub ${S3IntegrationBucketAccount}
Tags:
- Key: "Service"
Value: "https://prowler.com"
- Key: "Support"
Value: "support@prowler.com"
- Key: "CloudFormation"
Value: "true"
- Key: "Name"
Value: "ProwlerScan"
Outputs:
ProwlerScanRoleArn:
Description: "ARN of the ProwlerScan IAM Role"
Value: !GetAtt ProwlerScan.Arn
Export:
Name: !Sub "${AWS::StackName}-ProwlerScanRoleArn"

View File

@@ -3,8 +3,8 @@ AWSTemplateFormatVersion: "2010-09-09"
# You can invoke CloudFormation and pass the principal ARN from a command line like this:
# aws cloudformation create-stack \
# --capabilities CAPABILITY_IAM --capabilities CAPABILITY_NAMED_IAM \
# --template-body "file://prowler-pro-saas-scan-role.yaml" \
# --stack-name "ProwlerProSaaSScanRole" \
# --template-body "file://prowler-scan-role.yaml" \
# --stack-name "ProwlerScanRole" \
# --parameters "ParameterKey=ExternalId,ParameterValue=ProvidedExternalID"
Description: |
@@ -12,6 +12,8 @@ Description: |
all read-only permissions to scan your account for security issues.
Contains two AWS managed policies (SecurityAudit and ViewOnlyAccess) and an inline policy.
It sets the trust policy on that IAM Role to permit Prowler to assume that role.
This template is designed to be used in Prowler Cloud, but can also be used in other Prowler deployments.
If you are deploying this template to be used in Prowler Cloud please do not edit the AccountId, IAMPrincipal and ExternalId parameters.
Parameters:
ExternalId:
Description: |
@@ -125,3 +127,10 @@ Resources:
Value: "true"
- Key: "Name"
Value: "ProwlerScan"
Outputs:
ProwlerScanRoleArn:
Description: "ARN of the ProwlerScan IAM Role"
Value: !GetAtt ProwlerScan.Arn
Export:
Name: !Sub "${AWS::StackName}-ProwlerScanRoleArn"

View File

@@ -1,10 +1,47 @@
## Deployment using Terraform
To deploy the Prowler Scan Role in order to allow to scan you AWS account from Prowler, please run the following commands in your terminal:
To deploy the Prowler Scan Role in order to allow scanning your AWS account from Prowler, please run the following commands in your terminal:
1. `terraform init`
2. `terraform plan`
3. `terraform apply`
During the `terraform plan` and `terraform apply` steps you will be asked for an External ID to be configured in the `ProwlerScan` IAM role.
### Variables
- `external_id` (required): External ID for role assumption security
- `account_id` (optional): AWS Account ID that will assume the role (defaults to Prowler Cloud: "232136659152")
- `iam_principal` (optional): IAM principal pattern allowed to assume the role (defaults to Prowler Cloud: "role/prowler*")
- `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` (conditional): S3 bucket owner account ID (required if `enable_s3_integration` is true)
### Usage Examples
#### Basic deployment (without S3 integration):
```bash
terraform apply -var="external_id=your-external-id-here"
```
#### With S3 integration enabled:
```bash
terraform apply \
-var="external_id=your-external-id-here" \
-var="enable_s3_integration=true" \
-var="s3_integration_bucket_name=your-s3-bucket-name" \
-var="s3_integration_bucket_account=123456789012"
```
#### Using terraform.tfvars file:
Create a `terraform.tfvars` file:
```hcl
external_id = "your-external-id-here"
enable_s3_integration = true
s3_integration_bucket_name = "your-s3-bucket-name"
s3_integration_bucket_account = "123456789012"
```
Then run: `terraform apply`
> Note that Terraform will use the AWS credentials of your default profile.

View File

@@ -0,0 +1,4 @@
# Data Sources
###################################
data "aws_partition" "current" {}
data "aws_caller_identity" "current" {}

View File

@@ -1,62 +1,20 @@
# Variables
# Local validation for conditional requirements
###################################
variable "external_id" {
type = string
description = "This is the External ID that Prowler will use to assume the role ProwlerScan IAM Role."
locals {
s3_integration_validation = (
!var.enable_s3_integration ||
(var.enable_s3_integration && var.s3_integration_bucket_name != "" && var.s3_integration_bucket_account != "")
)
}
validation {
condition = length(var.external_id) > 0
error_message = "ExternalId must not be empty."
# Validation check using check block (Terraform 1.5+)
check "s3_integration_requirements" {
assert {
condition = !var.enable_s3_integration || (var.s3_integration_bucket_name != "" && var.s3_integration_bucket_account != "")
error_message = "When enable_s3_integration is true, both s3_integration_bucket_name and s3_integration_bucket_account must be provided and non-empty."
}
}
variable "account_id" {
type = string
description = "AWS Account ID that will assume the role created, if you are deploying this template to be used in Prowler Cloud please do not edit this."
default = "232136659152"
validation {
condition = length(var.account_id) == 12
error_message = "AccountId must be a valid AWS Account ID."
}
}
variable "iam_principal" {
type = string
description = "The IAM principal type and name that will be allowed to assume the role created, leave an * for all the IAM principals in your AWS account. If you are deploying this template to be used in Prowler Cloud please do not edit this."
default = "role/prowler*"
}
##### PLEASE, DO NOT EDIT BELOW THIS LINE #####
# Terraform Provider Configuration
###################################
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.83"
}
}
}
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
"Name" = "ProwlerScan",
"Terraform" = "true",
"Service" = "https://prowler.com",
"Support" = "support@prowler.com"
}
}
}
data "aws_partition" "current" {}
# IAM Role
###################################
data "aws_iam_policy_document" "prowler_assume_role_policy" {
@@ -86,7 +44,6 @@ data "aws_iam_policy_document" "prowler_assume_role_policy" {
resource "aws_iam_role" "prowler_scan" {
name = "ProwlerScan"
assume_role_policy = data.aws_iam_policy_document.prowler_assume_role_policy.json
}
resource "aws_iam_policy" "prowler_scan_policy" {
@@ -109,3 +66,16 @@ resource "aws_iam_role_policy_attachment" "prowler_scan_viewonly_policy_attachme
role = aws_iam_role.prowler_scan.name
policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/job-function/ViewOnlyAccess"
}
# S3 Integration Module
###################################
module "s3_integration" {
count = var.enable_s3_integration ? 1 : 0
source = "./s3-integration"
s3_integration_bucket_name = var.s3_integration_bucket_name
s3_integration_bucket_account = var.s3_integration_bucket_account
prowler_role_name = aws_iam_role.prowler_scan.name
}

View File

@@ -0,0 +1,22 @@
# Outputs
###################################
output "prowler_role_arn" {
description = "ARN of the Prowler scan role"
value = aws_iam_role.prowler_scan.arn
}
output "prowler_role_name" {
description = "Name of the Prowler scan role"
value = aws_iam_role.prowler_scan.name
}
output "external_id" {
description = "External ID used for role assumption"
value = var.external_id
sensitive = true
}
output "s3_integration_enabled" {
description = "Whether S3 integration is enabled"
value = var.enable_s3_integration
}

View File

@@ -0,0 +1 @@
data "aws_partition" "current" {}

View File

@@ -0,0 +1,54 @@
# S3 Integration Policy
###################################
resource "aws_iam_role_policy" "prowler_s3_integration" {
name = "ProwlerS3Integration"
role = var.prowler_role_name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:DeleteObject"
]
Resource = [
"arn:${data.aws_partition.current.partition}:s3:::${var.s3_integration_bucket_name}/*test-prowler-connection.txt"
]
Condition = {
StringEquals = {
"s3:ResourceAccount" = var.s3_integration_bucket_account
}
}
},
{
Effect = "Allow"
Action = [
"s3:PutObject"
]
Resource = [
"arn:${data.aws_partition.current.partition}:s3:::${var.s3_integration_bucket_name}/*"
]
Condition = {
StringEquals = {
"s3:ResourceAccount" = var.s3_integration_bucket_account
}
}
},
{
Effect = "Allow"
Action = [
"s3:GetBucketLocation"
]
Resource = [
"arn:${data.aws_partition.current.partition}:s3:::${var.s3_integration_bucket_name}"
]
Condition = {
StringEquals = {
"s3:ResourceAccount" = var.s3_integration_bucket_account
}
}
}
]
})
}

View File

@@ -0,0 +1,29 @@
variable "s3_integration_bucket_name" {
type = string
description = "The S3 bucket name where Prowler will store scan reports for your cloud providers."
validation {
condition = length(var.s3_integration_bucket_name) > 0
error_message = "s3_integration_bucket_name must not be empty."
}
}
variable "s3_integration_bucket_account" {
type = string
description = "The AWS Account ID owner of the S3 Bucket."
validation {
condition = length(var.s3_integration_bucket_account) == 12 && can(tonumber(var.s3_integration_bucket_account))
error_message = "s3_integration_bucket_account must be a valid 12-digit AWS Account ID."
}
}
variable "prowler_role_name" {
type = string
description = "Name of the Prowler scan IAM role to attach the S3 policy to."
validation {
condition = length(var.prowler_role_name) > 0
error_message = "prowler_role_name must not be empty."
}
}

View File

@@ -0,0 +1,3 @@
terraform {
required_version = ">= 1.5"
}

View File

@@ -0,0 +1,13 @@
# Required variable
external_id = "your-unique-external-id-here"
# Optional Variables
# Prowler Cloud Account
# account_id = "232136659152"
# Prowler Cloud Role
# iam_principal = "role/prowler*"
# S3 Integration (optional)
# enable_s3_integration = true
# s3_bucket_name = "your-prowler-reports-bucket"
# s3_bucket_account = "123456789012"

View File

@@ -0,0 +1,56 @@
# Variables
###################################
variable "external_id" {
type = string
description = "This is the External ID that Prowler will use to assume the role ProwlerScan IAM Role."
validation {
condition = length(var.external_id) > 0
error_message = "ExternalId must not be empty."
}
}
variable "account_id" {
type = string
description = "AWS Account ID that will assume the role created, if you are deploying this template to be used in Prowler Cloud please do not edit this."
default = "232136659152"
validation {
condition = length(var.account_id) == 12
error_message = "AccountId must be a valid AWS Account ID."
}
}
variable "iam_principal" {
type = string
description = "The IAM principal type and name that will be allowed to assume the role created, leave an * for all the IAM principals in your AWS account. If you are deploying this template to be used in Prowler Cloud please do not edit this."
default = "role/prowler*"
}
variable "enable_s3_integration" {
type = bool
description = "Enable S3 integration for storing Prowler scan reports."
default = false
}
variable "s3_integration_bucket_name" {
type = string
description = "The S3 bucket name where Prowler will store scan reports for your cloud providers. Required if enable_s3_integration is true."
default = ""
validation {
condition = length(var.s3_integration_bucket_name) > 0 || var.s3_integration_bucket_name == ""
error_message = "s3_integration_bucket_name must be a valid S3 bucket name."
}
}
variable "s3_integration_bucket_account" {
type = string
description = "The AWS Account ID owner of the S3 Bucket. Required if enable_s3_integration is true."
default = ""
validation {
condition = var.s3_integration_bucket_account == "" || (length(var.s3_integration_bucket_account) == 12 && can(tonumber(var.s3_integration_bucket_account)))
error_message = "s3_integration_bucket_account must be a valid 12-digit AWS Account ID or empty."
}
}

View File

@@ -0,0 +1,23 @@
# Terraform Provider Configuration
###################################
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.83"
}
}
}
provider "aws" {
region = "us-east-1"
default_tags {
tags = {
"Name" = "ProwlerScan",
"Terraform" = "true",
"Service" = "https://prowler.com",
"Support" = "support@prowler.com"
}
}
}