Ensure IAM Policies Do Not Grant Full CloudTrail Access
Overview
This check identifies customer-managed IAM policies that grant unrestricted CloudTrail permissions using the cloudtrail:* wildcard action. CloudTrail is AWS's audit logging service, so overly permissive access to it can undermine your security monitoring.
Risk
When IAM policies allow cloudtrail:*, principals with that policy can:
- Stop logging - Disable CloudTrail to hide malicious activity
- Delete trails - Remove audit evidence permanently
- Modify trails - Change where logs are stored or what gets logged
- Access all events - Read sensitive activity data for reconnaissance
This undermines the integrity of your audit trail and can help attackers cover their tracks.
Remediation Steps
Prerequisites
- Access to the AWS Console with permission to view and edit IAM policies
- Knowledge of which specific CloudTrail actions your users actually need
AWS Console Method
-
Sign in to the AWS IAM Console
-
In the left navigation, click Policies
-
Filter to show only Customer managed policies (use the filter dropdown)
-
For each policy, click the policy name to open it
-
Click the JSON tab to view the policy document
-
Search for
cloudtrail:*in the policy. If found, this policy grants full CloudTrail access -
Click Edit to modify the policy
-
Replace
cloudtrail:*with only the specific actions needed. Common read-only actions include:cloudtrail:DescribeTrailscloudtrail:GetTrailStatuscloudtrail:LookupEventscloudtrail:GetEventSelectors
-
Click Next, then Save changes
-
Repeat for all customer-managed policies containing
cloudtrail:*
AWS CLI (optional)
Find policies with full CloudTrail access
List all customer-managed policies:
aws iam list-policies \
--scope Local \
--query 'Policies[*].[PolicyName,Arn,DefaultVersionId]' \
--output table \
--region us-east-1
For each policy, retrieve the policy document to check for cloudtrail:*:
aws iam get-policy-version \
--policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/<POLICY_NAME> \
--version-id <VERSION_ID> \
--query 'PolicyVersion.Document' \
--region us-east-1
Update a policy with restricted permissions
- Create a new policy document file (
updated-policy.json) with specific CloudTrail actions:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CloudTrailReadOnly",
"Effect": "Allow",
"Action": [
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudtrail:LookupEvents",
"cloudtrail:GetEventSelectors",
"cloudtrail:ListTrails"
],
"Resource": "*"
}
]
}
- Create a new policy version with the updated document:
aws iam create-policy-version \
--policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/<POLICY_NAME> \
--policy-document file://updated-policy.json \
--set-as-default \
--region us-east-1
Note: Managed policies can have up to 5 versions. If you already have 5 versions, delete an old one first using aws iam delete-policy-version.
CloudFormation (optional)
Example: Restricted CloudTrail Policy
AWSTemplateFormatVersion: '2010-09-09'
Description: IAM policy with restricted CloudTrail permissions
Resources:
CloudTrailReadOnlyPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: CloudTrailReadOnlyPolicy
Description: Allows read-only access to CloudTrail
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: CloudTrailReadOnly
Effect: Allow
Action:
- cloudtrail:DescribeTrails
- cloudtrail:GetTrailStatus
- cloudtrail:LookupEvents
- cloudtrail:GetEventSelectors
- cloudtrail:ListTrails
- cloudtrail:GetTrail
Resource: '*'
CloudTrailAdminPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: CloudTrailAdminPolicy
Description: Allows specific CloudTrail admin actions (not full access)
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: CloudTrailManagement
Effect: Allow
Action:
- cloudtrail:CreateTrail
- cloudtrail:UpdateTrail
- cloudtrail:StartLogging
- cloudtrail:PutEventSelectors
Resource: !Sub 'arn:aws:cloudtrail:*:${AWS::AccountId}:trail/*'
- Sid: CloudTrailReadOnly
Effect: Allow
Action:
- cloudtrail:DescribeTrails
- cloudtrail:GetTrailStatus
- cloudtrail:ListTrails
Resource: '*'
Outputs:
ReadOnlyPolicyArn:
Description: ARN of the CloudTrail read-only policy
Value: !Ref CloudTrailReadOnlyPolicy
AdminPolicyArn:
Description: ARN of the CloudTrail admin policy
Value: !Ref CloudTrailAdminPolicy
Key points:
- The read-only policy allows viewing trails and events but not modifying them
- The admin policy allows specific management actions but explicitly excludes dangerous ones like
DeleteTrailandStopLogging - Both policies avoid using
cloudtrail:*
Terraform (optional)
Example: Restricted CloudTrail Policy
# CloudTrail read-only policy
resource "aws_iam_policy" "cloudtrail_readonly" {
name = "CloudTrailReadOnlyPolicy"
description = "Allows read-only access to CloudTrail"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "CloudTrailReadOnly"
Effect = "Allow"
Action = [
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudtrail:LookupEvents",
"cloudtrail:GetEventSelectors",
"cloudtrail:ListTrails",
"cloudtrail:GetTrail"
]
Resource = "*"
}
]
})
}
# CloudTrail admin policy (specific actions, not full access)
resource "aws_iam_policy" "cloudtrail_admin" {
name = "CloudTrailAdminPolicy"
description = "Allows specific CloudTrail management actions"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "CloudTrailManagement"
Effect = "Allow"
Action = [
"cloudtrail:CreateTrail",
"cloudtrail:UpdateTrail",
"cloudtrail:StartLogging",
"cloudtrail:PutEventSelectors"
]
Resource = "arn:aws:cloudtrail:*:${data.aws_caller_identity.current.account_id}:trail/*"
},
{
Sid = "CloudTrailReadOnly"
Effect = "Allow"
Action = [
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudtrail:ListTrails"
]
Resource = "*"
}
]
})
}
data "aws_caller_identity" "current" {}
output "readonly_policy_arn" {
description = "ARN of the CloudTrail read-only policy"
value = aws_iam_policy.cloudtrail_readonly.arn
}
output "admin_policy_arn" {
description = "ARN of the CloudTrail admin policy"
value = aws_iam_policy.cloudtrail_admin.arn
}
Verification
After updating the policies:
- Go back to the IAM Console and review each modified policy
- Confirm that
cloudtrail:*no longer appears in the JSON - Re-run the Prowler check to confirm the finding is resolved:
prowler aws --check iam_policy_no_full_access_to_cloudtrail --region us-east-1
Advanced verification with AWS CLI
List all customer-managed policies and check each for cloudtrail:*:
# Get all customer-managed policy ARNs
POLICY_ARNS=$(aws iam list-policies --scope Local --query 'Policies[*].Arn' --output text --region us-east-1)
# Check each policy for cloudtrail:*
for ARN in $POLICY_ARNS; do
VERSION=$(aws iam get-policy --policy-arn "$ARN" --query 'Policy.DefaultVersionId' --output text --region us-east-1)
DOCUMENT=$(aws iam get-policy-version --policy-arn "$ARN" --version-id "$VERSION" --query 'PolicyVersion.Document' --output text --region us-east-1)
if echo "$DOCUMENT" | grep -q "cloudtrail:\*"; then
echo "FAIL: $ARN contains cloudtrail:*"
fi
done
Additional Resources
Notes
-
AWS-managed policies: This check only applies to customer-managed policies. AWS-managed policies (like
AWSCloudTrailFullAccess) are maintained by AWS and cannot be modified. If you need to restrict access, avoid attaching such policies and create your own with specific permissions. -
Inline policies: This check focuses on managed policies. Inline policies attached directly to users, groups, or roles should also be reviewed separately.
-
Separation of duties: Consider separating CloudTrail read access (for security analysts) from CloudTrail management access (for administrators). This limits who can stop logging or delete trails.
-
Service Control Policies (SCPs): For organization-wide protection, consider using SCPs to deny dangerous CloudTrail actions like
cloudtrail:DeleteTrailandcloudtrail:StopLoggingacross all accounts.