Docs / Cross-Account Access

Cross-Account Access

With IAM_ENFORCEMENT=1, LocalEmu evaluates cross-account requests the AWS way. The caller's identity policies still gate the action, AND the target resource's resource-based policy is consulted, AND the AWS-global condition keys (aws:PrincipalOrgID, aws:PrincipalAccount, aws:ResourceAccount, aws:SourceAccount) are populated for the policy evaluator.

Two paths to cross-account access

AWS offers two mechanisms for letting account A operate against resources in account B. LocalEmu supports both:

Resource policy on an S3 bucket

The most common cross-account pattern. Account A grants account B read access on a specific bucket by naming B's account as the principal:

# Account A grants account B read access on a bucket
$ AWS_ACCESS_KEY_ID=111111111111 awsemu s3api put-bucket-policy \
    --bucket shared-data \
    --policy '{
  "Version":"2012-10-17",
  "Statement":[{
    "Effect":"Allow",
    "Principal":{"AWS":"arn:aws:iam::222222222222:root"},
    "Action":"s3:GetObject",
    "Resource":"arn:aws:s3:::shared-data/*"
  }]
}'

# Account B reads, IAM enforcement allows the call
$ AWS_ACCESS_KEY_ID=222222222222 \
  awsemu s3api get-object --bucket shared-data --key report.csv /tmp/out
{"ContentLength": 1234, ...}

Condition keys for cross-account scoping

LocalEmu populates the AWS-global condition keys on every cross-account request, so resource policies can use them to scope access:

The canonical "allow only members of my organization" pattern uses aws:PrincipalOrgID.

# Allow only callers whose account is in our organization
$ awsemu s3api put-bucket-policy --bucket org-data --policy '{
  "Version":"2012-10-17",
  "Statement":[{
    "Effect":"Allow",
    "Principal":"*",
    "Action":"s3:GetObject",
    "Resource":"arn:aws:s3:::org-data/*",
    "Condition":{
      "StringEquals":{"aws:PrincipalOrgID":"o-au9g8di70o"}
    }
  }]
}'

S3 Access Point delegation

Three additional condition keys are populated when an S3 request transits an access point:

These power the AWS-canonical pattern of letting the bucket delegate access decisions to its access points. The bucket policy says "allow any principal whose request arrives through an access point in this account":

# Bucket policy delegates access decisions to access points in this account
$ awsemu s3api put-bucket-policy --bucket finance-data --policy '{
  "Version":"2012-10-17",
  "Statement":[{
    "Sid":"DelegateToAccessPoints",
    "Effect":"Allow",
    "Principal":{"AWS":"*"},
    "Action":"s3:GetObject",
    "Resource":"arn:aws:s3:::finance-data/*",
    "Condition":{
      "StringEquals":{"s3:DataAccessPointAccount":"000000000000"}
    }
  }]
}'

# Direct read of the bucket: denied (the condition is not satisfied)
$ AWS_ACCESS_KEY_ID=<low-priv> awsemu s3api get-object \
    --bucket finance-data --key q3.xlsx /tmp/out
AccessDenied

# Read through an access point in account 000000000000: allowed
$ AWS_ACCESS_KEY_ID=<low-priv> awsemu s3api get-object \
    --bucket arn:aws:s3:us-east-1:000000000000:accesspoint/finance-ap \
    --key q3.xlsx /tmp/out
{"ContentLength": 1234, ...}

The access point itself carries a policy that scopes which principals can read through it:

$ awsemu s3control put-access-point-policy \
    --account-id 000000000000 --name finance-ap --policy '{
  "Version":"2012-10-17",
  "Statement":[{
    "Effect":"Allow",
    "Principal":{"AWS":"*"},
    "Action":"s3:GetObject",
    "Resource":"arn:aws:s3:us-east-1:000000000000:accesspoint/finance-ap/object/*"
  }]
}'

See S3 / Access Points for the routing forms (ARN, alias, hostname) and the full addressing matrix.

Resource policies on other services

Same shape applies to KMS key policies, SQS queue policies, SNS topic policies, Lambda function policies, and EventBridge event-bus policies. Example: granting cross-account sqs:SendMessage.

# Account A grants account B SendMessage on its queue
$ AWS_ACCESS_KEY_ID=111111111111 awsemu sqs set-queue-attributes \
    --queue-url http://sqs.us-east-1.localhost:4566/111111111111/work \
    --attributes '{
      "Policy": "{
        \"Version\":\"2012-10-17\",
        \"Statement\":[{
          \"Effect\":\"Allow\",
          \"Principal\":{\"AWS\":\"arn:aws:iam::222222222222:root\"},
          \"Action\":\"sqs:SendMessage\",
          \"Resource\":\"arn:aws:sqs:us-east-1:111111111111:work\"
        }]
      }"
    }'

Cross-account S3 replication

When an S3 replication rule's Destination.Bucket is in a different account, LocalEmu looks up the destination bucket in the named account and consults its bucket policy through the same cross-account evaluator. The destination owner has to allow the source account, exactly as on real AWS:

Evaluation order

LocalEmu's policy evaluator follows the AWS rules:

  1. Explicit Deny in any policy (identity, resource, or boundary) wins.
  2. If a resource policy explicitly allows the caller for the action and resource, the call is allowed.
  3. Otherwise the call needs an Allow in the caller's identity policies (subject to the permission boundary, if set).
  4. For S3 Access Point requests, the bucket policy is consulted in addition to the access point policy.

See also