如何使用AWS Lambda将S3对象备份到另一个帐户的存储桶中?

如何使用AWS Lambda将S3对象备份到另一个帐户的存储桶中?

问题描述:

我正在尝试使用AWS Lambda将对象从Account-A下的assets.myapp.com S3存储桶备份到Account-B下的backup-assets.myapp.com S3存储桶,但是无论Bucket Policy我使用的配置.

I'm attempting to use AWS Lambda to backup objects from an assets.myapp.com S3 bucket under Account-A to a backup-assets.myapp.com S3 bucket under Account-B, but I'm getting Access Denied no matter the IAM or Bucket Policy configuration I use.

我有一个名为Backup-S3-Object的Lambda函数,并由assets.myapp.com存储桶上的s3:PutObject事件触发器触发.

I've got a Lambda function called Backup-S3-Object and it's fired by an s3:PutObject event trigger on the assets.myapp.com bucket.

期望的结果是Backup-S3-Object函数将使用适用于Javascript的AWS-SDK执行s3.copyObjectbackup-assets.myapp.com.

The desired result is that the Backup-S3-Object function will the perform an s3.copyObject to backup-assets.myapp.com using the AWS-SDK for Javascript.

Lambda代码

var aws = require('aws-sdk');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });

exports.backupObject = function(event, context, callback) {

    var data = event.Records[0];
    var sourceBucket = data.s3.bucket.name;
    var targetBucket = 'backup-' + data.s3.bucket.name;
    var key = data.s3.object.key;

    console.log('BACKUP: ' + sourceBucket + '/' + key + ' to ' + targetBucket);

    s3.copyObject({
        Bucket : targetBucket,
        CopySource : sourceBucket + '/' + key,
        Key : key,
        ACL : 'private',
        ServerSideEncryption : 'AES256'
    }, function(error, data) {

        if (error) return context.done(error);
        return context.done(null, 'Successful backup of ' + sourceBucket + '/' + key);

    });

};

Lambda角色S3政策

对于Backup-S3-Lambda-Role,我具有以下策略,该策略已分配给我的Lambda函数.这允许Lambda函数从源帐户Account-A下的任何存储桶访问ListGet的任何对象.因此,此策略将允许Lambda函数从assets.myapp.com(即源存储桶)中获取对象.

I've got the following policy for the Backup-S3-Lambda-Role which is assigned to my Lambda function. This allows the Lambda function access to List or Get any object, from any bucket, under Account-A, which is the source account. So this policy will allow the Lambda function to get the object from assets.myapp.com, which is the source bucket.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::*"
            ]
        }
    ]
}

backup-assets.myapp.com存储桶策略

The backup-assets.myapp.com Bucket Policy

然后,在Account-B下,我在backup-assets.myapp.com上附加了以下存储桶策略,该策略旨在允许在Account-A下运行的Lambda函数写入以下位置的backup-assets.myapp.com存储桶Account-B

And then, under Account-B, I've got the following bucket policy attached to the backup-assets.myapp.com, which is intended to allow the Lambda function, running under Account-A, to write to the backup-assets.myapp.com bucket under Account-B

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "Allow PUT from Account-A Lambda function",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::ACCOUNT-A-NUMBER-HERE:role/Backup-S3-Lambda-Role"
            },
            "Action": "s3:PutObject",
            "Resource": [
                "arn:aws:s3:::backup-assets.myapp.com",
                "arn:aws:s3:::backup-assets.myapp.com/*"
            ]
        }
    ]
}

发生了什么事?

assets.myapp.com存储桶上的触发器会很好地触发.它会触发Backup-S3-Object Lambda函数,并且该函数逻辑能够确定正确的存储桶名称和要复制的对象键,但始终会失败,并显示Access Denied.

The trigger on the assets.myapp.com bucket fires just fine. It triggers the Backup-S3-Object Lambda function, and the function logic is able to determine the correct bucket names and object key to be copied, but it always fails with Access Denied.

