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:
- *
the AssumeRole APIfrom A to a role in B whose trust policy names A. Subsequent calls use the session credentials and route to B. See Multi-Account / AssumeRole. - *Resource-based policy on the target naming A as a principal directly. Identity policy in A still has to allow the action. Bucket policy, KMS key policy, SQS queue policy, SNS topic policy, Lambda function policy, and EventBridge event-bus policy all follow this shape.
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:
- *
aws:PrincipalAccount. the 12-digit ID of the calling account - *
aws:ResourceAccount. the 12-digit ID of the resource's owning account, parsed from the ARN - *
aws:SourceAccount. set when an AWS service calls another on the customer's behalf (populated from thex-amz-source-accountheader) - *
aws:PrincipalOrgID. the organization ID of the caller's account, when the account is part of an org - *
aws:PrincipalOrgPaths. the OU path of the caller's account in the org tree - *
aws:ResourceOrgID. the organization ID of the resource's owning account, when both are in the same org
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:
- *
s3:DataAccessPointArn. the access point's ARN - *
s3:DataAccessPointAccount. the 12-digit ID of the access point's owning account - *
s3:AccessPointNetworkOrigin.InternetorVPC
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:
- *
Destination.Accountset in the rule names the owner of the destination bucket. - * When the destination policy denies the source account (or has no statement allowing it), the source object's
ReplicationStatusflips toFAILEDinstead ofCOMPLETED.
Evaluation order
LocalEmu's policy evaluator follows the AWS rules:
- Explicit Deny in any policy (identity, resource, or boundary) wins.
- If a resource policy explicitly allows the caller for the action and resource, the call is allowed.
- Otherwise the call needs an Allow in the caller's identity policies (subject to the permission boundary, if set).
- For S3 Access Point requests, the bucket policy is consulted in addition to the access point policy.
See also
- *Multi-Account. how access keys map to accounts, account registry, AssumeRole
- *Organizations. org tree, member accounts, the auto-seeded access role
- *IAM Enforcement. enforcement modes and policy evaluation details
- *S3. access points, replication, bucket policies