AWS KMS S3バケットをSSE-KMSで暗号化しファイルのアップロード・ダウンロードするときにKMSへ必要なIAMポリシーを確認してみた

2021.11.21

SSE-KMS暗号化したS3バケットに対してファイルのアップロード、ダウンロードに必要なIAMポリシーを考えていました。S3バケットへの操作権限と、KMSのキーポリシーがデフォルトの場合はSSE-KMS暗号化したS3バケットの場合はKMSへの操作権限も必要です。KMSに必要なIAMポリシーについて失敗した例を元にKMSのIAMポリシーに着目した覚書です。

書き残したかったこと

  • ファイルのアップロードは暗号化するためにkms:Encryptと、kms:GenerateDataKey2つ許可が必要
  • ファイルのダウンロードは複合するためにkms:Decryptの許可が必要

IAMポリシーサンプル

Sample IAM policy

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

検証環境

EC2に設定したIAMロールのIAMポリシーのKMSに関するアクションを編集して、EC2からS3へのファイルアップロード・ダウンロードの動作を確認します。

CMKのキーポリシー

デフォルトのキーポリシー設定です。この場合はIAMポリシーで許可を与えられたアクションを許可されます。CMKのアクセス制御はIAMポリシーに寄せます。

キーポリシー

{
    "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": "*"
        }
    ]
}

EC2からS3のファイルをダウンロード

当初設定していたアップロードがダメなIAMポリシーです。失敗を記録することが目的なので順を追ってみていきます。ダウンロードに必要な権限を付与しています。

policy

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

バケットの中身を一覧出力

S3バケットにはテストファイルが保存されています。

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

ファイルのダウンロード

S3からファイルをダウンロードします。

$ aws s3 cp s3://sample-dev-blog-bucket/README.md .
download: s3://sample-dev-blog-bucket/README.md to ./README.md

EC2のローカルにコピーされたファイルを確認できました。kms:Decryptの許可があるためファイルを複合できます。

$ ll
合計 56
-rw-rw-r-- 1 ec2-user ec2-user    22 11月 20 06:58 README.md
-rw-rw-r-- 1 ec2-user ec2-user 51573 11月 20 07:09 sample-file.png

一度脱線します。以下の様にkms:Decryptを許可しない(明示的な許可がない)場合はどうなるでしょうか?気になったので試してみます。

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

複合できないため失敗します。AccessDeniedのメッセージからですとS3の権限不足なのか、KMSの権限不足なのかは判断つかないですね。

$ aws s3 cp s3://sample-dev-blog-bucket/README.md .
download failed: s3://sample-dev-blog-bucket/README.md to ./README.md An error occurred (AccessDenied) when calling the GetObject operation: Access Denied

CloudTrailからログを確認します。イベントソースをkms.amazonaws.comでフィルターすると探しやすいです。

エラーメッセージの項目にkms:Decryptの許可がないことを示したメッセージが載っていました。

イベントレコード

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAQ4BT4DHFF4UKLZA33:i-06593b38f6016a363",
        "arn": "arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363",
        "accountId": "123456789012",
        "accessKeyId": "ASIAAAAAAAAAA",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAQ4BT4DHFF4UKLZA33",
                "arn": "arn:aws:iam::123456789012:role/dev-sample-ec2-role1",
                "accountId": "123456789012",
                "userName": "dev-sample-ec2-role1"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2021-11-21T04:45:45Z",
                "mfaAuthenticated": "false"
            },
            "ec2RoleDelivery": "2.0"
        },
        "invokedBy": "AWS Internal"
    },
    "eventTime": "2021-11-21T05:25:32Z",
    "eventSource": "kms.amazonaws.com",
    "eventName": "Decrypt",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "AWS Internal",
    "errorCode": "AccessDenied",
    "errorMessage": "User: arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363 is not authorized to perform: kms:Decrypt on resource: arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03 because no resource-based policy allows the kms:Decrypt action",
    "requestParameters": null,
    "responseElements": null,
    "requestID": "b79ccf8e-51e7-48a3-a933-a56aacbb99db",
    "eventID": "ecfcb017-c7de-45d2-9bfc-2f8d94e260fe",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management"
}