我已经使用相同的设置进行了测试,并将其转移到也在Account-A下的另一个存储桶中,并且工作正常.因此,问题显然出在跨帐户权限上,但是我尝试过的任何方法都无法奏效.

I've done a test using the same setup and transferring to another bucket that's also under Account-A and it works just fine. So the problem is obviously with the cross-account permissions, but nothing I've tried works.

我也曾尝试将Account-ACanonicalID用作backup-assets.myapp.com存储桶策略中的Principal,但仍然会得到Access Denied作为结果错误.

I've also attempted using the CanonicalID of Account-A as the Principal in the backup-assets.myapp.com bucket policy, but still get Access Denied as the resulting error.

我尝试使用"AWS": "arn:aws:iam::ACCOUNT-A-NUMBER-HERE:root"作为主体,同样的错误.

And I've attempted using "AWS": "arn:aws:iam::ACCOUNT-A-NUMBER-HERE:root" as the principal, same error.

有帮助吗?

解决方案:如下面的Matt所述,该解决方案是您需要在Lambda角色策略中包含s3:PutObject权限 . 这对我来说有点奇怪,但是总体思路是 您在自己的存储桶策略中授予Lambda角色的权限 您还必须在Lambda策略中将目标存储分区复制到您的 源帐户.似乎完全采用了Lambda角色 当您在存储桶策略"中引用它时.为此,我添加了 我的Lambda角色的第二个内联策略,允许s3:PutObject Account-B下的backup-assets.myapp.com存储桶.这不允许 Lambda函数可写入任何源存储桶,但 允许它写入备份存储桶.

A Solution: As outlined by Matt below, the solution is that you need to include s3:PutObject permission in the Lambda role policy. This is a little weird to me, but the general idea is that any permissions you grant the Lambda role in the Bucket Policy of your target bucket must also be replicated in the Lambda policy under your source account. It seems that the Lambda role is adopted entirely when you reference it in the Bucket Policy. To achieve this I've added a second inline policy to my Lambda role that allows s3:PutObject to the backup-assets.myapp.com bucket under Account-B. This disallows the Lambda function to write to any of the source buckets, but does allow it to write to the backup bucket.

我看到两个可能的问题.

I see two possible problems.

  1. 您用于Lambda函数的IAM策略缺少写入到备份存储桶的权限.
  1. Your IAM policy for your Lambda function is missing permissions to write to your backup bucket.

尝试将s3:PutObject添加到您的策略中,如下所示:

Try adding s3:PutObject into your policy like this:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:ListBucketVersions",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::*"
            ]
        }
    ]
}

或者,如果您想继续将Lambda函数限制为仅对源存储分区进行读取访问,则可以在Lambda角色中添加第二个策略,以使其s3:PutObject在您的备份存储桶中,将您的备份存储桶命名为资源,如下所示:

Or, if you'd like to continue to restrict your Lambda function to read access only on your source bucket(s), you can add a second policy to your Lambda role allowing it to s3:PutObject on your backup bucket, naming your backup bucket as the resource, like so:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::backup-assets.myapp.com",
                "arn:aws:s3:::backup-assets.myapp.com/*"
            ]
        }
    ]
}

  1. 有时,在您的IAM策略中使用IAM角色作为委托人并没有那么简单.我并不是说这是问题所在,但让我们放宽限制,直到您可以使用副本为止.然后,您可以收紧政策.

现在,只需让您的目标帐户访问源存储桶即可:

For now, just let your target account access to the source bucket:

{
    "Version": "2008-10-17",
    "Id": "Backup-Assets",
    "Statement": [
        {
            "Sid": "Allow PUT from Account-A Lambda function",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::ACCOUNT-A-NUMBER-HERE:root"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::backup-assets.myapp.com",
                "arn:aws:s3:::backup-assets.myapp.com/*"
            ]
        }
    ]
}

复制成功后,您可以尝试重新添加角色.

Once copying is working, you can try adding the role back.