Improving policy readability with NotAction
Tags: aws, security, iam
Published: 2022-04-07 06:16:49

You can save time and improve the readability of your policies by using `NotAction`. In some cases, when you want to deny only a few actions and want to allow many of them, choosing this policy element can be a good option.

## 1. The problem

Alice and Bob work for the same company. Alice is a DevSecOps engineer, and Bob is in a non-technical department.

Alice is an administrator of the AWS accounts of the company. She is reliable and knows her stuff. Bob is new, and while he is probably reliable and knows his stuff in his field, he is not a technical person.

Alice just got the task to give Bob access to some department buckets in <a href="https://aws.amazon.com/s3/" class="link" target="_blank" rel="noopener noreferrer">S3</a>. He should be able to download and upload files to the buckets and modify them, too, and it is a requirement that Bob could not delete the objects, the buckets, and the object tags.

## 2. Possible solutions

Alice has a couple of options to mitigate the risk of data loss, which includes <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/Versioning.html" class="link" target="_blank" rel="noopener noreferrer">versioning</a> and <a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/MultiFactorAuthenticationDelete.html" class="link" target="_blank" rel="noopener noreferrer">MFA delete</a>, but she decided to disallow Bob the entire deletion operation.

She wants to solve this problem with <a href="https://aws.amazon.com/iam/" class="link" target="_blank" rel="noopener noreferrer">IAM</a> policies, and wants AWS to display big red error messages when Bob attempts to delete anything from the bucket or the bucket itself.

### 2.1. Allow what is needed

Alice can write a policy for Bob where she displays all actions that she wants to allow him except the forbidden ones.

It means the Bob can have all permissions except `s3:DeleteBucket`, `s3:DeleteObject`,`s3:DeleteObjectTagging` and `s3:DeleteBucketPolicy` (just to be on the safe side).

Part of the identity-based policy assigned to Bob can look like this:

```jsonc
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectTagging",
        "s3:ListBucket",
        "s3:PutObject",
        // ... and many more actions
      ],
      "Resource": [
        "arn:aws:s3:::MY-BUCKET-NAME",
        "arn:aws:s3:::MY-BUCKET-NAME/*"
      ]
    }
  ]
}
```

I counted 128 IAM permissions for S3 today (<a href="https://aws.amazon.com/s3/features/object-lambda/" class="link" target="_blank" rel="noopener noreferrer">S3 Object Lambda</a> and <a href="https://aws.amazon.com/s3/outposts/" class="link" target="_blank" rel="noopener noreferrer">S3 Outposts</a> excluded), which means that Alice needs to list a lot of actions if she wants to allow Bob full S3 experience except deleting objects, tags, and buckets.

This policy is hard to read and is very long. If Alice creates a couple more statements like this, she might exceed the <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html#reference_iam-quotas-entities" class="link" target="_blank" rel="noopener noreferrer">policy size limit</a>, too.

### 2.2. Use managed policies

Alice could use <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-vs-inline.html#aws-managed-policies" class="link" target="_blank" rel="noopener noreferrer">managed policies</a>, which provide permissions for common use cases.

AWS provides the `AmazonS3FullAccess` and `AmazonS3ReadOnlyAccess` managed policies for S3.

`AmazonS3FullAccess` looks like this:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*",
        "s3-object-lambda:*"
      ],
      "Resource": "*"
    }
  ]
}
```

The `*` includes the delete actions, so she decides not to go with this policy.

The `AmazonS3ReadOnlyAccess` managed policy, on the other hand, does not allow Bob to upload objects to the bucket, for example, so it is too narrow for him.

### 2.3. Deny deletion

Alice can decide to add an explicit deny to the delete actions. She can write something like this:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "s3:DeleteObject",
        "s3:DeleteBucket",
        "s3:DeleteObjectTagging",
        "s3:DeleteBucketPolicy",
      ],
      "Resource": [
        "arn:aws:s3:::MY-BUCKET-NAME",
        "arn:aws:s3:::MY-BUCKET-NAME/*"
      ]
    }
  ]
}
```

It looks good because **explicit deny always wins** no matter what, so Bob will not be able to delete anything from the bucket.

The problem is that the necessary actions (read and write) are still not allowed. Alice only **denied deletion** but she did not allow anything. Bob cannot get any objects from the bucket, and he cannot upload anything there either. His permission will need to contain an **explicit allow** for all other actions, and we are again back to point 2.1.

## 3. Use NotAction

IAM has a policy element called `NotAction` that acts like an inverse `Action`.

The meaning of `NotAction` is **every action but**. Given the scenario above, Alice can write a policy like this and attach it to Bob's identity:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "NotAction": "s3:Delete*",
      "Resource": [
        "arn:aws:s3:::MY-BUCKET-NAME",
        "arn:aws:s3:::MY-BUCKET-NAME/*"
      ]
    }
  ]
}
```

The policy above means that Bob is `ALLOW`ed to do **everything except** the `Delete` actions on the bucket called `MY-BUCKET-NAME`. Initially, Alice wanted to prevent Bob from deleting objects, buckets, tags, and bucket policy. She can achieve it with just one line of code by replacing the `Action` element with `NotAction`. (In fact, this policy disallows more than the required four actions because S3 has more `Delete` APIs. If Alice wants to be less strict, she can list these four actions in an array as the value of the `NotAction` key.)

This policy will result in an **implicit deny** for example, for the `s3:DeleteObject` actions because Alice explicitly allows (`Effect: Allow`) everything but `s3:Delete*`. It means that Bob cannot delete any objects from the bucket because he does not have explicit permission to do so.

## 4. Summary

We can create shorter, more concise policies using `NotAction`. In some cases, we can avoid listing many actions, which will make the policy statements more readable.

`NotAction` works well when we want to make a few exceptions from a large group of actions, like in the example above.

## 5. References and further reading

<a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html" class="link" target="_blank" rel="noopener noreferrer">IAM JSON policy elements reference</a> - collection of all policy elements, including `NotAction`

<a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html" class="link" target="_blank" rel="noopener noreferrer">Policy evaluation logic</a> - describes how IAM decides when to allow or deny an action