The state of ABAC on AWS

2020.11.02

RSS feed

Two years ago, in November 2018, AWS announced new conditions keys aws:PrincipalTag and aws:RequestTag, and started to push the concept of Attribute Based Access Control (ABAC). This post will describe what this is, the difficulties with implementing this strategy, and what AWS needs to do for customers to be successful with this concept.

What is ABAC?

A long standing problem with AWS security has been that if you had two projects in a single AWS account, it was often impossible to ensure that some principals (meaning the users and roles there) could only interact with the resources of one project and not the other. In order to implement a least privilege strategy, you want to isolate the actions each principal can take to only certain resources to ensure they cannot impact or exfil data from the other project.

The solution many customers have been forced to adopt is to isolate their projects into separate AWS accounts, but that’s not always ideal. For example, it can be difficult to take an existing account and move resources into another account as an account grows. So AWS started focusing on tagging resources and restricting access via tags. Over time, many privileges started to be able to work with the condition key aws:ResourceTag so that you could restrict who could interact with an existing resource. But what if you wanted the principal to create new resources, but restrict what tags they could use, so they couldn’t create a resource with the tag of another project? For this AWS released aws:RequestTag.

What if you had many principals and projects and you didn’t want to create separate IAM policies for each one? You want a single policy that you can apply to all principals that says “Only interact with resources that match the same tag as you have” or the common request of “You can only interact with resources you created.” To implement this concept, AWS released aws:PrincipalTag, so you could now use a conditions such as:

StringEquals: { "aws:RequestTag/project": "${aws:PrincipalTag/project}" }

Attribute-based access control (ABAC) is an authorization strategy that defines permissions based on attributes, which on AWS means tags. Two of the best resources on this concept are Brigid Johnson’s re:Inforce talk Scale Permissions Management in AWS w/ Attribute-Based Access Control and Michael Chan’s blog post Working backward: From IAM policies and principal tags to standardized names and tags for your AWS resources.

Problems with ABAC

Lack of privilege support

The first issue people ran into with ABAC was that not all resources supported tags. Of those resources that did, not all supported IAM conditions to restrict these tags. Of those that did, not all supported tag on create, so you could only restrict access to tag existing resources, leaving resources untagged. Let’s get some stats on how much coverage AWS has today. Using the IAM data from Parliament (which is just the AWS docs scraped into a json file), we find there are 869 privileges that contain the word create, which we can assume to be the privileges that grant permission to create a resource.

$ cat parliament/iam_definition.json | jq '.[]|.prefix as $prefix|.privileges[]|.privilege as $privilege|select($privilege|ascii_downcase|contains("create"))|$prefix+":"+$privilege'  | sort | uniq | wc -l
     869

Next, we’ll find all the privileges of these that allow the RequestTag condition key:

$ cat parliament/iam_definition.json | jq '.[]|.prefix as $prefix|.privileges[]|.privilege as $privilege|select($privilege|ascii_downcase|contains("create")).resource_types[].condition_keys[]|select(.|ascii_downcase |contains("requesttag"))|$prefix+":"+$privilege' | sort | uniq | wc -l
     381

We find that 381 of 869 (43%) privileges for creating resources on AWS allows you to both tag the new resources and to restrict what tags are used for that. This search does miss some privileges that are used to create resources but do not include the word create, such as ec2:RunInstances that lets you create an EC2 and route53:ChangeResourceRecordSets that lets you create a subdomain. It also misses situations where AWS has two privileges for creating a resource, where one privilege is used for creating the resource with tags and one without, such as cloudfront:CreateDistribution and cloudfront:CreateDistributionWithTags. However, 43% seems roughly correct.

One might try to argue that the more widely used resources do support tag on create and restricting those tags, but there are some popular resources that do not. For example, the following privileges are all unable to restrict tag on create: lambda:CreateFunction, dynamodb:CreateTable, kms:CreateKey, logs:CreateLogGroup, s3:CreateBucket, sqs:CreateQueue", and iam:CreateRole.

As a hack, for resources that don’t support tag on create, you can use the names of the resources in a similar way as a tag, but this is awkward.

Lack of tooling

Given a resource in an AWS account, there is not much tooling available that can tell you who all has access to it. Some tools (ex. awspx) will tell you who has certain privileges, but they don’t understand conditions, among other details. So for example, they can tell you who has secretsmanager:CreateSecret, but they won’t tell you who can create a secret with the tag foo.

I built some functionality into CloudMapper through it’s access_check command that tries to understand more IAM logic. It has some understanding of conditions and will also take IAM Boundaries into consideration, but it does not understand the existing tags on a resource, and lacks a lot of other functionality. Its answers will be more correct than other tools for some questions, but will still be incorrect for a lot of cases. The project PMapper also has some additional logic in it.

