Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(s3): replicating objects #30966

Open
wants to merge 51 commits into
base: main
Choose a base branch
from

Conversation

badmintoncryer
Copy link
Contributor

@badmintoncryer badmintoncryer commented Jul 28, 2024

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.

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

@github-actions github-actions bot added effort/small Small work item – less than a day of effort feature-request A feature should be added or improved. p1 labels Jul 28, 2024
@aws-cdk-automation aws-cdk-automation requested a review from a team July 28, 2024 14:00
@github-actions github-actions bot added the distinguished-contributor [Pilot] contributed 50+ PRs to the CDK label Jul 28, 2024
Copy link
Collaborator

@aws-cdk-automation aws-cdk-automation left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pull request linter has failed. See the aws-cdk-automation comment below for failure reasons. If you believe this pull request should receive an exemption, please comment and provide a justification.

A comment requesting an exemption should contain the text Exemption Request. Additionally, if clarification is needed add Clarification Request to a comment.

@aws-cdk-automation aws-cdk-automation dismissed their stale review August 7, 2024 23:26

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

@badmintoncryer badmintoncryer marked this pull request as ready for review August 8, 2024 14:26
@aws-cdk-automation aws-cdk-automation added the pr/needs-maintainer-review This PR needs a review from a Core Team Member label Aug 8, 2024
@shikha372 shikha372 self-assigned this Aug 13, 2024
@shikha372 shikha372 added SECURITY needs-security-review Related to feature or issues that needs security review and removed SECURITY labels Aug 15, 2024
@shikha372
Copy link
Contributor

shikha372 commented Aug 15, 2024

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
Copy link
Contributor

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.

replicationRules: [
{
// The destination bucket for the replication rule.
destination: s3.ReplicationDestination.sameAccount(destinationBucket1),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a ReplicationDestination? Shouldn't the destination just be an IBucket ?

All resources will have a resourceEnv property you can use to get account and region from.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed it to the IBucket type. Thank you for suggesting the use of ResourceEnv.

{
destination: s3.ReplicationDestination.sameAccount(destinationBucket2),
priority: 2,
// Whether to specify S3 Replication Time Control (S3 RTC).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please spare a sentence to explain what enabling RTC means.

According to the docs it has something to do with metrics, but metrics is a separate field here, so now I don't understand what this does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated like this.

      // Whether to specify S3 Replication Time Control (S3 RTC).
      // S3 RTC replicates most objects that you upload to Amazon S3 in seconds,
      // and 99.99 percent of those objects within specified time.
      replicationTimeControl: s3.ReplicationTimeValue.FIFTEEN_MINUTES,
      // Whether to enable replication metrics about S3 RTC.
      // If set, metrics will be output to indicate whether replication by S3 RTC took longer than the configured time.
      metrics: s3.ReplicationTimeValue.FIFTEEN_MINUTES,

packages/aws-cdk-lib/aws-s3/README.md Outdated Show resolved Hide resolved
Comment on lines 1441 to 1445
* A container specifying replication metrics-related settings enabling replication metrics and events.
*
* @default - Replication metrics are not enabled
*/
readonly metrics?: ReplicationTimeValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 15 minute value is used to log events that say something like "SLA missed", right? Might want to explain that here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added below description.

When a value is set, metrics will be output to indicate whether the replication took longer than the specified time.

I'm not very confident in writing English, so please feel free to improve my sentences, not just in this context.

Comment on lines 1496 to 1498
* Specifies whether Amazon S3 replicates delete markers.
*
* @default false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the implication of this being false ? That replicated objects in versioned buckets don't get deleted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

False means do not replicate delete marker to the destination bucket.
https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-marker-replication.html

I've updated JSDoc.

  /**
   * Specifies whether Amazon S3 replicates delete markers.
   *
   * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-marker-replication.html
   *
   * @default - delete markers in source bucket is not replicated to destination bucket
   */
  readonly deleteMarkerReplication?: boolean;

* @param accessControlTransition whether to want to change replica ownership to the AWS account that owns the destination bucket. The replicas are owned by same AWS account that owns the source object by default.
*/
public static crossAccount(bucket: IBucket, account: string, accessControlTransition?: boolean): ReplicationDestination {
return new ReplicationDestination(bucket, account, accessControlTransition);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd also prefer the bucket policy be added automatically. It is the CDK thing to do.

We can add the annotation if addToBucketPolicy(): boolean returns false, indicating that we have a referenced bucket.

@aws-cdk-automation aws-cdk-automation removed the pr/needs-maintainer-review This PR needs a review from a Core Team Member label Sep 24, 2024
@mergify mergify bot dismissed rix0rrr’s stale review September 27, 2024 13:10

Pull request has been modified.

@badmintoncryer
Copy link
Contributor Author

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

@aws-cdk-automation aws-cdk-automation added the pr/needs-maintainer-review This PR needs a review from a Core Team Member label Sep 28, 2024
@shikha372
Copy link
Contributor

shikha372 commented Oct 16, 2024

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.

@badmintoncryer
Copy link
Contributor Author

badmintoncryer commented Oct 16, 2024

@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?

@shikha372
Copy link
Contributor

shikha372 commented Oct 24, 2024

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

@badmintoncryer
Copy link
Contributor Author

badmintoncryer commented Oct 27, 2024

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?

@shikha372
Copy link
Contributor

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.

@badmintoncryer
Copy link
Contributor Author

Thank you very much @shikha372

Copy link

codecov bot commented Nov 27, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 77.46%. Comparing base (be000a2) to head (5a25de6).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #30966   +/-   ##
=======================================
  Coverage   77.46%   77.46%           
=======================================
  Files         105      105           
  Lines        7168     7168           
  Branches     1314     1314           
=======================================
  Hits         5553     5553           
  Misses       1433     1433           
  Partials      182      182           
Flag Coverage Δ
suite.unit 77.46% <ø> (ø)

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

Components Coverage Δ
packages/aws-cdk 77.46% <ø> (ø)

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: 5a25de6
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
distinguished-contributor [Pilot] contributed 50+ PRs to the CDK effort/small Small work item – less than a day of effort feature-request A feature should be added or improved. needs-security-review Related to feature or issues that needs security review p1 pr/needs-maintainer-review This PR needs a review from a Core Team Member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

s3: bucket replication
6 participants