SSE-KMSで暗号化したS3バケット同士のクロスアカウントレプリケーションでは、S3バケットキーの有無に注意してほしい

S3バケットキーが有効になっている場合、レプリケーションルール作成時にAWSが自動で作成してくれるIAMロールではレプリケーションに失敗します。今回はクロスアカウントレプリケーションの設定方法と共にハマったポイントをお伝えします。
2023.11.17

レプリケーションが上手くいかない

こんにちは!AWS事業本部のおつまみです。

みなさん、SSE-KMS暗号化したS3バケット同士のクロスアカウントレプリケーションをやりたいと思ったことはありますか?私はあります。

今回やってみたのですが、思わぬところでハマりました。
今回はその設定方法と共にハマったポイントをお伝えします。

いきなり結論:ハマったポイント

  • S3バケットキーを有効にしている場合、レプリケーションルール作成時にAWSが自動で作成するIAMロールではレプリケーションに失敗する。
  • IAMポリシーの暗号化コンテキストにバケットARNを設定する必要がある。

アーキテクチャー

今回構築するアーキテクチャーです。

赤字吹き出しのIAMポリシーの変更が今回ハマったポイントです。
他の吹き出しの権限周りの設定もハマりやすいので、1つずつ設定内容をみていきましょう。

大まかな流れは以下のとおりです。

  1. AWS KMS (SSE−KMS)の作成
  2. S3バケットの作成
  3. レプリケーションルールの作成(送信元S3バケット)
  4. レプリケーションルールのIAMポリシー変更(送信元S3バケット)
  5. バケットポリシーの設定(送信先S3バケット)

やってみた

1. AWS KMS (SSE−KMS)の作成

まずは各アカウントでAWS KMS (SSE−KMS)を作成します。

前提として、クロスアカウントレプリケーションの場合は、AWS KMS (SSE−KMS)の作成が必須です。なぜなら、以下の注記にあるようにAWSマネージドキーでは、キーポリシーの変更ができないためです。

AWS マネージドキー で暗号化されたオブジェクトは、キーポリシーを変更できないため、アカウント間で共有することはできません。SSE-KMS データをクロスアカウントに複製する必要がある場合は、AWS KMS の カスタマーマネージドキーを使用する必要があります。
サーバー側の暗号化 (SSE-C、SSE-S3、SSE-KMS、DSSE-KMS) で作成されたオブジェクトをレプリケートする - Amazon Simple Storage Service

そのため、まずは各アカウントでAWS KMS (SSE−KMS)を作成します。
どちらのアカウントから作成しても問題ありませんが、今回は送信先アカウントから作成します。

KMSコンソールでキーの作成を選択します。

S3では非対称暗号化KMSキーはサポートしていないので、対称暗号化KMSキーを作成します。

エイリアスは、任意の値を設定してください。

キーの管理アクセス許可は追加不要のため、そのまま進めます。

キーの使用アクセス許可を定義では、別のAWSアカウントの箇所で送信元のアカウントIDを追加します。 これにより、送信元アカウントからアクセスできるようになります。

最終的には以下のようなキーポリシーが定義されます。

{
    "Id": "key-consolepolicy-3",
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Enable IAM User Permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<送信先アカウント>:root"
            },
            "Action": "kms:*",
            "Resource": "*"
        },
        {
            "Sid": "Allow use of the key",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<送信元アカウント>:root"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "*"
        },
        {
            "Sid": "Allow attachment of persistent resources",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<送信元アカウント>:root"
            },
            "Action": [
                "kms:CreateGrant",
                "kms:ListGrants",
                "kms:RevokeGrant"
            ],
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "kms:GrantIsForAWSResource": "true"
                }
            }
        }
    ]
}

作成できたら、ARNをコピーしておきます。

同様な手順で送信元アカウントにて、AWS KMS (SSE−KMS)を作成します。

2. S3バケットの作成

