Facebook Google Plus Twitter LinkedIn YouTube RSS Menu Search Resource - BlogResource - WebinarResource - ReportResource - Eventicons_066 icons_067icons_068icons_069icons_070

Tenable Blog

Subscribe

Using AWS Condition Context Keys to Reduce Risk: A Least Privilege Cheat Sheet

Tenable Cloud Security

As strong advocates of least privilege, we believe these AWS keys can be quite effective — if they can be easily understood. Here’s a handy guide.

Amazon recently published in AWS’s security blog a blog post by AWS’s Ilya Epshteyn and Harsha Sharma that explains how and when to use AWS condition context keys such as aws:PrincipalIsAWSService, aws:ViaAWSService and aws:CalledVia to better secure the access to AWS resources. After a close read of the referenced post and others on the topic, like this one, and a review of AWS’s documentation, we decided to create an easy reference for using these keys.

As strong advocates of least privilege, we believe these AWS keys can be quite effective if they can be easily understood. And we believe it's possible to understand them if they’re explained in a straightforward manner. We hope this cheat sheet achieves that.

To make things easier, we’ll look at the aws:CalledVia* and aws:ViaAWSService keys separately from the aws:PrincipalIsAWSService key — as they are designed to perform very different validations. In this post, we address the two different purposes, specify each key and its relevant use cases, and provide an example of how to use it in a condition. Note that we have intentionally oversimplified the examples for ease of understanding.

Make sure access is cone by AWS service directly

Let’s kick things off with the new key in town. The recently announced aws:PrincipalIsAWSService key is meant to allow you to create a simple data perimeter around certain areas of your cloud environment. The perimeter only allow access directly for service principals which are AWS services. The most common example of such access can be CloudTrail writing to Amazon Simple Storage Service (S3) buckets (with the service principal “cloudtrail.amazonaws.com”).

The key aws:PrincipalIsAWSService indicates when this is actually the case or not, and allows limiting the use of a resource to only direct use by AWS services, for example, by applying it on a resource-based policy.

As the example below shows, if you have an S3 bucket you want to be accessible only to AWS services (e.g. CloudTrail) you can use a “deny” on anything which does not have “true” flagged on this context key .

Key(s) to use: aws:PrincipalIsAWSService condition example:

{
    "Effect":"Deny",
    "Condition":{
        "Bool":{
            "aws:PrincipalIsAWSService":"false"
        }
    }
} 

Limit principals from independently accessing services required by other services

aws:PrincipalIsAWSService wasn’t released in a vacuum, and while it’s pretty easy to understand on its own, the confusion usually comes from the differences between it and some existing keys, specifically the aws:CalledVia* and aws:ViaAWSService keys.

The purpose of the aws:CalledVia* and aws:ViaAWSService keys is to make sure that access to certain resources can be done only by an AWS service using the identity of a principal which triggered an action on it — or, better yet, by a specific AWS service for which this is the case. This is opposed to the principal calling it independently.

When a principal such as an IAM user or role needs to use AWS services which themselves have to make subsequent calls to other AWS services, access to the latter must also be given to the principal as the subsequent calls are done using the principal’s credentials.

For a simple example, consider a call to reading a piece of data in a DynamoDB table, encrypted with a KMS key. In order to decrypt the data, DynamoDB will have to call kms:Decrypt — and this will be done using the credentials of the principal that asked for the data to be read from the DynamoDB table. Therefore, that principal will have to have permissions to the DynamoDB table AND to kms:Decrypt on the KMS key which has to be used to decrypt it.

This could get more elaborate, such as in an example AWS provides in its documentation: using CloudFormation to read and write from a DynamoDB table, and DynamoDB then uses encryption supplied by KMS. CloudFormation and DynamoDB will use the credentials of the original principal to make subsequent calls, so the principal needs access to DynamoDB and KMS, respectively. Here is a short description of what each of the keys in this use case is meant to do:

  • aws:CalledVia — Will hold a list with the service principal values that made subsequent calls on behalf of the IAM user, in the order in which they were made
  • aws:CalledViaFirst — Will hold the service principal value of the first AWS service that started the subsequent calls
  • aws:CalledViaLast — Will hold the service principal value of the most recent AWS service via which a call was made
  • aws:ViaAWSService — Is a boolean that indicates if the call was made by an AWS service using the IAM user’s credentials (opposed to directly by the service principal, as we discuss in the next section)

In the scenarios below, our goal is to make sure that a call that needs to be made by an AWS service using a principal’s credentials is made by this mechanism — and not by the principal independently. The main difference between them is the level of granularity we can define via which service the access is to be made, in an increasing order of precision.

Scenario 1: Validate that the call was made via AWS service

Key(s) to use: aws:ViaAWSService

If you know a call needs to be made via an AWS service but are not sure which, you can use this key, which determines if the call was made via an AWS service regardless of its service principal. This is, of course, the least precise key you can use, however, it can still be useful in that it helps fight most of the battle: guaranteeing that a principal won’t be able to use a service that needs to be called via an AWS service independently.

Using this key could be done when you want to configure a policy to prevent all potential principals from accessing a certain resource — yet still allow calls made via AWS services, as shown here. This is very similar to the use we saw before of aws:PrincipalIsAWSService, except the permissions granted here would be for calls made via AWS services not directly by their principals.

