aws-cdk icon indicating copy to clipboard operation
aws-cdk copied to clipboard

feat(s3): replicating objects

Open badmintoncryer opened this issue 1 year ago • 6 comments

Issue # (if applicable)

Closes #1680.

Reason for this change

AWS S3 supports configuring object replication , but the s3.Bucket construct does not support it.

Description of changes

Added replicationRules to BucketProps.

Replication configuration version

There are two versions of replication configuration. This PR uses only the V2 replication configuration to enable the specification of the Filter element and S3 Replication Time Control (S3 RTC).

To use V2 replication configuration, this PR explicitly specifies Filter.Prefix property.

        const prefix = rule.prefixFilter ?? '';
        const filter = isAndFilter ? {
          and: {
            prefix,
            tagFilters: rule.tagFilter,
          },
        } : {
          prefix,
        };

V2 replication configuration has some restriction:

ReplicationStack | 4/7 | 9:22:08 PM | CREATE_FAILED        | AWS::S3::Bucket  | SourceBucket (SourceBucketDDD2130A) Resource handler returned message:
Delete marker replication is not supported if any Tag filter is specified. Please refer to S3 Developer Guide for more information. (Service: S3, Status Code: 400, Request ID: XXX, Extended Request ID: XXX)
ReplicationStack | 4/7 | 9:12:08 PM | CREATE_FAILED        | AWS::S3::Bucket  | SourceBucket (SourceBucketDDD2130A) Resource handler returned message:
Priority must be specified for this version of Cross Region Replication configuration schema. Please refer to S3 Developer Guide for more information. (Service: S3, Status Code: 400, Request ID: XXX, Extended Request ID: XXX)

These restriction is not documented but there are some posts about these points.

  • https://repost.aws/questions/QUiEc8wFE_Q16fX5WG-YWnrA/cloudformation-support-for-s3-replication-to-multiple-destination-buckets

To resolve these problems,I made the priority required and explicitly set the deleteMarkerReplication.

       const prefix = rule.prefixFilter ?? ''; // set empty string to use V2 replication configuration
        const filter = isAndFilter ? {
          and: {
            prefix,
            tagFilters: rule.tagFilter,
          },
        } : {
          prefix,
        };

        return {
          id: rule.id,
          priority: rule.priority,
          status: 'Enabled',
          destination: {
            bucket: rule.destination.bucket.bucketArn,
            account: rule.destination.account,
            storageClass: rule.storageClass?.toString(),
            accessControlTranslation: rule.destination.accessControlTransition ? {
              owner: 'Destination',
            } : undefined,
            encryptionConfiguration: rule.kmsKey ? {
              replicaKmsKeyId: rule.kmsKey.keyArn,
            } : undefined,
            replicationTime: rule.replicationTimeControl !== undefined ? {
              status: rule.replicationTimeControl ? 'Enabled' : 'Disabled',
              time: {
                minutes: 15,
              },
            } : undefined,
            metrics: rule.replicationTimeControlMetrics !== undefined ? {
              status: rule.replicationTimeControlMetrics ? 'Enabled' : 'Disabled',
              eventThreshold: {
                minutes: 15,
              },
            } : undefined,
          },
          filter,
          // To avoid deploy error when there are multiple replication rules with undefined deleteMarkerReplication,
          // CDK explicitly set the deleteMarkerReplication if it is undefined.
          deleteMarkerReplication: {
            status: rule.deleteMarkerReplication ? 'Enabled' : 'Disabled',
          },
          sourceSelectionCriteria,
        };

IAM permission

There is a documentation to setup IAM permissions for service role.

{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "s3:GetReplicationConfiguration",
            "s3:ListBucket"
         ],
         "Resource":[
            "arn:aws:s3:::SRC-BUCKET"
         ]
      },
      {
         "Effect":"Allow",
         "Action":[
            "s3:GetObjectVersionForReplication",
            "s3:GetObjectVersionAcl",
            "s3:GetObjectVersionTagging"
         ],
         "Resource":[
            "arn:aws:s3:::SRC-BUCKET/*"
         ]
      },
      {
         "Effect":"Allow",
         "Action":[
            "s3:ReplicateObject",
            "s3:ReplicateDelete",
            "s3:ReplicateTags"
         ],
         "Resource":"arn:aws:s3:::DST-BUCKET/*"
      }
   ]
}

However, there are discrepancies between the automatically generated IAM policies in the management console and the IAM policies in the documentation.

Generated Policy:

{
    "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:::SRC-BUCKET",
                "arn:aws:s3:::SRC-BUCKET/*"
            ]
        },
        {
            "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:::DST-BUCKET/*"
            ]
        },
        {
            "Action": [
                "kms:Decrypt"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLike": {
                    "kms:ViaService": "s3.ap-northeast-1.amazonaws.com",
                    "kms:EncryptionContext:aws:s3:arn": [
                        "arn:aws:s3:::SRC-BUCKET/*"
                    ]
                }
            },
            "Resource": [
                "arn:aws:kms:ap-northeast-1:123456789012:key/hogehuga"
            ]
        },
        {
            "Action": [
                "kms:Encrypt"
            ],
            "Effect": "Allow",
            "Condition": {
                "StringLike": {
                    "kms:ViaService": [
                        "s3.ap-northeast-1.amazonaws.com"
                    ],
                    "kms:EncryptionContext:aws:s3:arn": [
                        "arn:aws:s3:::DST-BUCKET*"
                    ]
                }
            },
            "Resource": [
                "arn:aws:kms:ap-northeast-1:123456789012:key/hogefuga"
            ]
        }
    ]
}