次にS3バケットを作成します。
どちらのアカウントから作成しても問題ありませんが、キーの作成と同様に送信先アカウントから作成します。

バケットの作成で設定が必須の箇所を抜粋します。

  • バケット名 : 任意の名前
  • AWSリージョン : 送信元アカウントのS3バケットと同じリージョン
  • バケットのバージョニング : 有効
  • デフォルト暗号化
    • 暗号化キータイプ : AWS Key Management Serviceキー(SSE-KMS)
    • AWS KMSキー : AWS KMSキーARNを選択する
    • AWS KMSキー ARN : 作成したAWS KMSキーのARN

同様な手順で送信元アカウントにて、S3バケットを作成します。

3. レプリケーションルールの作成(送信元S3バケット)

送信元S3バケットでレプリケーションルールを作成します。

管理タブからレプリケーションルールの作成を選択します。

レプリケーションルールの設定を行います。

設定が必須の箇所を抜粋します。

  • レプリケーションルール名 : 任意の名前
  • 送信先
    • 送信先 : 別のアカウントのバケットを指定する
    • アカウントID : 送信先アカウントID
    • バケット名 : 送信先バケット名
    • オブジェクト所有者を送信先バケット所有者に変更 : 有効
  • IAMロール
    • IAMロール : 新しいロールの作成 ※後の手順で修正が必要
  • 暗号化
    • AWS KMSで暗号化されたオブジェクトをレプリケートする : 有効
    • ソースオブジェクトを複合するためのAWS KMSキー : 送信元アカウントで作成したAWS KMSキーARN
    • 送信先オブジェクトを暗号化するためのAWS KMSキー : AWS KMSキーを入力する
    • AWS KMSキーARN : 送信先アカウントで作成したAWS KMSキーARN

4. レプリケーションルールのIAMポリシー変更(送信元S3バケット)

今回ここがハマったポイントです。
レプリケーションルール作成時にAWSが自動で作成したIAMロールでは、権限が足りず、レプリケーションに失敗します。

理由はS3バケットキーが有効になっている場合、IAMポリシーの暗号化コンテキストにバケットARNを設定する必要があるためです。

S3バケットのデフォルト設定では、暗号化のバケットキーが有効になっています。
S3バケット作成画面をもう一度見てみましょう。

このバケットキーが使用されている場合、下記の公式ドキュメントに記載の通りバケット ARN を暗号化コンテキストとして使用する必要があります。
※暗号化コンテキストとは、AWS KMSを利用する場合の暗号化操作に追加的なセキュリティ情報を提供するためのものです。

S3 バケットキーは、バケット ARN を暗号化コンテキストとして使用します。S3 バケットキーを有効にする前に、バケット ARN を暗号化コンテキストとして使用するため、IAM ポリシーまたは AWS KMS キーポリシーを更新してください。
Amazon S3 バケットキーを使用した SSE−KMS のコストの削減 - Amazon Simple Storage Service

今回AWSが自動で作成したレプリケーションルールのIAMロールをみてみましょう。
作成したレプリケーションルールを選択し、ルールの編集を選択します。

IAMロールのセクションで表示を選択します。

