CloudWatch Log Group is not publicly accessible
CloudWatch Log Groups with resource policies that grant access to any principal are identified. Statements using Principal:"*" or wildcard Resource that reference a log group ARN indicate that the log group is exposed through a public policy.
Risk
Public access to log groups enables unauthorized reading of logs, revealing secrets and operational metadata, harming confidentiality. If broad actions are allowed, attackers can modify subscriptions or logs, undermining integrity and disrupting availability of audit evidence.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks cloudwatch_log_group_not_publicly_accessibleRecommendation
Remove public access from log group resource policies. Replace Principal:"*" and Resource:"*" with narrowly scoped principals and specific ARNs. Grant only necessary actions, apply conditions to constrain use, and enforce least privilege and separation of duties with regular policy reviews.
Remediation
CLI
aws logs delete-resource-policy --policy-name <policy-name>Terraform
resource "aws_cloudwatch_log_resource_policy" "<example_resource_name>" {
policy_name = "<example_resource_name>"
policy_document = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { AWS = "<example_account_id>" } # FIX: restrict Principal (not "*") to avoid public access
Action = "logs:PutSubscriptionFilter"
Resource = "arn:aws:logs:<region>:<account-id>:destination:<example_resource_name>"
}]
})
}NativeIaC
Resources:
<example_resource_name>:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: <example_resource_name>
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: "<example_account_id>" # FIX: restrict to specific account (not *) to prevent public access
Action: logs:PutSubscriptionFilter
Resource: "arn:aws:logs:<region>:<account-id>:destination:<example_resource_name>"Other
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.cloudwatch.logs_client import logs_client
from prowler.providers.aws.services.iam.lib.policy import is_policy_public
class cloudwatch_log_group_not_publicly_accessible(Check):
def execute(self):
findings = []
public_log_groups = []
if (
logs_client.resource_policies is not None
and logs_client.log_groups is not None
):
for resource_policies in logs_client.resource_policies.values():
if resource_policies is not None:
for resource_policy in resource_policies:
if is_policy_public(
resource_policy.policy, logs_client.audited_account
):
for statement in resource_policy.policy.get(
"Statement", []
):
public_resources = statement.get("Resource", [])
if isinstance(public_resources, str):
public_resources = [public_resources]
for resource in public_resources:
for log_group in logs_client.log_groups.values():
if log_group.arn in resource or resource == "*":
public_log_groups.append(log_group.arn)
for log_group in logs_client.log_groups.values():
report = Check_Report_AWS(metadata=self.metadata(), resource=log_group)
report.status = "PASS"
report.status_extended = (
f"Log Group {log_group.name} is not publicly accessible."
)
if log_group.arn in public_log_groups:
report.status = "FAIL"
report.status_extended = (
f"Log Group {log_group.name} is publicly accessible."
)
findings.append(report)
return findings