SimulatePrincipalPolicy

There is an API called SimulatePrincipalPolicy that can be used to understand who has access to resources, but it is missing a lot of functionality you would expect. For example, you can pass it the ARN of a principal, the ARN of a resource, and associated privilege to check for, but if there are any conditions, you then have to also include the condition values that should be used when checking this. This means you have to figure out the tags of the resource and the resource policy to then pass to this call.

So for example, assume we have a principal that can call secretsmanager:GetSecretValue only on secrets that have been tagged with a project key that has a value foo, and we have a secret with that tag. In order to check if our principal can access this secret, we can run:

aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/testuser \
  --action-names secretsmanager:getsecretvalue \
  --resource-arns arn:aws:secretsmanager:us-east-1:123456789012:secret:test-abcdef \
  --context-entries ContextKeyName=secretsmanager:ResourceTag/project,ContextKeyValues=foo,ContextKeyType=string

Notice in the last line, I have to tell it the value of the tag for the resource that I want it to check. The policy simulator does not figure that out for you. So if you were to try to automate this, you would have to make a describe call, knowing where in the response to find the tag value, and how to format the call to SimulatePrincipalPolicy with this value. Also, if the IAM policy has unrelated condition keys for other privileges, you have to provide context keys for those too. Next, you have to provide the resource policy if one exists, the IAM boundary if one exists, and potentially other data.

Because of these limitations, this API is not as useful as you might hope.

Zelkova

Zelkova is an automated reasoning solution for IAM policies that was announced by AWS in 2017 and available for private beta. When I talk to people about the problem of understanding who has access what, this project often comes up as a possible option by those who aren’t familiar with what exactly it does. Unfortunately, Zelkova is an engine that you still have to figure out the inputs to. It can answer some IAM related questions, but for our goals of understanding who has access to what, it has all the same limitations as iam:SimulatePrincipalPolicy.

Limited capabilities of Tag Policies

An AWS Organization feature called Tag Policies was supposed to help enforce tagging, but it is critically limited by only being able to enforce what tag values may be used when defined tag keys are used. This means you cannot enforce that a resource is tagged. You can only enforce that when someone attempts to tag a resource with a certain key, that the value is one of a defined set. In order to enforce tagging actually be used in an organization, you have to use SCPs as described here.

Enforcement has no effect on resources that are created without tags.

This warning is from the docs here.

Further, Tag Policies do not have coverage across all resources that support tags. The list of supported resources is here. An example of a resource that supports tags, but is not supported by Tag Policies, is S3 objects.

AWS needs to extend the functionality of this feature to support enforcement of using tag keys, as there is little value in it in its current form.

Lack of support for working with multiple tag values

People often work on multiple projects, but there is no way to tag a principal with a key that has multiple tag values. As an example, imagine you have two projects, foo and bar, and you want to allow a person to work on just foo and all the resources they create should have a Project tag with value foo, and likewise you have another person who should only work on the bar projects. AWS has shown how to create a single IAM policy that can be applied to both people, and you’d just need to make sure to apply a Project tag to the principals to define which project they can work on.

Now imagine that one of these employees needs to work on both projects. You cannot tag a principal with both foo and bar. One solution is to have the person assume different IAM roles depending on which project they are working on. Another option would be to create a custom IAM policy for this person that hard-codes what projects they can work on, and therefore stops using the principal tag on them. Neither option seems ideal.

Lack of transitive tags for the creation of resources

AWS has a concept of transitive tags that will follow an entity as they assume between roles which can assist with auditing sessions. They have transitive tags for the creation of sessions, but not for other resources. It would be nice if this concept could be applied to any resources they create, such that EC2s and other resources are automatically tagged in some way as they are created, without the user having to specify those tags. So if a user is part of the foo project, all the resources they create are automatically tagged as being part of foo.

Other criticisms of ABAC

Others outside of AWS have also written about the limitations and difficulties of ABAC, touching on other problems they’ve run into:

Conclusion

Creating correct IAM policies to enforce tags is difficult. Some goals are impossible and some are awkward due to limitations of IAM. The limitations are not constant across resource types and these discrepancies add to the complexity. It is possible in some situations to define permissions based on tags, but it is not universally possible, so beware when attempting to adopt this strategy. AWS’s lack of company-wide direction and standardization is a regular source of frustration for customers.

One of the biggest problems on AWS that has a relatively straightforward solution, is tooling to understand environments, such as who has access to which resources. I would love for AWS to increase investment in this area, such as by improving the SimulatePrincipalPolicy API to automatically figure out the conditions involved in a call. Until they do so, or someone else does, attempting to define permissions based on tags is going to be unnecessarily difficult, and your best strategy for security boundaries, for a variety of reasons, will continue to be to use separate AWS accounts.