The two could be combined in order to provide a general permission for actions done in one of the two methods while limiting access to principals independently. When, for example, you want to set up a network perimeter outside of which only AWS Services could perform actions on a resource - using both context keys to exclude calls from AWS Services makes sense. However - when you want to set up an identity based perimeter you should not exclude calls with the aws:ViaAWSService context key set to true from the Deny, as by doing so you will prevent it from limiting actions which may originate from calls made by any identity - which defeats the purpose of the perimeter.

Condition example:

{
    "Effect":"Deny",
    "Condition":{
        "Bool":{
            "aws:ViaAWSService":"false"
        }
    }
}

Scenario 2: Validate that a certain service is included in the call chain

Key(s) to use: aws:CalledVia

As described above, subsequent calls made by AWS services with the credentials of the principal making the initial call which triggers are held in an array which is the value of the aws:CalledVia key.

If you know of a specific AWS service via which a call is made during any of the subsequent calls and can name its service principal, you can make the restriction even more granular by specifying it. Using aws:CalledVia lets you make sure that, when a specific AWS service is one of the callers on the chain, the access is being carried out by an AWS service.

If there’s a resource that is only to be used by a specific service — e.g. a KMS key that is meant to serve CloudFormation — regardless of what other services are part of the process, you can specify the condition as including the CloudFormation service principal as part of the aws:CalledVia array.

Condition example:

"ForAnyValue:StringEquals": {
     "aws:CalledVia": ["cloudformation.amazonaws.com"]
}

Note that should you want to achieve an even higher level of granularity — and, if you know the first and/or last service making the call and another service that has to be involved in the process, you can use this call along with a condition on aws:CalledViaFirst and/or aws:CalledViaLast (discussed next).

Scenario 3: Validate the chain of service principals making the calls

Key(s) to use: aws:CalledViaFirst and/or aws:CalledViaLast (respectively).

Getting more granular, you can validate the specific chain of service principals making calls using the principal’s credentials. This will ensure that calls are only made via the appropriate services and as part of the proper sequence to which they belong. It requires a very intimate understanding of the mechanisms in which the calls are made and can be very restrictive but is, of course, the most secure way to go.

Using the keys aws:CalledViaFirst and aws:CalledViaLast you can check that a call to a service is done as part of a specific chain of calls. What these keys do is check the service principals of the first and last calls of a specific chain of calls.

Let’s demonstrate how this could be helpful using the previous example, where CloudFormation calls DynamoDB which calls KMS. On the call between CloudFormation to DynamoDB, both aws:CalledViaFirst and aws:CalledViaLast keys will be cloudformation.amazonaws.com. On the call from DynamoDB to KMS, aws:CalledViaFirst will be cloudformation.amazonaws.com (as it’s the first service which made the call on the principal’s behalf) and aws:CalledViaLast will be dynamodb.amazonaws.com. If you are certain this process is the only use case in which the permissions would be put into use, you can validate these values on the permissions granted to DynamoDB and KMS and thus check in a more precise way that calls to them are not only made by the appropriate services but also at the correct order as part of the process they are meant to serve.

Condition example:

"StringEquals": {
     "aws:CalledViaFirst": "cloudformation.amazonaws.com",
     "aws:CalledViaLast": "dynamodb.amazonaws.com"
}

Note that you don’t have to use both if you only have definite knowledge of one end of the process (that is, the origin of the call chain or the most recent service which makes the call). The more precise you are, the closer you are to achieving least privilege.

Bonus scenario: Validating the source of calls to KMS operations

While the global condition context keys aws:CalledVia are relatively new, KMS has a condition key specific to it — kms:ViaService — which serves a very similar purpose. Using it you can limit the access granted to operations on KMS keys to only be allowed from specific services, by providing a list of one or several service principals which are allowed to perform the operation.

It’s important to know this key since the aws:CalledVia* keys currently can only be used with four services:

  1. Amazon Athena
  2. AWS CloudFormation
  3. Amazon DynamoDB
  4. AWS Key Management Service (AWS KMS)

This is quite a limited inventory compared to the list of services kms:ViaService supports.

So, for example, if you have an S3 bucket that uses a KMS key for encryption, and you want to validate that access to this KMS key is only done when called via the S3 service, you can do so using the kms:ViaService — while you couldn’t do so using the aws:CalledVia key, which currently doesn’t support S3.

Make sure you notice the service principal in the case of kms:ViaService also requires the specification of the region from which the call is made.

Key(s) to use: kms:ViaService condition example:

"StringEquals": {
      "kms:ViaService": [
        "ec2.us-west-2.amazonaws.com",
        "s3.us-west-2.amazonaws.com"
      ]
}

Moving forward

We hope you found this post useful in understanding these AWS global condition context keys and how you can use them to achieve least privilege. We highly recommend that you visit the posts referenced earlier and use this acquired knowledge to have an easier time understanding how they work. You can also try and apply these keys in your policies.

Related Articles

Cybersecurity News You Can Use

Enter your email and never miss timely alerts and security guidance from the experts at Tenable.