尝试将对象放入 s3 时访问被拒绝
Access Denied when trying to PutObject to s3
我正在使用无服务器框架创建一个将 CSV 保存到 S3 存储桶的 lambda。
我已经有一个类似的 lambda 可以用另一个桶来做这个。
这就是它变得奇怪的地方:我可以将 CSV 上传到我创建的第一个 S3 存储桶(很多个月前),但是在将相同的 CSV 上传到新的 S3 存储桶时我遇到了 AccessDenied 错误,据我所知,通过 serverless.yml 配置以与第一个完全相同的方式创建。
错误是:
Error: AccessDenied: Access Denied
这些是 serverless.yml 中的相关位:
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: eu-west-1
environment:
BUCKET_NEW: ${self:custom.bucketNew}
BUCKET: ${self:custom.bucket}
iam:
role:
statements:
- Effect: 'Allow'
Action: 'lambda:InvokeFunction'
Resource: '*'
- Effect: 'Allow'
Action:
- 's3:GetObject'
- 's3:PutObject'
Resource:
- 'arn:aws:s3:::*' # Added this whilst debugging
- 'arn:aws:s3:::*/*' # Added this whilst debugging
- 'arn:aws:s3:::${self:custom.bucket}'
- 'arn:aws:s3:::${self:custom.bucket}/*'
- 'arn:aws:s3:::${self:custom.bucketNew}'
- 'arn:aws:s3:::${self:custom.bucketNew}/*'
functions:
uploadReport:
handler: services/uploadReport.handler
vpc:
securityGroupIds:
- 000001
subnetIds:
- subnet-00000A
- subnet-00000B
- subnet-00000C
resources:
Resources:
Bucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: ${self:custom.bucket}
BucketNew:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: ${self:custom.bucketNew}
custom:
stage: ${opt:stage, 'dev'}
bucket: ${self:service}-${self:custom.stage}-report
bucketNew: ${self:service}-${self:custom.stage}-report-new
Lambda 代码(简化):
const fs = require('fs')
const AWS = require('aws-sdk')
const S3 = new AWS.S3({
httpOptions: {
connectTimeout: 1000,
},
})
const uploadToS3 = (params) => new Promise((resolve, reject) => {
S3.putObject(params, err => (err ? reject(err) : resolve(params.Key)))
})
module.exports.handler = async () => {
const fileName = `report-new.csv`
const filePath = `/tmp/${fileName}`
// Some code that creates a CSV file at filePath.
const bucketParams = {
Bucket: process.env.BUCKET_NEW, // Works for process.env.BUCKET, but not process.env.BUCKET_NEW.
Key: fileName,
Body: fs.readFileSync(filePath).toString('utf-8'),
}
try {
const s3Upload = await uploadToS3(bucketParams)
} catch (e) {
throw new Error(e) // Throws Error: AccessDenied: Access Denied.
}
}
我认为您可能缺少一些我经常在我的无服务器应用程序上使用“s3:Put*”的权限,这可能是不可取的,因为它太广泛了。
这是上传我在此处找到的对象所需的最低权限列表
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObjectAcl",
"s3:ListBucket",
"s3:GetBucketLocation"
找到了解决方案,但这是我自己的错误。我的 lambda 实际上在 VPC 中。我原来的问题(编辑前)没有显示这个。
VPC 中的 Lambda 无法与 S3 存储桶通信,除非 VPC 具有端点网关,使其能够与任何特定引用的存储桶通信。
我之前创建了一个端点网关,让它与我前一段时间创建的初始存储桶通信,但忘记更新端点网关以让它与新存储桶通信。
除非其他人花了一整天的时间试图解决一些愚蠢的问题,否则就把这个答案留在这里。
我正在使用无服务器框架创建一个将 CSV 保存到 S3 存储桶的 lambda。
我已经有一个类似的 lambda 可以用另一个桶来做这个。
这就是它变得奇怪的地方:我可以将 CSV 上传到我创建的第一个 S3 存储桶(很多个月前),但是在将相同的 CSV 上传到新的 S3 存储桶时我遇到了 AccessDenied 错误,据我所知,通过 serverless.yml 配置以与第一个完全相同的方式创建。
错误是:
Error: AccessDenied: Access Denied
这些是 serverless.yml 中的相关位:
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage, 'dev'}
region: eu-west-1
environment:
BUCKET_NEW: ${self:custom.bucketNew}
BUCKET: ${self:custom.bucket}
iam:
role:
statements:
- Effect: 'Allow'
Action: 'lambda:InvokeFunction'
Resource: '*'
- Effect: 'Allow'
Action:
- 's3:GetObject'
- 's3:PutObject'
Resource:
- 'arn:aws:s3:::*' # Added this whilst debugging
- 'arn:aws:s3:::*/*' # Added this whilst debugging
- 'arn:aws:s3:::${self:custom.bucket}'
- 'arn:aws:s3:::${self:custom.bucket}/*'
- 'arn:aws:s3:::${self:custom.bucketNew}'
- 'arn:aws:s3:::${self:custom.bucketNew}/*'
functions:
uploadReport:
handler: services/uploadReport.handler
vpc:
securityGroupIds:
- 000001
subnetIds:
- subnet-00000A
- subnet-00000B
- subnet-00000C
resources:
Resources:
Bucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: ${self:custom.bucket}
BucketNew:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: ${self:custom.bucketNew}
custom:
stage: ${opt:stage, 'dev'}
bucket: ${self:service}-${self:custom.stage}-report
bucketNew: ${self:service}-${self:custom.stage}-report-new
Lambda 代码(简化):
const fs = require('fs')
const AWS = require('aws-sdk')
const S3 = new AWS.S3({
httpOptions: {
connectTimeout: 1000,
},
})
const uploadToS3 = (params) => new Promise((resolve, reject) => {
S3.putObject(params, err => (err ? reject(err) : resolve(params.Key)))
})
module.exports.handler = async () => {
const fileName = `report-new.csv`
const filePath = `/tmp/${fileName}`
// Some code that creates a CSV file at filePath.
const bucketParams = {
Bucket: process.env.BUCKET_NEW, // Works for process.env.BUCKET, but not process.env.BUCKET_NEW.
Key: fileName,
Body: fs.readFileSync(filePath).toString('utf-8'),
}
try {
const s3Upload = await uploadToS3(bucketParams)
} catch (e) {
throw new Error(e) // Throws Error: AccessDenied: Access Denied.
}
}
我认为您可能缺少一些我经常在我的无服务器应用程序上使用“s3:Put*”的权限,这可能是不可取的,因为它太广泛了。
这是上传我在此处找到的对象所需的最低权限列表
"s3:PutObject",
"s3:PutObjectAcl",
"s3:GetObjectAcl",
"s3:ListBucket",
"s3:GetBucketLocation"
找到了解决方案,但这是我自己的错误。我的 lambda 实际上在 VPC 中。我原来的问题(编辑前)没有显示这个。
VPC 中的 Lambda 无法与 S3 存储桶通信,除非 VPC 具有端点网关,使其能够与任何特定引用的存储桶通信。
我之前创建了一个端点网关,让它与我前一段时间创建的初始存储桶通信,但忘记更新端点网关以让它与新存储桶通信。
除非其他人花了一整天的时间试图解决一些愚蠢的问题,否则就把这个答案留在这里。