Control: 3.3 Ensure the S3 bucket used to store CloudTrail logs is not publicly accessible
Description
CloudTrail logs a record of every API call made in your AWS account. These logs file are stored in an S3 bucket. It is recommended that the bucket policy or access control list (ACL) applied to the S3 bucket that CloudTrail logs to prevent public access to the CloudTrail logs.
Allowing public access to CloudTrail log content might aid an adversary in identifying weaknesses in the affected account's use or configuration.
Remediation
Perform the following to remove any public access that has been granted to the bucket via an ACL or S3 bucket policy:
From Console
Using Block public access settings.
- Go to Amazon S3 console at S3
- Choose the name of the bucket where your CloudTrail are stored.
- Choose Permissions and then choose Block public access settings.
- Choose Edit, select all four options under
Block all public access
check box, and then choose save changes. - If prompted, enter
confirm
and then choose Confirm.
Using ACL and Bucket Policy settings.
- Go to Amazon S3 console at S3
- Choose the name of the bucket where your CloudTrail are stored.
- Choose Permissions and navigate to
Access control list (ACL)
- The tab shows a list of grants, one row per grant, in the bucket ACL. Each row identifies the grantee and the permissions granted.
- Ensure no rows exists that have the Grantee set to Everyone or the Grantee set to Any Authenticated User.
- If the Edit bucket policy button is present, click it to review the bucket policy.
- Ensure the policy does not contain a Statement having an Effect set to Allow and a
Principal set to
"*"
or{"AWS" : "*"}
Usage
Run the control in your terminal:
powerpipe control run aws_compliance.control.cis_v130_3_3
Snapshot and share results via Turbot Pipes:
powerpipe loginpowerpipe control run aws_compliance.control.cis_v130_3_3 --share
SQL
This control uses a named query:
with public_bucket_data as (-- note the counts are not exactly CORRECT because of the jsonb_array_elements joins,-- but will be non-zero if any matches are foundselect t.s3_bucket_name as name, b.arn, t.region, t.account_id, t.tags, t._ctx, count(acl_grant) filter (where acl_grant -> 'Grantee' ->> 'URI' like '%acs.amazonaws.com/groups/global/AllUsers') as all_user_grants, count(acl_grant) filter (where acl_grant -> 'Grantee' ->> 'URI' like '%acs.amazonaws.com/groups/global/AuthenticatedUsers') as auth_user_grants, count(s) filter (where s ->> 'Effect' = 'Allow' and p = '*' ) as anon_statementsfrom aws_cloudtrail_trail as tleft join aws_s3_bucket as b on t.s3_bucket_name = b.nameleft join jsonb_array_elements(acl -> 'Grants') as acl_grant on trueleft join jsonb_array_elements(policy_std -> 'Statement') as s on trueleft join jsonb_array_elements_text(s -> 'Principal' -> 'AWS') as p on truegroup by t.s3_bucket_name, b.arn, t.region, t.account_id, t.tags, t._ctx)select case when arn is null then 'arn:aws:s3::' || name else arn end as resource, case when arn is null then 'skip' when all_user_grants > 0 then 'alarm' when auth_user_grants > 0 then 'alarm' when anon_statements > 0 then 'alarm' else 'ok' end as status, case when arn is null then name || ' not found in account ' || account_id || '.' when all_user_grants > 0 then name || ' grants access to AllUsers in ACL.' when auth_user_grants > 0 then name || ' grants access to AuthenticatedUsers in ACL.' when anon_statements > 0 then name || ' grants access to AWS:*" in bucket policy.' else name || ' does not grant anonymous access in ACL or bucket policy.' end as reason , region, account_idfrom public_bucket_data;