JSONをコピーを選択し、内容を確認します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket",
                "s3:GetReplicationConfiguration",
                "s3:GetObjectVersionForReplication",
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionTagging",
                "s3:GetObjectRetention",
                "s3:GetObjectLegalHold"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::<送信元アカウントバケット名>",
                "arn:aws:s3:::<送信元アカウントバケット名>/*"
            ]
        },
        {
            "Action": [
                "s3:ReplicateObject",
                "s3:ReplicateDelete",
                "s3:ReplicateTags",
                "s3:GetObjectVersionTagging",
                "s3:ObjectOwnerOverrideToBucketOwner"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLikeIfExists": {
                    "s3:x-amz-server-side-encryption": [
                        "aws:kms",
                        "aws:kms:dsse",
                        "AES256"
                    ]
                }
            },
            "Resource": [
                "arn:aws:s3:::<送信先アカウントバケット名>/*"
            ]
        },
        {
            "Action": [
                "kms:Decrypt"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLike": {
                    "kms:ViaService": "s3.ap-northeast-1.amazonaws.com",
                    "kms:EncryptionContext:aws:s3:arn": [
                        "arn:aws:s3:::<送信元アカウントバケット名>/*"
                    ]
                }
            },
            "Resource": [
                "<送信元アカウントで作成したAWS KMSキーARN>"
            ]
        },
        {
            "Action": [
                "kms:Encrypt"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLike": {
                    "kms:ViaService": [
                        "s3.ap-northeast-1.amazonaws.com"
                    ],
                    "kms:EncryptionContext:aws:s3:arn": [
                        "arn:aws:s3:::<送信先アカウントバケット名>/*"
                    ]
                }
            },
            "Resource": [
                "<送信先アカウントで作成したAWS KMSキーARN>"
            ]
        }
    ]
}

