Skip to content

Commit b517efb

Browse files
committed
docs(aws-iam): add SourceAccount guidance for service principals
- Document aws:SourceAccount/aws:SourceArn conditions for cross-service confused deputy prevention - Add skill reference page for cross-service confused deputy pattern
1 parent 6682d41 commit b517efb

3 files changed

Lines changed: 107 additions & 0 deletions

File tree

rules/412-aws-iam.mdc

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,43 @@ If a vendor assumes a role in your account, require an `ExternalId` and lock to
190190
> [!NOTE]
191191
> Use `ExternalId` when you are protecting against the **confused deputy** problem (common in SaaS integrations).
192192

193+
#### Cross-service confused deputy prevention (resource policies): `aws:SourceAccount` / `aws:SourceArn`
194+
195+
When a **resource-based policy** grants access to an **AWS service principal** (for example,
196+
`cloudtrail.amazonaws.com`, `events.amazonaws.com`, `sns.amazonaws.com`), the policy is trusting the
197+
service principal — not the human/role that configured the calling service.
198+
199+
To reduce confused-deputy risk, restrict the service principal with global condition keys:
200+
201+
- `aws:SourceAccount` (allow only when acting on behalf of a specific AWS account)
202+
- `aws:SourceArn` (allow only when acting on behalf of a specific AWS resource ARN)
203+
204+
AWS guidance: `https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html`
205+
206+
Example (CloudTrail writing to S3; restrict by source account):
207+
208+
```json
209+
{
210+
"Version": "2012-10-17",
211+
"Statement": [
212+
{
213+
"Sid": "CloudTrailAclCheck",
214+
"Effect": "Allow",
215+
"Principal": { "Service": "cloudtrail.amazonaws.com" },
216+
"Action": "s3:GetBucketAcl",
217+
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket1",
218+
"Condition": {
219+
"StringEquals": { "aws:SourceAccount": "111122223333" }
220+
}
221+
}
222+
]
223+
}
224+
```
225+
226+
> [!IMPORTANT]
227+
> Prefer **both** `aws:SourceAccount` and `aws:SourceArn` where supported by the calling service.
228+
> Check the specific service’s docs for supported keys.
229+
193230
## Identity-based vs resource-based policies (quick rules)
194231

195232
- **Identity-based** policies generally do **not** specify `Principal` (it's implicit: the attached user/role)

skills/aws-iam/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ Use this skill when you are working on:
3838

3939
- `references/iam-authorization-model.md`
4040
- `references/cross-account-and-externalid.md`
41+
- `references/cross-service-confused-deputy.md`
4142
- `references/kms-key-policy-and-grants.md`
4243
- `references/eks-pod-identity-abac-and-lattice.md`
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Cross-service confused deputy prevention (`aws:SourceAccount`, `aws:SourceArn`)
2+
3+
When a **resource-based policy** grants access to an **AWS service principal** (for example,
4+
`cloudtrail.amazonaws.com`, `events.amazonaws.com`, `sns.amazonaws.com`), the policy authorizes the
5+
service principal — not the actor that configured the calling service.
6+
7+
If the policy is missing constraints, an unauthorized actor can sometimes abuse that trust relationship
8+
(the **confused deputy** problem).
9+
10+
## Recommended pattern
11+
12+
- **Always add conditions** when granting access to an AWS service principal in a resource policy
13+
- Prefer using **both**:
14+
- `aws:SourceAccount` to scope the caller to an expected AWS account
15+
- `aws:SourceArn` to scope the caller to an expected AWS resource ARN
16+
- Where appropriate (org-wide guardrails), consider:
17+
- `aws:SourceOrgID`
18+
- `aws:SourceOrgPaths`
19+
20+
## Example: S3 bucket policy for CloudTrail (scope by source account)
21+
22+
```json
23+
{
24+
"Version": "2012-10-17",
25+
"Statement": [
26+
{
27+
"Sid": "CloudTrailAclCheck",
28+
"Effect": "Allow",
29+
"Principal": { "Service": "cloudtrail.amazonaws.com" },
30+
"Action": "s3:GetBucketAcl",
31+
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket1",
32+
"Condition": {
33+
"StringEquals": { "aws:SourceAccount": "111122223333" }
34+
}
35+
}
36+
]
37+
}
38+
```
39+
40+
## Example: S3 bucket policy for an AWS service (scope by source ARN)
41+
42+
```json
43+
{
44+
"Version": "2012-10-17",
45+
"Statement": [
46+
{
47+
"Effect": "Allow",
48+
"Principal": { "Service": "appstream.amazonaws.com" },
49+
"Action": "s3:GetObject",
50+
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket2/examplefile.psh",
51+
"Condition": {
52+
"ArnEquals": {
53+
"aws:SourceArn": "arn:aws:appstream:us-east-1:111122223333:fleet/ExampleFleetName"
54+
}
55+
}
56+
}
57+
]
58+
}
59+
```
60+
61+
## Notes
62+
63+
- Not every AWS service supports every key in every integration. Use the service docs as source of truth.
64+
- For KMS specifically, key policies/grants often have additional service-specific controls.
65+
66+
## References
67+
68+
- Confused deputy (cross-account + cross-service): `https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html`
69+
- Global condition keys (including `aws:SourceAccount`, `aws:SourceArn`): `https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html`

0 commit comments

Comments
 (0)