Skip to main content

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

  1. Sign in to the AWS IAM Console

  2. In the left navigation, click Policies

  3. Filter to show only Customer managed policies (use the filter dropdown)

  4. For each policy, click the policy name to open it

  5. Click the JSON tab to view the policy document

  6. Search for cloudtrail:* in the policy. If found, this policy grants full CloudTrail access

  7. Click Edit to modify the policy

  8. Replace cloudtrail:* with only the specific actions needed. Common read-only actions include:

    • cloudtrail:DescribeTrails
    • cloudtrail:GetTrailStatus
    • cloudtrail:LookupEvents
    • cloudtrail:GetEventSelectors
  9. Click Next, then Save changes

  10. 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

  1. 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": "*"
}
]
}
  1. 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 DeleteTrail and StopLogging
  • 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:

  1. Go back to the IAM Console and review each modified policy
  2. Confirm that cloudtrail:* no longer appears in the JSON
  3. 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:DeleteTrail and cloudtrail:StopLogging across all accounts.