Have you ever wondered how to replicate a S3 object encrypted with KMS from one S3 bucket to a different one in a different account and region without losing encryption at REST? Since this feature is not yet supported by Amazon AWS, I proposed a solution how to accomplish this task.
This tutorial is based on the blog post published on AWS Documentation.
This solution requires you to have access to two different AWS accounts. One S3 source bucket and one destination bucket in different accounts (however, you can do this in the same account if you want). The buckets can reside in the same region or in different regions. You need to create SNS topic which allows you to publish notifications from source bucket. SNS Topic will initiate Lambda function to copy the object from source bucket to destination bucket. Source code for the function is part of this post as well as all policies needed for this solution to work properly.
A Lambda function requires the IAM role assigned to it in order to copy an object to destination bucket with the KMS encryption.
The Lambda function requires the following permissions:
For the source account set Trust relationship with the destination account for the source S3 objects KMS Key.
In this post, I will provide all source code for the IAM Policies.
Important NOTE: Create an SNS Topic in destination account
{
"Version": "2008-10-17",
"Id": "",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"SNS:Publish"
],
"Resource": "arn:aws:sns:us-east-1:123123123123:s3-source-bucket-fanout",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:s3:*:*:s3-source-bucket-name"
}
}
}
]
arn:aws:sns:us-east-1:123123123123:s3-source-bucket-fanout
arn:aws:s3:*:*:s3-source-bucket-name
Important NOTE: Make sure that the source bucket has access to publish SNS Notification to the SNS Topic.
Important NOTE: Create a Lambda function in destination bucket.
import urllib
import boto3
import ast
import json
from botocore.client import Config
print('Loading function')
def lambda_handler(event, context):
s3 = boto3.client('s3',config=Config(signature_version='s3v4'))
sns_message = ast.literal_eval(event['Records'][0]['Sns']['Message'])
target_bucket = context.function_name
source_bucket = str(sns_message['Records'][0]['s3']['bucket']['name'])
key = str(urllib.unquote_plus(sns_message['Records'][0]['s3']['object']
['key']).decode('utf8'))
copy_source = {'Bucket':source_bucket, 'Key':key}
print "Copying %s from bucket %s to bucket %s ..." % (key, source_bucket,
target_bucket)
s3.copy_object(Bucket=target_bucket, Key=key, CopySource=copy_source,
StorageClass='STANDARD', ServerSideEncryption='aws:kms', SSEKMSKeyId='arn:aws:kms:eu-central-1:123123123123:key/992da83f-f7f1-428a-a70d-041652ib43bs')
Change the text in the code that represents a KMS Key generated in IAM KMS. Make sure that the KMS Key is created in the region where the destination bucket is located. If you want to use standard aws/s3 key from KMS just remove the SSEKMSKeyId part from the source code and set ServerSideEncryption value to “AES256”.
arn:aws:kms:eu-central-1:123123123123:key/992da83f-f7f1-428a-a70d-041652ib43bs
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::source-bucket-name/*"
]
},
{
"Effect": "Allow",
"Action": [
"sns:Receive"
],
"Resource": [
"arn:aws:sns:eu-west-1:321321321321:source-bucket-name-fanout"
]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::destination-bucket-name/*"
]
},
{
"Effect": "Allow",
"Action": [
"kms:Decrypt"
],
"Resource": [
"arn:aws:kms:eu-west-1:123123123:key/38e744bf-66a5-4762-6d85-gd4a78d7349c"
]
},
{
"Effect": "Allow",
"Action": [
"kms:Encrypt"
],
"Resource": [
"arn:aws:kms:eu-central-1:321321321321:key/99a3c83f-d731-438i-a70b-041542ad19cd"
]
}
]
arn:aws:s3:::source-bucket-name/*
arn:aws:sns:eu-west-1:321321321321:source-bucket-name-fanout
arn:aws:s3:::destination-bucket-name/*
arn:aws:kms:eu-west-1:123123123:key/38e744bf-66a5-4762-6d85-gd4a78d7349c
arn:aws:kms:eu-central-1:321321321321:key/99a3c83f-d731-438i-a70b-041542ad19cd
Important NOTE: Make sure that your source bucket KMS Key have the destination bucket account set in Key users for External Accounts.
For a successful execution, this should look similar to the following screenshot:
Do not forget to set Lambda role access for the destination bucket KMS Key in IAM KMS part.
"Statement": [
{
"Sid": "Stmt1493285959480",
"Effect": "Allow",
"Principal": {
} ]
"AWS": "arn:aws:iam::321321321321:role/[nameOfTheLambdaRole]"
},
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::source-bucket-name",
"arn:aws:s3:::source-bucket-name/*"
]
IAM Policy for source bucket:
"Statement": [
{
"Sid": "Stmt1493285959480",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::321321321:role/[nameOfTheLambdaRole]"
},
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::destination-bucket-name",
"arn:aws:s3:::destination-bucket-name/*"
]
}
]
Do you see yourself working with us? Check out our vacancies. Is your ideal vacancy not in the list? Please send an open application. We are interested in new talents, both young and experienced.
Join us