AWS KMS CMK(Customer Master Key)のキーポリシーとIAMポリシーの組み合わせのアクセス制御と、キーポリシーのみのアクセス制御を試して設定の違いをみてみた

2021.11.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS KMSのCMK(Customer Master Key)のアクセス制御方法について考えていました。キーポリシーとIAMポリシーを組み合わせたアクセス制御と、キーポリシーのみのアクセス制御を確認したかったので試しました。

本記事から得られるものはCMKのキーポリシーについて何かの参考になることがあるかもしれないくらいです。

キーポリシーとは

アクセス元に設定するIAMポリシーとは別にKMSにはキーポリシーというアクセス制御の仕組みがあります。

以下はデフォルトのキーポリシーの例です。

{
    "Version": "2012-10-17",
    "Id": "key-default-1",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        }
    ]
}

キーポリシーの設定はIAMポリシーで見られる設定と異なる点は明示的にrootを許可しないとルートユーザーがアクセスできないです。ルートユーザーのアクセスを暗黙的に許可されている多くのAWSリソースとは異なる点です。

有事のリカバリ手段としてルートユーザーのアクセス許可は入れておく方が無難でしょう。仮に設定ミスにより管理不能となったKMSキーについては、ドキュメントによるとAWSサポートへ連絡する必要があると記載があります。

こちらのキーポリシーですが、一見するとルートユーザーしか許可していないように思えました。ですが、実際は以下の効力があります。

  • アカウント123456789012のルートユーザーの任意のアクション
    • 見た目通りの印象です
  • アカウント123456789012のIAMのうち、IAMポリシーで許可を与えられたアクション
    • そうだったのかという印象です

上記の表現をした明確な記述のある文章をドキュメントからは見つけられませんでした。しかし、以下のドキュメントを参考にすると上記の挙動であると読み取れます。

次から実際に動作確認します。

動作検証

キーポリシーとIAMポリシーの組み合わせでアクセス制御する方法と、キーポリシーのみのアクセス制御する方法を試してみます。今回はルートユーザーを使える環境ではないため残念ながらルートユーザーによるアクセス検証は行えません。

CMKで暗号化したS3バケットを対象にEC2からファイルのアップロード、ダウンロードができるどうかでCMKへの権限があるか、ないかを確認していきます。

今回の検証に使用したCMKとS3バケットの環境の一部を作成したCloudformationテンプレートは以下に置いてあります。

権限設定1

キーポリシーとIAMポリシーの組み合わせの例です。CMKのキーポリシーはデフォルトの設定値です。IAMポリシーでCMKへの権限、CMKで暗号化したS3バケットへのアクセスに必要な権限を付与します。

キーポリシー

デフォルトのキーポリシーです。

{
    "Version": "2012-10-17",
    "Id": "key-default-1",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        }
    ]
}

IAMポリシー

CMKへの暗号化、複合に必要なアクションと、CMKで暗号化されたS3バケットへのファイル操作のアクションを設定しました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "kms:Decrypt",
                "kms:Encrypt",
                "kms:GenerateDataKey",
                "kms:DescribeKey",
                "kms:ReEncrypt*"
            ],
            "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03",
            "Effect": "Allow",
            "Sid": "SamplePolicy"
        },
        {
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::sample-dev-blog-bucket",
                "arn:aws:s3:::sample-dev-blog-bucket/*"
            ],
            "Effect": "Allow"
        }
    ]
}

S3アクセステスト1

確認結果まとめ

  • IAMポリシーにはデータ暗号化、複合に必要なアクションを設定
  • デフォルトのキーポリシーはIAMポリシーで許可されたアクションを許可
  • よって、ファイルアップロード、ダウンロードともに成功

S3バケットの参照成功。

$ aws s3 ls s3://sample-dev-blog-bucket
(戻り値なし)

ファイルのアップロード成功。

$ aws s3 mv ./sample-file.png s3://sample-dev-blog-bucket
move: ./sample-file.png to s3://sample-dev-blog-bucket/sample-file.png
$ aws s3 ls s3://sample-dev-blog-bucket
2021-11-20 06:23:33      51573 sample-file.png
$ ll
合計 0

ファイルのダウンロード成功。

$ aws s3 mv s3://sample-dev-blog-bucket/sample-file.png .
move: s3://sample-dev-blog-bucket/sample-file.png to ./sample-file.png
$ ll
合計 52
-rw-rw-r-- 1 ec2-user ec2-user 51573 11月 20 06:23 sample-file.png
$ aws s3 ls s3://sample-dev-blog-bucket
(戻り値なし)

権限設定2

権限設定1のIAMポリシーからKMSへの権限部分を丸っと削除しました。キーポリシーはデフォルトの設定値です。

キーポリシー

デフォルトのキーポリシーです。(変化なし)

{
    "Version": "2012-10-17",
    "Id": "key-default-1",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        }
    ]
}

IAMポリシー

KMSの権限をすべて削除。S3バケットへの権限は変化なし。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::sample-dev-blog-bucket",
                "arn:aws:s3:::sample-dev-blog-bucket/*"
            ],
            "Effect": "Allow"
        }
    ]
}