エラーメッセージの親切仕様は2021年11月の以下のアップデートによるものだと思います。(こんなに親切だった記憶がないため)

EC2からS3へファイルのアップロード

当初に設定したIAMポリシーへ戻しました。ファイルのダウンロードの冒頭のIAMポリシーと同じです。

policy

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

EC2からS3へファイルアップロード

ここからが本題です。EC2のローカルあるファイルをS3バケットへコピーしましたが失敗しました。暗号化するkms:Encryptがあれば良いのでは?と深く考えず設定していました。

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

CloudTrailからGenerateDataKeyのアクセス拒否されたログを確認できました。

because no resource-based policy allows the kms:GenerateDataKey actionとのことです。

イベントレコード

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAQ4BT4DHFF4UKLZA33:i-06593b38f6016a363",
        "arn": "arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363",
        "accountId": "123456789012",
        "accessKeyId": "ASIAIOFEWWNFHDMBXTQQ",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAQ4BT4DHFF4UKLZA33",
                "arn": "arn:aws:iam::123456789012:role/dev-sample-ec2-role1",
                "accountId": "123456789012",
                "userName": "dev-sample-ec2-role1"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2021-11-21T05:30:17Z",
                "mfaAuthenticated": "false"
            },
            "ec2RoleDelivery": "2.0"
        },
        "invokedBy": "AWS Internal"
    },
    "eventTime": "2021-11-21T05:39:05Z",
    "eventSource": "kms.amazonaws.com",
    "eventName": "GenerateDataKey",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "AWS Internal",
    "errorCode": "AccessDenied",
    "errorMessage": "User: arn:aws:sts::123456789012:assumed-role/dev-sample-ec2-role1/i-06593b38f6016a363 is not authorized to perform: kms:GenerateDataKey on resource: arn:aws:kms:ap-northeast-1:123456789012:key/15dfcb2a-6625-4f70-b9a6-edcec05d6f03 because no resource-based policy allows the kms:GenerateDataKey action",
    "requestParameters": null,
    "responseElements": null,
    "requestID": "d315fec2-e9c1-46dd-a6a4-8ebc31505eea",
    "eventID": "13731da2-dcbb-4d43-9965-6ea61a864701",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "123456789012",
    "eventCategory": "Management"
}

データキーを生成してデータキーを使って暗号化するという基本的なことを失念していました。

アップロードに失敗した当初はググって以下のリンクを見て原因に気づきました。CloudTrailのログがわかりやすかったので説明と紹介の意味で後付けしています。

IAMポリシーを修正しkms:GenerateDataKeyの許可を追加しました。

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

改めてファイルをアップロードし成功します。

$ aws s3 cp ./sample-file.png s3://sample-dev-blog-bucket
upload: ./sample-file.png to s3://sample-dev-blog-bucket/sample-file.png

S3バケットにコピーできたことを確認できました。ファイルのアップロードするにはファイルを暗号化するため、kms:Encryptkms:GenerateDataKeyの許可が必要でした。

$ aws s3 ls s3://sample-dev-blog-bucket
2021-11-20 06:58:36         22 README.md
2021-11-21 06:05:10      51573 sample-file.png

まとめ

KMSに限れば以下のIAMポリシーの許可が必要でした。

  • ファイルのアップロードは暗号化するためにkms:Decryptと、kms:GenerateDataKey2つ許可が必要です。
  • ファイルのダウンロードは複合するためにkms:Decryptの許可が必要です。

その他、S3バケットへのs3:GetObjects3:PutObjectも必要です。

SSE-KMSで暗号化したS3バケットのファイル操作、KMSの権限については以下のリンクが勉強になりました。

CloudTrailからKMSのログを確認するときは、イベントソースをkms.amazonaws.comでフィルターすると探しやすいです。

おわりに

AWSのセキュリティ試験で散々KMSを勉強していたのですが凡ミスをしました。自戒の意味を込めて失敗例に補足説明を加えて書き残しました。

参考