mediumAdvancedSymptom-first#s3#cross-account#bucket-policy#iam

Cross-account S3 access blocked — policy mismatch

A role in account A cannot read a bucket owned by account B

Likely source:securityconfiguration

Focused incident mode — strips away everything but this runbook.

Symptoms

  • A role in account A cannot read a bucket owned by account B
  • Works from one account but not from the delegated role
  • CloudTrail shows both slips in S3 + IAM 'no matching statement'

Evidence to grab

Collect these artifacts before you change anything — they prove (or rule out) each cause.

  • Caller identity

    aws sts get-caller-identity
  • Bucket policy of the owner

    aws s3api get-bucket-policy --bucket <BUCKET> --region <REGION>

Diagnostic flow

Run the steps in order, marking each claim confirmed or refuted. The evidence you gather feeds the ranked causes below.

Guided decision tree is ready.

Steps with commands, expected output, and pass/fail branches.

Ranked root causes

Evidence checklist

  • get-bucket-policy has no matching Principal/Action for the caller
  • The policy references only the owner account or an explicit Deny

Confirm before fixing

aws s3api get-bucket-policy --bucket <BUCKET> --query Policy --output text | grep -A3 -B3 '<CALLER-ACCOUNT>'
READ

Find the caller in the policy

Expected: Either a grant statement or nothing

Fix

aws s3api put-bucket-policy --bucket <BUCKET> --policy file://cross-account-policy.json
CHANGE

Add the cross-account grant (Principal = caller account/role, s3:GetObject on arn:aws:s3:::<BUCKET>/*)

Resolution runbook

Apply from top to bottom after the cause is confirmed. Review every command before running it — risk tags are shown on each.

1
aws sts get-caller-identity
READ

Pin the caller

2
aws s3api get-bucket-policy --bucket <BUCKET> --query Policy --output text
READ

Pin the owner's side

3
aws s3api put-bucket-policy --bucket <BUCKET> --policy file://cross-account-policy.json
CHANGE

Add the grant on the owning bucket

4
aws s3api head-object --bucket <BUCKET> --key <KEY>
READ

Retry from the caller's credentials

Expected: 200 or the resolved permission error

Prevention

  • Model cross-account access as policy pairs and validate both in CI (IAM Access Analyzer).
  • Prefer org-level principals and aws:PrincipalOrgID for durable grants.
  • Share KMS keys and their policy grants when SSE-KMS objects move between accounts.

Commands marked READ CHANGE DESTRUCTIVE — never run DESTRUCTIVE commands against production data you cannot restore. Copy → verify → execute.