S3アクセステスト2

確認結果まとめ

  • IAMポリシーにはS3バケットへの操作権限だけがあり、KMSへの操作権限はない
  • デフォルトのキーポリシーはIAMポリシーで許可されたアクションを許可する
  • よって、CMKを使った暗号化、複合ができないためファイルのアップロード、ダウンロードに失敗した
  • S3バケットのリスト操作(表示)はS3バケットの権限のみでファイル名の表示はできた

S3バケットの参照成功。

$ aws s3 ls s3://sample-dev-blog-bucket
(戻り値なし)

ファイルのアップロード失敗。

$ aws s3 mv ./sample-file.png s3://sample-dev-blog-bucket
move failed: ./sample-file.png to s3://sample-dev-blog-bucket/sample-file.png An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

ファイルのダウンロード失敗。ダウンロードテストのためにマネコンから手動でテストファイルをアップロードしてからテストしています。

$ aws s3 ls s3://sample-dev-blog-bucket
2021-11-20 06:58:36         22 README.md
$ aws s3 mv s3://sample-dev-blog-bucket/README.md .
move failed: s3://sample-dev-blog-bucket/README.md to ./README.md An error occurred (AccessDenied) when calling the GetObject operation: Access Denied

権限設定3

キーポリシーを変更し、キーポリシーのみでKMSへのアクセス制御します。

キーポリシー

EC2に設定しているIAMロールから暗号化、複合に必要なアクションを許可します。権限設定1でIAMポリシーに設定していたアクションと同じです。Resource:*にしています。キーポリシーでの*はCMK自身にあたるため対象が限定されます。

{
    "Version": "2012-10-17",
    "Id": "key-default-1",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Sid": "Allow use of the key",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/dev-sample-ec2-role1"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "*"
        }
    ]
}

IAMポリシー

権限設定2のIAMポリシーのままです。KMSへの操作権限はありません。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::sample-dev-blog-bucket",
                "arn:aws:s3:::sample-dev-blog-bucket/*"
            ],
            "Effect": "Allow"
        }
    ]
}

S3アクセステスト3

確認結果まとめ

  • IAMポリシーにはS3バケットへの操作権限だけがあり、KMSへの操作権限はない
  • キーポリシーでEC2のIAMロールはCMKへの暗号化、複合の操作を許可する
  • よって、キーポリシーで暗号化、複合ができて、IAMポリシーにはS3バケットの操作権限があるためファイルのアップロード、ダウンロードに成功した

S3バケットの参照成功。

$ aws s3 ls s3://sample-dev-blog-bucket
2021-11-20 06:58:36         22 README.md

ファイルのアップロード成功。

$ aws s3 ls s3://sample-dev-blog-bucket
2021-11-20 06:58:36         22 README.md
$ aws s3 mv ./sample-file.png s3://sample-dev-blog-bucket
move: ./sample-file.png to s3://sample-dev-blog-bucket/sample-file.png
$ aws s3 ls s3://sample-dev-blog-bucket
2021-11-20 06:58:36         22 README.md
2021-11-20 07:09:21      51573 sample-file.png
$ ll
合計 0

ファイルのダウンロード成功。

$ aws s3 mv s3://sample-dev-blog-bucket/sample-file.png .
move: s3://sample-dev-blog-bucket/sample-file.png to ./sample-file.png
$ ll
合計 52
-rw-rw-r-- 1 ec2-user ec2-user 51573 11月 20 07:09 sample-file.png
$ aws s3 ls s3://sample-dev-blog-bucket
2021-11-20 06:58:36         22 README.md

まとめ

ドキュメントに書いてある通りの動作であることを確認できました。この検証で得たものはCMKを利用している場合、CMKを利用する各リソースのIAMロール(IAMポリシー)だけを注意するのではなく、キーポリシーで意図しない設定がされていればCMKを利用することはできるため、CMKのキーポリシーの設定内容も注意する必要があるということです。ただ、仮に単体でCMKを利用できてもCMKで暗号化しているリソースへのアクセス権限がないと、実データへはアクセスはできないというのはあります。

おわりに

AWSKeyManagementServicePowerUserポリシーのようなKMSのリソース全般(kms:*)に対して強い権限をもつユーザー、リソースが不意に生み出されてしまうことを想定すると、CMKのキーポリシーで特定のリソース以外は拒否と書けば安全ではないのか?と思いました。しかし、運用のまわる良い設定方法が思いつきませんでした。他のアクセス制限も含め改めて考えたいと思います。

ガードレールの発想でうまくキーポリシーを設定したかったのですが、あとから読んだKMSのベストプラクティスにはkms:*の権限を含めないようにと書かれていました。「今ならその気持ちすごくわかるよ」という感想を残して終わります。

機密情報を保護するために CMK が使用されている場合、対応するキーポリシーが最少の権限のモ デルに従っていることを確認するように作業する必要があります。これには、IAM ポリシーに kms:* 権 限を含めないようにすることが含まれます。

参考