I adopted the policy from the document. I look forward to hearing your thoughts on this matter.

Description of how you validated changes

Added both unit and integ tests.

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

badmintoncryer avatar Jul 28 '24 14:07 badmintoncryer

Hi @badmintoncryer , Thank you for submitting this PR, as this feature is adding some additional pemissions for s3, we'll need to have an internal security review while we go through this PR. Will keep you posted !!

shikha372 avatar Aug 15 '24 22:08 shikha372

Hi @badmintoncryer, to update had the first security review for this PR, currently concern is around populating correct permissions for opt-in regions, will let you know if any changes are required to address it.

shikha372 avatar Sep 10 '24 17:09 shikha372

@rix0rrr Thank you for your review! I've addressed all of your comments.

badmintoncryer avatar Sep 28 '24 22:09 badmintoncryer

Hello @badmintoncryer , Apologies to keep you waiting, have completed the feature review for this PR. Current ask is if we can add integ test for bidirectional replication and for cross-account replication(if possible) as well before final approval.

shikha372 avatar Oct 16 '24 20:10 shikha372

@shikha372 Thank you for your information.

add integ test for bidirectional replication

What is the meaning of bidirectional replication? Are you suggesting that we prepare Bucket A and Bucket B, and designate each other as their respective replication destinations?

 cross-account replication(if possible)

I don't have two AWS account... Would it be better to create a new account and run the tests?

badmintoncryer avatar Oct 16 '24 21:10 badmintoncryer

What is the meaning of bidirectional replication? Are you suggesting that we prepare Bucket A and Bucket B, and designate each other as their respective replication destinations?

  • Yes, since this can also be supported using new feature, we'll need a integ test for this case.

I don't have two AWS account... Would it be better to create a new account and run the tests?

  • Got it, in that case I will help adding integ test for this one

shikha372 avatar Oct 24 '24 16:10 shikha372

I considered bidirectional replication. First, I added an addReplicationRules() method to allow the destination bucket to be set lazily.

  public addReplicationRules(rules: ReplicationRule[]): void {
    this.replicationRules.push(...rules);
  }

However, it seems that the circular reference between the source and destination buckets cannot be resolved.

Do you have any suggestions on the implementation?

badmintoncryer avatar Oct 27 '24 11:10 badmintoncryer

I considered bidirectional replication. First, I added an addReplicationRules() method to allow the destination bucket to be set lazily.

  public addReplicationRules(rules: ReplicationRule[]): void {
    this.replicationRules.push(...rules);
  }

However, it seems that the circular reference between the source and destination buckets cannot be resolved.

Do you have any suggestions on the implementation?

Thanks @badmintoncryer , working on adding cross-account integ test will give it a try at my end for bi-directional.

shikha372 avatar Nov 18 '24 19:11 shikha372

Thank you very much @shikha372

badmintoncryer avatar Nov 18 '24 23:11 badmintoncryer

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 81.48%. Comparing base (717d91d) to head (f543e0f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #30966   +/-   ##
=======================================
  Coverage   81.48%   81.48%           
=======================================
  Files         226      226           
  Lines       13768    13768           
  Branches     2416     2416           
=======================================
  Hits        11219    11219           
  Misses       2271     2271           
  Partials      278      278           
Flag Coverage Δ
suite.unit 81.48% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
packages/aws-cdk 80.89% <ø> (ø)
packages/aws-cdk-lib/core 82.10% <ø> (ø)

codecov[bot] avatar Nov 27 '24 14:11 codecov[bot]

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify[bot] avatar Jan 02 '25 19:01 mergify[bot]

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify[bot] avatar Jan 02 '25 19:01 mergify[bot]

@mergifyio update

badmintoncryer avatar Jan 03 '25 02:01 badmintoncryer

update

✅ Branch has been successfully updated

mergify[bot] avatar Jan 03 '25 02:01 mergify[bot]

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify[bot] avatar Jan 03 '25 03:01 mergify[bot]

@mergify update

badmintoncryer avatar Jan 03 '25 21:01 badmintoncryer

update

✅ Branch has been successfully updated

mergify[bot] avatar Jan 03 '25 21:01 mergify[bot]

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify[bot] avatar Jan 03 '25 22:01 mergify[bot]

Hi @badmintoncryer , tried to merge it yesterday but I think there is unit test coverage missing for new addReplication method, I can get back to it next week

shikha372 avatar Jan 03 '25 22:01 shikha372

Thank you very much @shikha372 . Looking forward to the updates!

badmintoncryer avatar Jan 04 '25 12:01 badmintoncryer

@mergify refresh

badmintoncryer avatar Jan 15 '25 23:01 badmintoncryer

refresh

✅ Pull request refreshed

mergify[bot] avatar Jan 15 '25 23:01 mergify[bot]

@mergify update

badmintoncryer avatar Jan 15 '25 23:01 badmintoncryer

update

✅ Branch has been successfully updated

mergify[bot] avatar Jan 15 '25 23:01 mergify[bot]

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify[bot] avatar Jan 15 '25 23:01 mergify[bot]

@mergify update

badmintoncryer avatar Jan 16 '25 00:01 badmintoncryer

update

✅ Branch has been successfully updated

mergify[bot] avatar Jan 16 '25 00:01 mergify[bot]

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify[bot] avatar Jan 16 '25 00:01 mergify[bot]

@mergify update

badmintoncryer avatar Jan 16 '25 05:01 badmintoncryer

update

✅ Branch has been successfully updated

mergify[bot] avatar Jan 16 '25 05:01 mergify[bot]