5 IAM Condition Keys That Prevent the Mistakes Wildcards Create
An IAM policy with Action: "s3:*" and Resource: "*" is an obvious problem. You can spot it from across the room.
The harder problems are the policies that look reasonable. Action: "s3:GetObject" on Resource: "*" — that’s just one action, right? But it means the role can read objects from every S3 bucket in the account, including the one where CloudTrail logs land and the one the billing team uses for customer invoices.
Condition keys are how you write policies that are broad where they need to be and tight everywhere else. They add a second dimension to every permission statement: not just what this role can do, but under what circumstances it can do it.
Here are five condition keys that catch the most common IAM overpermission patterns.
1. aws:SourceIp — Restrict by Network Origin
The most straightforward condition. If a role should only be used from your office VPN or a specific VPC, add an IP restriction.
"Condition": {
"IpAddress": {
"aws:SourceIp": "10.0.0.0/8"
}
}
Enter fullscreen mode Exit fullscreen mode
This is useful for break-glass admin roles and any permission that should never be called from outside your network. Note that aws:SourceIp checks the original requester’s IP — if the call comes through a VPC endpoint or AWS service, you might want aws:VpcSourceIp instead.
2. aws:RequestedRegion — Lock to Specific Regions
If your infrastructure only runs in us-east-1 and eu-west-1, a role that can create resources in ap-southeast-1 is a role you don’t need. aws:RequestedRegion prevents actions in regions you don’t use, which limits blast radius if credentials leak.
"Condition": {
"StringEquals": {
"aws:RequestedRegion": ["us-east-1", "eu-west-1"]
}
}
Enter fullscreen mode Exit fullscreen mode
This is also a compliance win — many frameworks expect you to demonstrate that production workloads only run in approved regions.
3. iam:PassedToService — Scope PassRole
iam:PassRole is one of the most dangerous permissions in IAM. It controls which principals can pass which roles to which services. A wildcard PassRole on Resource: "*" means the principal can attach any role — including AdministratorAccess — to any service it can launch.
Add iam:PassedToService to restrict which AWS services can receive a passed role:
"Condition": {
"StringEquals": {
"iam:PassedToService": "lambda.amazonaws.com"
}
}
Enter fullscreen mode Exit fullscreen mode
Now the principal can still pass roles, but only to Lambda. If it’s a CI/CD role that deploys functions, that’s exactly what you want. If the role gets compromised, the attacker can’t use it to launch an EC2 instance with an admin instance profile.
4. aws:ResourceTag + aws:PrincipalTag — Attribute-Based Access Control
Tag-based conditions scale better than listing individual resource ARNs. Instead of maintaining a list of every EC2 instance a role can terminate, tag your instances by environment and write the condition once:
"Condition": {
"StringEquals": {
"aws:ResourceTag/Environment": "staging"
}
}
Enter fullscreen mode Exit fullscreen mode
Pair it with aws:PrincipalTag on the role side, and you can write policies that say “users in the Engineering department can terminate instances tagged with Engineering.” That’s ABAC — attribute-based access control — and it’s how teams with 200+ roles keep permissions manageable without drowning in policy length limits.
5. s3:ResourceAccount — Cross-Account S3 Access
If a role in account A needs to read objects from a bucket in account B, s3:GetObject on Resource: "*" lets it read from every account that grants it access — not just account B. An overly permissive bucket policy in a third account suddenly becomes readable.
"Condition": {
"StringEquals": {
"s3:ResourceAccount": "222222222222"
}
}
Enter fullscreen mode Exit fullscreen mode
Now the role can only access S3 objects in account B, even if other accounts have open bucket policies. This matters when you’re managing cross-account access across an organization with dozens of accounts and hundreds of buckets.
Finding Policies That Miss These
The problem with condition keys is that they’re invisible until you go looking. An IAM policy with no conditions looks exactly as “valid” as one with them — both return 200 OK from the API, and neither throws an error until the wrong access happens.
Shieldly’s free AI-Powered demo at shieldly.io/app/iam flags policies that are missing condition keys where they’d reduce risk. Paste a JSON policy and you’ll see which statements would benefit from IP restrictions, PassedToService scoping, or resource tag conditions — no signup needed.
For teams: the CLI (@shieldly/cli), GitHub Action, CDK Construct (@shieldly/cdk-guard), and VS Code extension all surface the same findings in your existing workflow.
Condition keys don’t make your policies longer. They make them more precise. And precision is what separates “nobody’s found it yet” from “there’s nothing to find.”
답글 남기기