Lambda生成S3预签名URL时始终返回Access Denied求助排查
问题:Lambda生成S3预签名URL返回Access Denied
问题背景
通过Lambda生成S3预签名URL,访问时始终返回“Access Denied”。Lambda能正常列出桶内对象、上传文件,但生成的预签名URL无法访问,仅当对象设为公开时才能正常打开。
签名代码(Go)
sess, err := session.NewSession(&aws.Config{Region: aws.String("us-east-2")}) if err != nil { log.Fatal(err) } svc := s3.New(sess) key := strconv.FormatInt(accountID, 10) + "/" params := &s3.ListObjectsV2Input{ Bucket: aws.String(os.Getenv("S3_USER_BUCKET")), Prefix: aws.String(key + loc[0] + "/"), Delimiter: aws.String("/"), } resp, err := svc.ListObjectsV2(params) if err != nil { return nil, err } if *resp.KeyCount == 0 { return nil, nil } for _, key := range resp.Contents { if *key.Size == 0 { continue } req, _ := svc.GetObjectRequest(&s3.GetObjectInput{ Bucket: aws.String(os.Getenv("S3_USER_BUCKET")), Key: aws.String(*key.Key), }) urlStr, err := req.Presign(15 * time.Minute) }
Lambda执行角色权限
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:*", "s3-object-lambda:*" ], "Resource": "*" } ] }
Lambda信任策略
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
S3桶策略
{ "Version": "2012-10-17", "Id": "S3BukcetPolicyVPCAccessOnly", "Statement": [ { "Sid": "DenyIfNotFromAllowedVPC", "Effect": "Deny", "Principal": "*", "Action": [ "s3:GetObject", "s3:ListBucket", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::iscore-users", "arn:aws:s3:::iscore-users/*" ], "Condition": { "StringNotEquals": { "aws:SourceVpc": "vpc-{ID HERE}" } } } ] }
测试情况
- Lambda部署在指定VPC内,能正常列出对象,响应示例:
2023-03-10T18:49:11.275-05:00 Contents: [{ 2023-03-10T18:49:11.275-05:00 ETag: "\"TAG\"", 2023-03-10T18:49:11.275-05:00 Key: "KEY", 2023-03-10T18:49:11.275-05:00 LastModified: 2023-03-10 17:23:30 +0000 UTC, 2023-03-10T18:49:11.275-05:00 Size: 3664382, 2023-03-10T18:49:11.275-05:00 StorageClass: "STANDARD" 2023-03-10T18:49:11.275-05:00 }
- 生成的预签名URL示例(已脱敏):
https://BUCKET_NAME.s3.us-east-2.amazonaws.com/35/Scotland/Inverness/c2b8e69f-b746-4a7c-97ef-1931573c338c?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAQMATP5I3KR3QUD74%2F20230310%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20230310T234911Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEMj%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMiJGMEQCIA6BAtAHaqXuqBVug4s%2BAwU5tGPaTbYaRUEAU4Wg8mG2AiBnQwyfLnb4ryPlKX2PLGWEUkHKtQHSaJRy77XaNHelryrxAgiB%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDAyNTgxMDY5MjY2MiIMq7OanPDNAOiHaVpoKsUCorxYhoNxj1YyF7d4pcnrur1ndrWVDAaZbkF2LWyqExG1%2F57Ocdyc74GuYgd6x%2FLGLNzkXJt6NZryvrK2xy0Zx5Gg0Jxf9mmvaC5MCPrhpkgfA92U%2F1sJ5odFwyzsbK%2F2WzvY6gy1t5hT0zTcVebu%2BZ0vMCS5BqfSQJcTKMNi%2FRqZTpsb8CRjjakkIuj27ZfJirdmzykWYK9N7b4Mgm1LCBsf643V2uUsw1mUzfkzaZeTSLXYcteHN%2FTajHGt82Bh8Vzgq6%2BuAj7Yh0%2BIBJVyqK1sIdT9UgWiQt%2F09th2ep4cwEJ8ApKB0Cbl3YejevZ9QbIBluZJnKbnu%2B26RZ9N4ZvTW0hdHnWHbQJOA%2Fz43btJGLQTvbPJl2P%2BlYbCnyKXA5X6Kh56CeW8y%2FNoGmkTM%2B7GZ%2FsHXfpHUlg6ueM6iQRprMMejzDxg6%2BgBjqfAXjiT1zTkBYbE9ntg5cgZuOmVS%2B66tNTeOXMkAbd%2BU3fSssBFZQgBN0WMwfyCMCUrs7aWw6coRntFofOHdpvwK%2FKSnmydYPbuxcH7FvJH%2FV4pwEFzMfjwgkcQQyEJCAF%2Bt3JX2xEltX5D7NO3jB8hU4yGdo9ZOpfhuWT3AUJMS0LU8hYfMCc%2BhrL%2F%2BrljKJ5NVLQEpj8gX7nJViORpOyUQ%3D%3D&X-Amz-SignedHeaders=host&X-Amz-Signature=19472da6b7b3f77bff201be08b6bf9417222395f82c565d632661f78f5344d60
- 生成URL后数秒内访问,排除凭证过期问题;对象设为公开可正常访问,说明对象本身无损坏。
问题原因
你的S3桶策略设置了Deny所有非指定VPC来源的GetObject请求,而预签名URL的访问请求来自外部客户端(不在你的VPC内),因此被桶策略直接拒绝。
Lambda能正常操作是因为它部署在指定VPC内,符合桶策略的允许条件;但预签名URL是给外部用户使用的,访问时请求来源不在VPC,触发了Deny规则。
解决方案
方案1:修改桶策略,允许预签名URL的访问
在桶策略中添加一条Allow规则,允许由你的Lambda角色签名的预签名URL请求(S3桶策略中Deny优先级高于Allow,需明确排除预签名的情况)。
修改后的桶策略示例:
{ "Version": "2012-10-17", "Id": "S3BukcetPolicyVPCAccessOnly", "Statement": [ { "Sid": "AllowPresignedRequestsFromLambdaRole", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::iscore-users/*", "Condition": { "StringEquals": { "aws:PrincipalArn": "arn:aws:iam::你的账号ID:role/你的Lambda执行角色名称" } } }, { "Sid": "DenyIfNotFromAllowedVPC", "Effect": "Deny", "Principal": "*", "Action": [ "s3:GetObject", "s3:ListBucket", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::iscore-users", "arn:aws:s3:::iscore-users/*" ], "Condition": { "StringNotEquals": { "aws:SourceVpc": "vpc-{ID HERE}" }, "StringNotEquals": { "aws:PrincipalArn": "arn:aws:iam::你的账号ID:role/你的Lambda执行角色名称" } } } ] }
方案2:使用VPC端点+CloudFront(适合严格VPC控制场景)
如果必须限制所有访问都来自VPC,可以设置S3 VPC端点,然后通过CloudFront配置Origin Access Control(OAC),让预签名URL的请求通过CloudFront并经过VPC端点访问S3。此方案配置相对复杂。
方案3:移除桶策略中的Deny规则(不推荐,除非无VPC访问限制需求)
如果不需要强制所有S3访问都来自指定VPC,可以删除桶策略中的Deny规则,仅保留Lambda角色的权限即可。
内容的提问来源于stack exchange,提问作者HLT
相关产品推荐
相关产品推荐

