KMSとSQSの鍵権限

# はじめに
KMSの権限管理、およびKMSとSQSの権限の関係がややこしかったので整理のために書きます。

主に自分の整理のために書きます。誰かのためにもなると良いなと思います。

なお、私はバックエンドを専門としています。普段terraformをゴリゴリ書いてたりはするのですが、まだ半人前なので内容についての責任は持てませんのでご容赦ください。

とはいえ、AWSのドキュメントを整理しているだけなので誤りはないかとは思いますが。

# KMS
KMSの[ドキュメント](https://docs.aws.amazon.com/kms/latest/developerguide/key-policies.html)から引用しつつ、整理します。

>  Key policies are the primary way to control access to KMS keys. Every KMS key must have exactly one key policy.

KMSの鍵は主にキーポリシー側で制御することが望まれ、常に1つの鍵が紐づきます。

これはAWSのサービスの中でも特徴的な性質です。
例えば、IAMのポリシーの評価ロジックが記されている[ドキュメント](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html) には以下のような記載があります。


> For most resources, you only need an explicit allow for the principal in either an identity-based policy or a resource-based policy to grant access. IAM role trust policies and KMS key policies are exceptions to this logic, because they must explicitly allow access for principals.

IAMの原則として、同一アカウントでは、アイデンティティベースポリシーとリソースベースのポリシーの「OR」で評価が決まるのでどちらかで許可を行えば良いのですが、「信頼関係」と「KMSのキーポリシー」は例外で必ず明示的に許可する必要があります。

明示的に設定しない場合にはデフォルトの設定が暗黙的にAWSによって設定され、それが以下のようなポリシーです。

```json
{
  "Sid": "Enable IAM policies",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::111122223333:root"
   },
  "Action": "kms:*",
  "Resource": "*"
}
```

キーポリシーにおけるResourceは常に`*`で、鍵自身を指します。

この設定を見ると「デフォルトではAWSアカウント内のリソースは全ての鍵操作が許されているんだな」と勘違いしがちですが、誤りです。

ここは非常にややこしいのでドキュメントを長めに引用すると、

> The principal in this key policy statement is the account principal, which is represented by an ARN in this format: arn:aws:iam::account-id:root. The account principal represents the AWS account and its administrators.
When the principal in a key policy statement is the account principal, the policy statement doesn't give any IAM users or roles permission to use the KMS key. Instead, it allows the account to use IAM policies to delegate the permissions specified in the policy statement. This default key policy statement allows the account to use IAM policies to delegate permission for all actions (kms:*) on the KMS key.

`arn:aws:iam::111122223333:root`形式のprincipalを[account principal](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#principal-accounts)と呼びます。キーポリシーのprincipalがaccount principalである場合、**アカウントに対して鍵操作自体を許可するのではなく、鍵に対するアイデンティティベースポリシーの設定を許可する** という意味になります。

そのため、デフォルトのキーポリシーの状態ではIAMを追加で設定しなければ鍵操作できません。

その他のprincipalを設定した場合は、素直に鍵操作が許可されます。

ややこしいですが、[ドキュメント](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-overview.html)の末尾にある「Example key policy」で具体例をもとに整理できると思います。

# SQS
SQSでは、server-side encryption (SSE)のためにKMSが利用できます。
KMSには`AWS managed key`と`customer managed key`の2種類あります。

`AWS managed key`は文字通りAWSが管理する鍵で「アカウント×AWSサービス」の粒度で鍵が管理されています。

> Every KMS key must have a key policy. Note that you cannot modify the key policy of an AWS managed KMS key for Amazon SQS. The policy for this KMS key includes permissions for all principals in the account (that are authorized to use Amazon SQS) to use encrypted queues.

`AWS managed key`の鍵ポリシーは以下のように設定されており、変更することができません。アカウント内の全てのprincipalに対して鍵操作が許可されています。

先述の通りaccount principalではこのような制限ができないので、principalには`*`が指定され、`Condition`で絞っているのが興味深いです。



```json
{
    "Version": "2012-10-17",
    "Id": "auto-sqs-1",
    "Statement": [
        {
            "Sid": "Allow access through Simple Queue Service (SQS) for all principals in the account that are authorized to use SQS",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:CreateGrant",
                "kms:DescribeKey"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "kms:ViaService": "sqs.ap-northeast-1.amazonaws.com",
                    "kms:CallerAccount": "111122223333"
                }
            }
        },
        {
            "Sid": "Allow direct access to key metadata to the account",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::111122223333:root"
            },
            "Action": [
                "kms:Describe*",
                "kms:Get*",
                "kms:List*",
                "kms:RevokeGrant"
            ],
            "Resource": "*"
        }
    ]
}

```

`customer managed key` は利用者が管理する鍵です。

`AWS managed key`の場合は先述の通り、同一アカウントのprincipalであれば権限が付与されているので特に権限まわりで意識することはない(当然ながら`sqs:SendMessage`,`sqs:ReceiveMessage`等SQSに対する権限設定は必要)のですが、`customer managed key`の場合は意識する必要があります。

こちらの[ドキュメント](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-key-management.html)にまとまってますが、

Producerには、`kms:GenerateDataKey`と`kms:Decrypt`が必要です。

<details>
<summary>例</summary>

```json
{
   "Version": "2012-10-17",
      "Statement": [{
         "Effect": "Allow",
         "Action": [
            "kms:GenerateDataKey",
            "kms:Decrypt"
         ],
         "Resource":  "arn:aws:kms:us-east-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"
         }, {
         "Effect": "Allow",
         "Action": [
            "sqs:SendMessage" 
         ],
         "Resource": "arn:aws:sqs:*:123456789012:MyQueue"
      }]
}
```

</details>

Consumerには、`kms:Decrypt`が必要です。

<details>
<summary>例</summary>

```json
{
   "Version": "2012-10-17",
      "Statement": [{
         "Effect": "Allow",
         "Action": [
            "kms:Decrypt"
         ],
         "Resource": "arn:aws:kms:us-east-2:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab"
         }, {
         "Effect": "Allow",
         "Action": [
            "sqs:ReceiveMessage"
         ],
         "Resource": "arn:aws:sqs:*:123456789012:MyQueue"
      }]
}
```
</details>


上記例はアイデンティティベースポリシー側で権限設定してましたが、当然キーポリシー側に設定しても良いです。


> In general, unless you are required to control the encryption key that protects your resources, an AWS managed key is a good choice. You don't have to create or maintain the key or its key policy, and there's never a monthly fee for an AWS managed key.

とあるので基本的には`AWS managed key`を使えば良いでしょう。


# SNSからSQSに送る場合
ブログを書くきっかけになった実際にハマったパターンです。

アプリケーションからはSNSにpublishし、SNSから複数のSQSにFanoutするのは[典型的なデザイン](https://docs.amazonaws.cn/en_us/sns/latest/dg/sns-sqs-as-subscriber.html)です。マイクロサービスアーキテクチャでは、各マイクロサービスに専用のSQSが紐付き、非同期コミュニケーションを実現するためにこのパターンがよく使われます。

各マイクロサービス専用のSQSが既にある状態でSNSをアプリケーションとSQSの間にかませようとしたところ、メッセージが届かずハマりました。

原因は単純です。SQSにて`AWS managed key`を利用していたためです。

**SNSのようなAWSサービスに対して鍵操作を許可するには`customer managed key`を使う必要があリます。**

なぜかというと、復習になりますが、`AWS managed key`は編集できないが、同一AWSアカウントのみにIAM操作を許可しているためでしょう。

以下はドキュメントにあるKMSのキーポリシーの例です。

```json
{
   "Version": "2012-10-17",
      "Statement": [{
         "Effect": "Allow",
         "Principal": {
            "Service": "sns.amazonaws.com"
         },
         "Action": [
            "kms:GenerateDataKey",
            "kms:Decrypt"
         ],
         "Resource": "*"
       }]
}
```

`customer managed key`に変更し、このキーポリシーを設定することでSNSからSQSに問題なく送ることができます。

ただ、この修正だけでは、マイクロサービスに既に存在するConsumerがSQSからreceiveできなくなってしまいます。何故なら`AWS managed key`では特にKMS関連の権限設定が不要だったため、許可設定がされていないためです。

マイクロサービスを追加するごとにKMSの鍵権限を意識する必要があるのは運用が大変だよなぁ〜。`AWS managed key`のキーポリシーの定義を真似て、アカウント全体に許可するキーポリシーを設定するのはどうだろう〜 by 同僚。

と悩んでいたところ、[Server-Side Encryption with Amazon SQS-managed encryption keys (SSE-SQS)](https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-sqs-server-side-encryption-keys-sse/)なるKMS以外の暗号化手段が2021年11月から提供されていることを発見しました。

KMSによる暗号化のようにユーザが鍵を意識する必要が全くなく、透過的に暗号化、復号化され、お金もかかりません。SSE-KMSからSSE-SQSに切り替えることで、特に追加の権限設定等行わずに解決することができました。

基本的には、SSE-KMSよりもSSE-SQSを利用した方がいいのではと思いますが、デメリットも完全にないわけではないようなので、ドキュメントをちゃんと確認しましょう。

# まとめ
KMSの権限管理が複雑すぎる。

整理するために色々書いたけど、SQSにおいてはSSE-KMS使わずにSSE-SQSを使えばKMSと決別できそうだ。