Fine-tuning access with AWS IAM global condition context keys

AWS IAM policies answer the question “who gets access to what?”. AWS IAM policy conditions answer the more precise question “who gets access to what, when?”. Conditions enhance the expressive power of IAM policies by allowing authors to restrict access control by context. But be warned! They come with surprising gotchas. This blog post describes the AWS global condition context keys (i.e. those prefixed with aws:) and their caveats. Use it as a reference the next time you need to solve advanced IAM access issues.

Restricting by calling service

AWS “service-to-service” API requests are invoked by other AWS API requests. For example, a request to create a CloudFormation stack could cause CloudFormation to make a request to create an EC2 instance. The aws:CalledVia keys were released in early 2020 and can be used to restrict requests based on the chain of services making the request. CalledVia is a multi-valued condition key that can be used to restrict based on any, or all, of the calling services. aws:CalledViaFirst and aws:CalledViaLast are single values with the first and last calling service in the chain.

Image for post
Image for post
Example request service chain with aws:CalledVia values

As of April 2020, CalledVia tags support four services: Athena, CloudFormation, DynamoDB, and KMS. These tags can help enforce the use of CloudFormation or restrict a KMS key for DynamoDB and Athena use cases.

These older keys for restricting by calling service are more limited. aws:ViaAWSService is a boolean that is set to true for all “service-to-service” requests and is often used with other keys to distinguish direct versus indirect access. aws:SourceAccount and aws:SourceArn can be used to check the calling service account or resource, but only look at the direct caller instead of the entire call chain. These condition keys can be useful for preventing confused deputy vulnerabilities.

Restricting by network

These conditions work on the request’s network origin. Be aware that aws:SourceIp and aws:SourceVpc condition keys won’t have the end-user’s network context with “service-to-service” requests. You can check if ViaAWSService is false to exclude these cases. If you’re using a VPC Endpoint, use aws:SourceVpce and aws:VpcSourceIp instead.

Restricting by header

These keys can be set arbitrarily by any caller and have limited security utility. Some practical examples: aws:Referer can be used to prevent hotlinking S3 files, and aws:UserAgent can be used as a defensive option for preventing SSRF of IAM role credentials.

Restricting by transport

Most examples use aws:SecureTransport to enforce S3 object encryption in transit and prevent plaintext HTTP access. It’s not common elsewhere because AWS API clients are likely to use TLS by default, but you can see it applied globally for engineer IAM policies in the open-source but deprecated self-service-iam project from Coinbase.

Restricting by region

Service-control policies use aws:requestedRegion to limit access to regions at the AWS account level. Remember that some services like IAM, Route 53, and CloudFront are global services and must be excluded.

Restricting by request time

Use the aws:CurrentTime and aws:EpochTime condition keys if you have time-based access requirements. I couldn’t find any general use cases documented for these keys.

Restricting by credential time

aws:TokenIssueTime is used in the IAM policy created by the AWS console feature to “revoke sessions” for an IAM role. Remember that users can also have session tokens, so if you intend invalidate a IAM user access key, use this key to invalidate any temporary sessions it has created.

aws:MultiFactorAuthAge can be used for protecting extra-sensitive calls that need recent evidence of MFA.

Restricting by credential type

By default, the AWS console requires MFA for all users with configured MFA devices. Requiring it for the API requires using the aws:MultiFactorAuthPresent key. This condition key can be confusing because requests made with static IAM user access keys don’t pass it. Here’s a summary of the different conditional snafus:

aws:MultiFactorAuthPresent conditions that do and don’t prevent long-term access keys

Restricting by tag

You can use tags to support attribute-based access control. Use the aws:ResourceTag key for resource-based attributes like data classification or team ownership on S3 buckets or EC2 instances. aws:TagKeys and aws:RequestTag restrict tag keys and values in API calls that modify tags.

For principal-based attributes, such as those that may be propagated from a federated identity provider, you can use the PrincipalTag described below.

Restricting by principal

IAM policies associated with users, roles, or groups are principal-based. Resource-based IAM policies apply to AWS resources, so that they need to specify a Principal condition in their policy.

For broad definitions, you can specify the account (aws:PrincipalAccount), organization (aws:PrincipalOrgID, aws:PrincipalOrgPaths), or principal type (aws:PrincipalType).

aws:userid and aws:username are older and less useful — aws:userid has an esoteric format, and aws:username is only set for IAM users. To get specific about a principal, use the aws:PrincipalArn, although the documentation is sparse. As mentioned earlier, the aws:PrincipalTag is the tool for attribute-based policies that scale over time as employees come and go.

In addition to the global condition context keys, there are service-specific condition context keys. While these tools aren’t used often, they can help draft flexible policies that solve unusual requirements. How do you use condition context keys? Let me know on Twitter.

Security for the people.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store