Attribute-based access control in AWS
Tags: aws, security, beginners
Published: 2022-03-18 07:02:46

When a company grows, the number of developers and resources increase, it can be cumbersome to always modify <a href="https://aws.amazon.com/iam/" class="link" target="_blank" rel="noopener noreferrer">IAM</a> permissions and policies to keep up with the changing environment.

One solution that can ease this problem is **attribute-based access control (ABAC)**.

## 1. What is ABAC?

Attribute-based access control is an **authorization strategy** that lets us create fine-grained permissions based on user attributes.

It scales well as the company grows because we won't necessarily need to create new permissions and policies when a new resource or team member is introduced.

## 2. ABAC vs RBAC

In the traditional **role-based access control (RBAC)** we create permissions to the resource, add the permissions to policies, and attach the policies to users or roles.

If we want to follow the [least privilege principle](https://arpadt.com/articles/least-privilege-principle) (and we do want to follow it), we'll need to create minimum permissions for the IAM identity to get the job done.

This means that if *Identity1* wants to access *Resource1* and *Identity2* needs *Resource2*, then we should have two policies, one for *Identity1* and *Resource1* and another one for *Identity2* and *Resource2*. Resources are specific in the policy, and as a new resource is added or some other changes occur in the application, we'll need to modify the policy.

With **ABAC**, the number of permissions can be reduced, which means multiple users can use the same policy to access different resources. When a new resource is added or a new member joins the team, we won't necessarily need to update the policy as long as the new resource and team member have the correct attributes attached. This also means that all resources with the relevant attributes can be accessed.

If the ABAC has been planned correctly, team or project changes will become easy, too. We only need to change the relevant attribute on the user or role, and the corresponding resources will be accessible.

## 3. ABAC elements

Before going into detail about the elements of ABAC, let's quickly recap how AWS authorizes requests.

### 3.1. Request authorization

When a *principal* (user or role) wants to access a *resource* via the <a href="https://aws.amazon.com/console/" class="link" target="_blank" rel="noopener noreferrer">AWS Console</a> or <a href="https://aws.amazon.com/cli/" class="link" target="_blank" rel="noopener noreferrer">CLI</a>, AWS will create an *context* object. Context will contain a lot of information about the *request* itself, and we can write policies about these context attributes.


![Image AWS authorization process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xcz7uhtbykm111bv43nu.png)

When the request is made, AWS will check all the relevant policies, and if there is an overall `ALLOW` permission (given that there's no explicit `DENY`), it will let the principal access the resource.

### 3.2. Use tags for ABAC

On AWS, the attributes are **tags**, and we use the `Condition` element in the policy to control access to the resources.

Based on the authorization process above, tags can be attached to the *request*, *resource*, and *principal* (can be hardcoded on the IAM identity or passed in the session when users federate through their identity provider).

## 4. Example

Let's see some examples how we can control access to <a href="https://aws.amazon.com/sns/" class="link" target="_blank" rel="noopener noreferrer">SNS</a> using tags.

The scenario is the following: We have two teams, Red and Blue. The Red team should only create, modify, use and delete SNS resources that belong to the Red team but not the Blue team. The Blue team will control the Blue resources but not the Red ones.

The following policy statements are excerpts from the policy which is attached to the Red and Blue team roles. Using ABAC, we can attach the same policy to both roles, and we won't need to specify either Red or Blue resources.

We will use the `project` tag for authorization, and as such the permissions are built around this tag key and its value. This way the Red and Blue roles need to have the `project: red` and `project: blue` tags, respectively. Of course, they can have other tags as well, but they are not relevant to the authorization process.

### 4.1. Tags in the request

Tags can be passed in the request. In this case, we use the `aws:RequestTag/<key-name>` condition key. The `RequestTag` will be the new tag on the resource to be created.

```json
{
  "Sid": "CreateTopic",
  "Effect": "Allow",
  "Action": "sns:CreateTopic",
  "Resource": "*",
  "Condition": {
    "StringEquals": {
      "aws:RequestTag/project": "${aws:PrincipalTag/project}"
    },
    "ForAllValues:StringEquals": {
      "aws:TagKeys": [
        "project",
        "application"
      ]
    }
  }
}
```

This statement only allows the topic creation if the request contains the `project` key (`aws:RequestTag/project`), and it matches the `project` tag of the role that is submitting the request. In the case of the Red team, they will need to add the `project: red` tag in the request.

For example, the CLI command can look like this:

```bash
aws sns create-topic --name test-red-topic --tags 'Key=project,Value=red' --region us-east-1
```

The `TagKeys` condition key contains all tags that can be passed in the request. In this case, the Red team can only pass the `project` and `application` tags when creating a topic but not else. `TagKeys` also helps prevent the use of the same keys with a different spelling. Condition keys are case insensitive, so both `project` and `Project` would work if we didn't specify the lower case version in the `TagKeys` array.

This statement by itself is not enough to create the topic. The Red role also needs the `sns:TagResource` permission, which can look like this:

```json
{
  "Sid": "TagTopic",
  "Effect": "Allow",
  "Action": "sns:TagResource",
  "Resource": "*",
  "Condition": {
    "StringEqualsIfExists": {
      "aws:ResourceTag/project": "${aws:PrincipalTag/project}",
      "aws:RequestTag/project": "${aws:PrincipalTag/project}"
    },
    "ForAllValues:StringEquals": {
      "aws:TagKeys": [
        "project",
        "application",
        "name",
        "environment"
      ]
    }
  }
}
```

The statement above can be applied when a new resource is created with the relevant `project` tag (the resource tag is conditional because there cannot be a `project` tag on the topic if the topic doesn't exist yet), or an existing topic needs additional tags (the request `project` tag is conditional because the tag already exists along with the topic, so the request doesn't need to have the `project: red` or `project: blue` tag).

### 4.2. Tags on the resource

After the resource (in this case the new topic) has been created, the Red team can use the topic. The policy has the `aws:ResourceTag` condition key to allow access to the resource whose `project` tag matches the `project` tag of the Red role (which has the value of `red`).

```json
{
  "Sid": "UseTopic",
  "Effect": "Allow",
  "Action": [
    "sns:Publish",
    "sns:DeleteTopic",
    "sns:Unsubscribe",
    "sns:Subscribe",
    "sns:GetTopicAttributes",
    "sns:ListTagsForResource",
    "sns:ListSubscriptionsByTopic",
    "sns:SetTopicAttributes"
  ],
  "Resource": "*",
  "Condition": {
    "StringEquals": {
      "aws:ResourceTag/project": "${aws:PrincipalTag/project}"
    }
  }
}
```

The difference between `aws:RequestTag` and `aws:ResourceTag` is that the former specifies the tag to be added to the resource while the latter refers to an existing tag of an existing resource.

### 4.3. Tags on the principal

In both cases, the matching is checked using the `aws:PrincipalTag` tag key. The request will contain the Red role's `project: red` tag (and the Blue role's `project: blue` tag), and the `${aws:PrincipalTag/project}` **policy variable** will have the tag value when the policy evaluation occurs.

## 5. Summary

Attribute-based access control is a great way to simplify permission management. It scales well when the resources or the IAM identities start to grow.

ABAC makes it possible to add new users, roles, and resources without modifying the policy, which would be a required step if we used the traditional role-based access control.

This way, attribute-based access control gives us the possibility to create granular permissions without modification these permissions.

You can find the <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html" class="link" target="_blank" rel="noopener noreferrer">list of AWS services supporting ABAC</a> in the *Authorization based on tags* column.

## 6. References, further reading and watching

<a href="https://www.youtube.com/watch?v=Iq_hDc385t4" class="link" target="_blank" rel="noopener noreferrer">Scale Permissions Management in AWS w/ Attribute-Based Access Control</a>, AWS re:Inforce 2019, Brigid Johnson - great video on the topic, it's a must if you are interested to learn more

<a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html" class="link" target="_blank" rel="noopener noreferrer">What is ABAC for AWS?</a> - Short summary on AWS ABAC

<a href="https://aws.amazon.com/identity/attribute-based-access-control/" class="link" target="_blank" rel="noopener noreferrer">What is ABAC for AWS?</a> - Collection of ABAC resources on the AWS ABAC landing page