S3 Replication with KMS

After much research, I have realized that there isn’t really a concise, straight to the point guide on designing replication in S3, using SSE-KMS, and cross-account ownership. So, a quick guide on what has worked for me:

In your ORIGIN ACCOUNT

  • Create origin Bucket
  • Create origin IAM role
  • Create origin IAM Policy
  • Create origin KMS key
  • Set up origin bucket replication

In your TARGET ACCOUNT

  • Create target bucket
  • Create target bucket policy
  • Create target KMS Key
  • Create target KMS policy
  • Create Target IAM Role

Things to make

ORIGIN.IAM.ROLE (Assume Role Policy)

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

ORIGIN.IAM.POLICY

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:ListBucket",
        "s3:GetReplicationConfiguration",
        "s3:GetObjectVersionForReplication",
        "s3:GetObjectVersionAcl",
        "s3:GetObjectVersionTagging",
        "s3:GetObjectRetention",
        "s3:GetObjectLegalHold",
        "s3:GetObjectVersion",
        "s3:ObjectOwnerOverrideToBucketOwner"
      ],
      "Effect": "Allow",
      "Resource": [
        "ORIGIN.BUCKET.ARN",
        "ORIGIN.BUCKET.ARN/*"
      ]
    },
    {
      "Action": [
        "s3:ReplicateObject",
        "s3:ReplicateDelete",
        "s3:ReplicateTags",
        "s3:GetObjectVersionTagging",
        "s3:ObjectOwnerOverrideToBucketOwner"
      ],
      "Effect": "Allow",
      "Resource": "TARGET.BUCKET.NAME/*"
    },
    {
      "Action": [
        "kms:Decrypt"
      ],
      "Effect": "Allow",
      "Resource": "ORIGIN.KMS.KEY.ARN"
    },
    {
      "Action": [
        "kms:Encrypt"
      ],
      "Effect": "Allow",
      "Resource": "TARGET.KMS.KEY.ARN"
    }
  ]
}

ORIGIN.BUCKET

Set up Replication through the GUI, Cloudformation, or another functionality. I use Terraform for a lot of stuff so here is a Terraform version of the S3 bucket I would deploy for my origin:

resource "aws_s3_bucket" "INSERTUNIQUENAME" {
  bucket = "INSERTBUCKETNAME" 
  acl    = "private"

lifecycle_rule {
    id      = "SaveMoney"
    enabled = true

    expiration {
      days = 30
  }
}

versioning {
  enabled = true
}
  
server_side_encryption_configuration {
  rule {
    apply_server_side_encryption_by_default {
      kms_master_key_id = "ORIGIN.KMS.KEY.ID"
      sse_algorithm     = "aws:kms"
      }
    }
  }

replication_configuration {
  role = "ORIGIN.IAM.ROLE.ARN"

  rules {
    id     = "replicate_to_other_bucket"
    status = "Enabled"

    source_selection_criteria {
      sse_kms_encrypted_objects {
        enabled = "true"
      }
    }

    destination {
      bucket        = TARGET.BUCKET.NAME
      storage_class = "STANDARD"
      replica_kms_key_id = "TARGET.KMS.ARN"
      access_control_translation {
        owner = "Destination"
      }
      account_id = "TARGET.ACCOUNT.ID"
      }
    }
  }

tags = {
  Name = "Value"
    }

}

TARGET.KMS

statement {
      sid = "AllowExternalAccountUse"
      effect = "Allow"
      principals {
        type = "AWS"
        identifiers =  [
          "ORIGIN.ROLE.ARN"
          ]
      }
      actions = [
          "kms:Encrypt",
          "kms:ReEncrypt*",
          "kms:GenerateDataKey*",
          "kms:DescribeKey"
      ]
      resources = ["*"]
    }
    statement {
      sid = "AllowAdmin"
      effect = "Allow"
      principals {
          type = "AWS"
          identifiers = ["arn:aws:iam::TARGET.ACCOUNTID:root"]
      }
      actions = ["kms:*"]
      resources = ["*"]
     }

TARGET.BUCKETPOLICY

{
  "Version": "2012-10-17",
  "Statement": [
      "Sid": "S3ReplicationPolicy",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::ORIGIN.ACCOUNTID:root"
        ]
      },
      "Action": [
        "s3:GetBucketVersioning",
        "s3:PutBucketVersioning",
        "s3:ReplicateObject",
        "s3:ObjectOwnerOverrideToBucketOwner"
      ],
      "Resource": [
          "TARGET.BUCKET.ARN",
          "TARGET.BUCKET.ARN/*"
        ]
    }
  ]
}