ハイライトをつけた箇所が暗号化コンテキスト("kms:EncryptionContext:aws:s3:arn")となりますが、 (バケットの ARN)/* つまりオブジェクトARNのみに制限されています。
この場合、バケットキーが無効になっていればレプリケーションが成功しますが、今回有効になっているためこのままではレプリケーション失敗してしまいます。

そのため、暗号化コンテキストにバケット ARN を追加します。
先ほどのコンソール画面から、編集を選択します。

下記の通りポリシーを修正します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket",
                "s3:GetReplicationConfiguration",
                "s3:GetObjectVersionForReplication",
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionTagging",
                "s3:GetObjectRetention",
                "s3:GetObjectLegalHold"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::<送信元アカウントバケット名>",
                "arn:aws:s3:::<送信元アカウントバケット名>/*"
            ]
        },
        {
            "Action": [
                "s3:ReplicateObject",
                "s3:ReplicateDelete",
                "s3:ReplicateTags",
                "s3:GetObjectVersionTagging",
                "s3:ObjectOwnerOverrideToBucketOwner"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLikeIfExists": {
                    "s3:x-amz-server-side-encryption": [
                        "aws:kms",
                        "aws:kms:dsse",
                        "AES256"
                    ]
                }
            },
            "Resource": [
                "arn:aws:s3:::<送信先アカウントバケット名>/*"
            ]
        },
        {
            "Action": [
                "kms:Decrypt"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLike": {
                    "kms:ViaService": "s3.ap-northeast-1.amazonaws.com",
                    "kms:EncryptionContext:aws:s3:arn": [
                        "arn:aws:s3:::<送信元アカウントバケット名>/*",
                        "arn:aws:s3:::<送信元アカウントバケット名>"
                    ]
                }
            },
            "Resource": [
                "<送信元アカウントで作成したAWS KMSキーARN>"
            ]
        },
        {
            "Action": [
                "kms:Encrypt"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLike": {
                    "kms:ViaService": [
                        "s3.ap-northeast-1.amazonaws.com"
                    ],
                    "kms:EncryptionContext:aws:s3:arn": [
                        "arn:aws:s3:::<送信先アカウントバケット名>/*",
                        "arn:aws:s3:::<送信先アカウントバケット名>"
                    ]
                }
            },
            "Resource": [
                "<送信先アカウントで作成したAWS KMSキーARN>"
            ]
        }
    ]
}

修正後、次へを選択します。

確認画面で変更を保存を選択します。

5. バケットポリシーの設定(送信先S3バケット)

最後に送信先S3バケットで送信元アカウントからレプリケーションを許可するバケットポリシーを設定します。

アクセス許可タブからバケットポリシーのセクションで編集を選択します。

以下のポリシーを設定します。

{
   "Version":"2012-10-17",
   "Id":"",
   "Statement":[
      {
         "Sid":"Set-permissions-for-objects",
         "Effect":"Allow",
         "Principal":{
            "AWS":"<送信元アカウントのレプリケーションRole ARN>"
         },
         "Action":["s3:ReplicateObject", "s3:ReplicateDelete","s3:ReplicateTags","s3:ObjectOwnerOverrideToBucketOwner"],
         "Resource":"arn:aws:s3:::<送信先アカウントバケット名>/*"
      },
      {
         "Sid":"Set permissions on bucket",
         "Effect":"Allow",
         "Principal":{
            "AWS":"<送信元アカウントのレプリケーションRole ARN>"
         },
         "Action":["s3:List*", "s3:GetBucketVersioning", "s3:PutBucketVersioning"],
         "Resource":"arn:aws:s3:::<送信先アカウントバケット名>"
      }
   ]
}

参考:レプリケート元バケットとレプリケート先バケットが異なるアカウントによって所有されている場合での、レプリケーションの設定 - Amazon Simple Storage Service

ポリシー設定後、変更の保存を選択します。

以上で設定完了です。

動作確認

送信元アカウントにオブジェクトをアップロードし、送信先アカウントのバケットにレプリケーションされるか確認します。
送信元アカウントにオブジェクトをアップロードします。

数秒後、オブジェクトのプロパティを確認するとレプリケーションステータスがCOMPLETEDとなっており、成功していることが確認できます。

送信先アカウントを確認すると、送信元アカウントのオブジェクトがレプリケーションされていることが確認できます。

ちなみにレプリケーションステータスはREPLICAとなっています。

IAMロールを修正しなかった場合

手順4のIAMロールを修正を行わずに送信元アカウントにオブジェクトをアップロードすると、このようにレプリケーションステータスがFAILEDとなります。

繰り返しの説明となりますが、バケットで S3バケットキーが有効になっている場合、暗号化コンテキストはバケットレベルで設定する必要があります。

バケットキーを無効にした場合

念の為バケットキーを無効にした場合、AWSが自動で作成してくれるIAMロールで問題ないか確認してみましょう。 送信元S3バケットのバケットキーを無効にします。

送信元アカウントにオブジェクトをアップロードします。

オブジェクトのプロパティを確認するとレプリケーションステータスがCOMPLETEDとなっており、成功していることが確認できます。

送信先アカウントを確認すると、レプリケーションされていることが確認できます。

よって、バケットキーが無効な場合はIAMロールの修正が不要だということがわかりました。

さいごに

今回はSSE-KMS暗号化したS3バケット同士でクロスアカウントレプリケーションする方法をお伝えしました。

まさかAWSが自動で作成してくれるIAMロールでハマると思わなかったので、調査に時間がかかりました。。
キーポリシー・バケットポリシー・IAMロールと権限周りの設定が多いため、ハマった時は1つずつ設定を確認していきましょう。

レプリケーションのトラブルシューティング - Amazon Simple Storage Service

宛先バケットにレプリケートされていない S3 オブジェクトのトラブルシューティング | AWS re:Post

最後までお読みいただきありがとうございました!
どなたかのお役に立てれば幸いです。

以上、おつまみ(@AWS11077)でした!

参考資料

SSE-KMS暗号化したS3バケットのクロスアカウントレプリケーションをやってみた | DevelopersIO

S3バケットのクロスアカウントレプリケーションを試してみた | DevelopersIO

レプリケーションのトラブルシューティング - Amazon Simple Storage Service

レプリケート元バケットとレプリケート先バケットが異なるアカウントによって所有されている場合での、レプリケーションの設定 - Amazon Simple Storage Service

Amazon S3 バケットキーを使用した SSE−KMS のコストの削減 - Amazon Simple Storage Service

Amazon S3 レプリケーションの失敗の理由 - Amazon Simple Storage Service

宛先バケットにレプリケートされていない S3 オブジェクトのトラブルシューティング | AWS re:Post