Contents

KMS key context, IAM conditions, and s3

Contents

At $work, I’ve been using KMS to encrypt s3 bucket contents for some time now. It works rather well, but one thing that had been bugging me is that our IAM policies granted both read permissions on bucket objects and encrypt/decrypt on the relevant KMS key. That is, principals with the policies attached can use the key to encrypt/decrypt anything they otherwise have permission to access, not just objects in the bucket. It didn’t appear that there was a reasonable way to tighten this until I ran across references to the IAM kms:EncryptionContext: condition.

Using kms:EncryptionContext: it is possible to conditionally restrict a policy based on the ARN of the resource being acted upon. That is to say, one can use this condition to only allow a KMS key to be used to decrypt objects in a certain s3 bucket.

It took me a bit to figure this out as the docs didn’t quite spell out how to use an ARN as an encryption context (and, you know, encryption), so here’s a policy that shows it in action. The policy allows actions a certain KMS key to be used only in the context of the given bucket:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "kmsKeyAccess",
      "Effect": "Allow",
      "Action": [
        "kms:ReEncrypt",
        "kms:GenerateDataKey*",
        "kms:Encrypt",
        "kms:Decrypt"
      ],
      "Resource": "arn:aws:kms:us-west-2:12345678901234:key/1234-abcd-567890-12345",
      "Condition": {
        "StringLike": {
          "kms:EncryptionContext:aws:s3:arn": "arn:aws:s3:::davros-world-domination/*"
        }
      }
    }
  ]
}

These actions are only granted on objects in the specific s3 bucket and not denied explicitly to anything else. This is important, as we don’t want our efforts here to block a legitimate grant somewhere else.

This can be extended to apply to multiple buckets by changing the condition test to ForAnyValue:StringLike.