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

2023.05.10

こんにちは。たかやまです。

SSE-KMSで暗号化したS3バケットのレプリケーションをする機会があったので、今回はその時の設定方法をまとめていきたいと思います。

アーキテクチャ

アーキテクチャの概要はこちらです。

やってみた

送信先アカウント

送信先アカウントでは以下のリソースを作成していきます。

  • カスタマーマネージドキー
  • レプリケーションを許可したS3バケット

カスタマーマネージドキーの作成

クロスアカウントレプリケーションの場合は、以下の注記にあるようにAWSマネージドキーはキーポリシーが変更できないためカスタマーマネージドキーを作成する必要があります。

このあと作成するカスタマーマネージドキーでは送信元アカウントがアクセスできるようなキーポリシーを設定していく必要があります。

AWS マネージドキー で暗号化されたオブジェクトは、キーポリシーを変更できないため、アカウント間で共有することはできません。SSE-KMS データをクロスアカウントに複製する必要がある場合は、AWS KMS の カスタマーマネージドキーを使用する必要があります。

サーバー側の暗号化 (SSE-C、SSE-S3、SSE-KMS) で作成されたオブジェクトをレプリケートする - Amazon Simple Storage Service

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をコピーしておきます。

送信先S3バケットの作成

次にレプリケーション先のS3バケットを作成します。

バケットを作成するCloudFormationテンプレートを用意しているので、良ければ活用してください。

CloudFormationサンプル

各パラメータは以下のように設定してください。

  • ProjectName : 適当な名前
  • Environment : 適当な名前
  • KmsKeyArn : 作成したカスタマーマネージドキーのARN
  • SourceAccountId : 送信元アカウントID
Parameters:
  ProjectName:
    Type: String
    Default: test
    ConstraintDescription: This parameter is required.
    Description: (required)The name of the project.
    MinLength: 1
  Environment:
    Type: String
    Default: dev
    ConstraintDescription: This parameter is required.
    Description: (required)The name of the environment.
    MinLength: 1
  KmsKeyArn:
    Type: String
    Default: ""
    Description: KMS Key ARN of your own account
  SourceAccountId:
    Type: String
    Default: ""
    Description: Your Source AccountID
Resources:
  DestinationBucket4BECDB47:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - BucketKeyEnabled: true
            ServerSideEncryptionByDefault:
              KMSMasterKeyID:
                Ref: KmsKeyArn
              SSEAlgorithm: aws:kms
      BucketName:
        Fn::Join:
          - ""
          - - Ref: ProjectName
            - "-"
            - Ref: Environment
            - -s3
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
  DestinationBucketPolicyFCD81088:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: DestinationBucket4BECDB47
      PolicyDocument:
        Statement:
          - Action:
              - s3:ReplicateDelete
              - s3:ReplicateObject
            Condition:
              ArnLike:
                aws:PrincipalArn: arn:aws:iam::522303440560:role/source-dev-role-replication
            Effect: Allow
            Principal:
              AWS: "*"
            Resource:
              Fn::Join:
                - ""
                - - Fn::GetAtt:
                      - DestinationBucket4BECDB47
                      - Arn
                  - /*
            Sid: Set permissions for objects
          - Action:
              - s3:GetBucketVersioning
              - s3:List*
              - s3:PutBucketVersioning
            Condition:
              ArnLike:
                aws:PrincipalArn:
                  Fn::Join:
                    - ""
                    - - "arn:aws:iam::"
                      - Ref: SourceAccountId
                      - :role/source-dev-role-replication
            Effect: Allow
            Principal:
              AWS: "*"
            Resource:
              Fn::GetAtt:
                - DestinationBucket4BECDB47
                - Arn
            Sid: Set permissions on bucket
        Version: "2012-10-17"

バケットは以下の設定で作成していきます。

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

バケットが作成できたら、送信元アカウントからレプリケーションを許可するバケットポリシーを設定します。

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

公式で案内されているポリシーはPrincipalでアクセス制御を行っています。ただ、Principalに定義できる値は現在存在しているIAM Roleのみ指定でき、将来的に作成されるアカウントIDが入ったIAM Roleを指定することはできません。

ここではワークアラウンド的に、Principalを*で指定し、ConditionでArnLikeを指定することで、IAM Roleの作成を待たずにレプリケーションを許可を実装しています。

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

設定例:

送信元アカウント

送信先アカウントの設定が終えたら、次に送信元アカウントの設定を行います。

送信元アカウントでは以下のリソースを作成していきます。

  • IAM Role
  • レプリケーションを行うS3バケット

IAM RoleとS3バケットを作成するCloudFormationテンプレートも用意しているので、良ければ活用してください。

CloudFormationサンプル

各パラメータは以下のように設定してください。

  • ProjectName : 適当な名前
  • Environment : 適当な名前
  • DestinationAccountId : 送信先アカウントID
  • DestinationBucketName : 送信先のバケット名
  • DestinationKmsKeyArn : 送信先アカウントで作成したカスタマーマネージドキーARN
Parameters:
  ProjectName:
    Type: String
    Default: test
    ConstraintDescription: This parameter is required.
    Description: (required)The name of the project.
    MinLength: 1
  Environment:
    Type: String
    Default: dev
    ConstraintDescription: This parameter is required.
    Description: (required)The name of the environment.
    MinLength: 1
  DestinationAccountId:
    Type: String
    Default: ""
    Description: Your Destination AccountID
  DestinationBucketName:
    Type: String
    Default: ""
    Description: Audit Bucket name of the Destination Account
  DestinationKmsKeyArn:
    Type: String
    Default: ""
    Description: KMS Key ARN of the Destination Account
Resources:
  SourceBucketDDD2130A:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - BucketKeyEnabled: true
            ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
      BucketName:
        Fn::Join:
          - ""
          - - Ref: ProjectName
            - "-"
            - Ref: Environment
            - -s3
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      ReplicationConfiguration:
        Role:
          Fn::GetAtt:
            - ReplicationRoleCE149CEC
            - Arn
        Rules:
          - DeleteMarkerReplication:
              Status: Disabled
            Destination:
              AccessControlTranslation:
                Owner: Destination
              Account:
                Ref: DestinationAccountId
              EncryptionConfiguration:
                ReplicaKmsKeyID:
                  Ref: DestinationKmsKeyArn
              Bucket:
                Fn::Join:
                  - ""
                  - - "arn:aws:s3:::"
                    - Ref: DestinationBucketName
              Metrics:
                Status: Enabled
              ReplicationTime:
                Status: Disabled
                Time:
                  Minutes: 15
              StorageClass: STANDARD
            Filter:
              Prefix: ""
            Id:
              Fn::Join:
                - ""
                - - Ref: ProjectName
                  - "-"
                  - Ref: Environment
                  - -replication-rule
            Priority: 1
            SourceSelectionCriteria:
              SseKmsEncryptedObjects:
                Status: Enabled
            Status: Enabled
      VersioningConfiguration:
        Status: Enabled
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
  ReplicationRoleCE149CEC:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: s3.amazonaws.com
        Version: "2012-10-17"
      RoleName:
        Fn::Join:
          - ""
          - - Ref: ProjectName
            - "-"
            - Ref: Environment
            - -role-replication
  ReplicationPolicy48D09A53:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - s3:GetReplicationConfiguration
              - s3:ListBucket
            Effect: Allow
            Resource:
              Fn::GetAtt:
                - SourceBucketDDD2130A
                - Arn
          - Action:
              - s3:GetObjectVersionAcl
              - s3:GetObjectVersionForReplication
              - s3:GetObjectVersionTagging
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - Fn::GetAtt:
                      - SourceBucketDDD2130A
                      - Arn
                  - /*
          - Action:
              - s3:ReplicateDelete
              - s3:ReplicateObject
              - s3:ReplicateTags
            Effect: Allow
            Resource:
              Fn::Join:
                - ""
                - - "arn:aws:s3:::"
                  - Ref: DestinationBucketName
                  - /*
          - Action:
              - kms:Decrypt
              - kms:DescribeKey
              - kms:Encrypt
              - kms:GenerateDataKey*
              - kms:ReEncrypt*
            Effect: Allow
            Resource:
              Ref: DestinationKmsKeyArn
        Version: "2012-10-17"
      PolicyName:
        Fn::Join:
          - ""
          - - Ref: ProjectName
            - "-"
            - Ref: Environment
            - -policy-replication
      Roles:
        - Ref: ReplicationRoleCE149CEC

IAM Roleの作成

IAM Roleは以下の許可ポリシーと信頼関係のものを作成します。

  • 許可ポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:GetReplicationConfiguration",
                "s3:ListBucket"
            ],
            "Resource": "arn:aws:s3:::<送信元アカウントバケット名>",
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionForReplication",
                "s3:GetObjectVersionTagging"
            ],
            "Resource": "arn:aws:s3:::<送信元アカウントバケット名>/*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:ReplicateDelete",
                "s3:ReplicateObject",
                "s3:ReplicateTags"
            ],
            "Resource": "arn:aws:s3:::<送信先アカウントバケット名>/*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "kms:Decrypt",
                "kms:DescribeKey",
                "kms:Encrypt",
                "kms:GenerateDataKey*",
                "kms:ReEncrypt*"
            ],
            "Resource": "<送信先アカウントで作成したカスタマーマネージドキーARN>",
            "Effect": "Allow"
        }
    ]
}

許可のセットアップ - Amazon Simple Storage Service

  • 信頼関係
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "s3.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

送信元S3バケットの作成

レプリケーション元のS3バケットは以下の設定で作成していきます。

  • バケット名 : 適当な名前
  • AWSリージョン : 送信元アカウントのS3バケットと同じリージョン
  • バケットのバージョニング : 有効
  • デフォルト暗号化
    • 暗号化キータイプ : AWS Key Management Serviceキー(SSE-KMS)
    • AWS KMSキー : AWS KMSキーARNを入力する
    • AWS KMSキー ARN : AWSマネージド型キー aws/s3のARN

バケットが作成できたら、送信先アカウントのS3バケットにレプリケーションを行うための設定をしていきます。

バケットの管理タブ -> レプリケーションルールを作成を選択します。

レプリケーションルールは以下の設定で作成していきます。

  • レプリケーションルール名 : 適当な名前
  • 送信先
    • 送信先 : 別のアカウントのバケットを指定する
    • アカウントID : 送信先アカウントID
    • バケット名 : 送信先バケット名
    • オブジェクト所有者を送信先バケット所有者に変更 : 有効
  • IAMロール
    • IAMロールARN : 作成したIAM Role ARN
  • 暗号化
    • AWS KMSで暗号化されたオブジェクトをレプリケートする : 有効
    • AWS KMSキーARN : 送信先アカウントで作成したカスタマーマネージドキーARN
  • 追加のレプリケーションオプション
    • レプリケーションメトリクス : 有効

追加のレプリケーションオプションは必要に応じて設定していただければと思いますが、レプリケーションメトリクスはレプリケーションの状態を確認するために設定しておくと良いかと思います。

レプリケーションの確認

送信先アカウントと送信元アカウントの設定が完了したら、送信元アカウントにオブジェクトをアップロードし、送信先アカウントのバケットにレプリケーションされているか確認していきます。

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

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

また、この時オブジェクトは送信元のKMSキーで暗号化されていることがわかります。

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

レプリケーションされたオブジェクトを確認すると、こちらは送信先アカウントのKMSキーで暗号化されていることがわかります。

もちろんオブジェクトは送信先アカウントがアクセスできるKMSキーで暗号化されているため正常にダウンロードすることができます。

誤ったKMSキーを指定した場合

ちなみに、送信先アカウントがアクセスできないKMSキーでもレプリケーションすることができてしまいます。

この状態でレプリケーションすると、送信先アカウントにレプリケーションされたオブジェクトは送信元アカウントのKMSキーで暗号化されていることが確認できます。

このオブジェクトを送信先アカウントでダウンロードしようとすると、KMSキーにアクセスできないためダウンロードすることができません。

なのでレプリケーションの成功だけでなく、送信先アカウントで問題なくダウンロードできることも確認していただければと思います。

<Error>
<Code>AccessDenied</Code>
<Message>The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.</Message>
<RequestId>MA5ZER5X2EZPV05K</RequestId>
<HostId>jLSg9zEMEjTpO4dbH06xYaroZ7ps9pyAzAWADURuDCxPvk3h0l8DwASfl31BOBc6exijoV3CZJwmQTfOdL0EMQ==</HostId>
</Error>

最後に

SSE-KMSを利用したクロスアカウントレプリケーションの設定方法を紹介しました。

KMSが絡んでくると思わぬところでアクセスできない問題が起きたりするので、レプリケーションしたオブジェクトは送信先アカウントで問題なくダウンロードできることも確認しておくと良いかと思います。

以上、たかやま(@nyan_kotaroo)でした。