Golang Lambda读取S3文件遇凭证问题,如何用IAM角色创建Session?
AWS Lambda + Go 读取S3文件的凭证问题解决
我编写了一段Golang代码,用于在AWS Lambda中接收文件名后读取S3文件并提取指定列,但在获取S3文件的步骤出现问题。
我的代码如下:
package main import ( "fmt" "packageXXX/cmd/utils" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" "go.uber.org/zap" ) var ( log *zap.Logger ) func init() { log = utils.InitLogger() } func DownloadFile(downloader *s3manager.Downloader, bucketName string, key string) error { buff := &aws.WriteAtBuffer{} numBytes, err := downloader.Download( buff, &s3.GetObjectInput{ Bucket: aws.String(bucketName), Key: aws.String(key), }, ) data := buff.Bytes() // now data is my []byte array log.Info("nnnnnnnnnnnnnnn", zap.Any("nnn", numBytes)) log.Info("xxxxxxxxxxxxxxx", zap.Any("bytes", data)) return err } func handler(fileName string) (string, error) { log.Info(fmt.Sprintf("received: %s", fileName)) sess, err := session.NewSessionWithOptions(session.Options{ Profile: "default", Config: aws.Config{ Region: aws.String("us-west-2"), }, }) if err != nil { log.Info("errorrrrrrrrr", zap.Any("session", err)) return "errorr,", nil } else { log.Info("sessionnnnnnnnnnnnnn", zap.Any("sess", sess)) } bucketName := "s3-test-lambda-go-xxx" downloader := s3manager.NewDownloader(sess) log.Info("downloader", zap.Any("downloader", err)) key := fileName err = DownloadFile(downloader, bucketName, key) if err != nil { fmt.Printf("Couldn't download file: %v", err) return "errror", nil } fmt.Println("Successfully downloaded file") return fmt.Sprintf("el file recibido es: %s", fileName), nil } func main() { lambda.Start(handler) }
日志输出如下:
For verbose messaging see aws.Config.CredentialsChainVerboseErrorsSTART RequestId: dba8ebd5-9102-4a2f-8e45-cdef355f1bbd Version: $LATEST {"level":"info","ts":"2022-11-02T17:31:32Z","msg":"received: gggggg2.txt"} {"level":"info","ts":"2022-11-02T17:31:32Z","msg":"sessionnnnnnnnnnnnnn","sessError":"json: unsupported type: endpoints.endpointDefaults"} {"level":"info","ts":"2022-11-02T17:31:32Z","msg":"downloader","downloader":null} {"level":"info","ts":"2022-11-02T17:31:38Z","msg":"nnnnnnnnnnnnnnn","nnn":0} {"level":"info","ts":"2022-11-02T17:31:38Z","msg":"xxxxxxxxxxxxxxx","bytes":""} Couldn't download file: NoCredentialProviders: no valid providers in chain. Deprecated. END RequestId: dba8ebd5-9102-4a2f-8e45-cdef355f1bbd
问题出在Session创建环节,日志显示错误为“NoCredentialProviders: no valid providers in chain”。由于是生产环境,我不想使用Access Key和Secret Key,请问是否可以通过IAM角色创建Session?
解决方案
完全可以通过IAM角色解决这个问题,这也是AWS生产环境的最佳实践,具体操作如下:
1. 移除代码中的本地Profile配置
Lambda运行环境中不存在本地的default Profile配置,这会导致SDK尝试读取不存在的凭证文件,直接删除Profile: "default"这一行。
2. 为Lambda函数关联IAM角色
创建一个IAM角色,为其添加允许读取目标S3桶的权限策略,示例策略如下:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::s3-test-lambda-go-xxx/*" } ] }
将该角色关联到你的Lambda函数上,Lambda运行时会自动获取该角色的临时凭证,无需手动配置。
3. 简化Session创建代码
修改后的Session创建代码无需指定Profile,SDK会自动从Lambda运行环境中获取凭证和区域信息(也可以保留区域配置确保一致性):
func handler(fileName string) (string, error) { log.Info(fmt.Sprintf("received: %s", fileName)) // 简化Session创建,移除Profile配置 sess, err := session.NewSession(&aws.Config{ Region: aws.String("us-west-2"), }) if err != nil { log.Error("Failed to create session", zap.Error(err)) return "error creating session", err } bucketName := "s3-test-lambda-go-xxx" downloader := s3manager.NewDownloader(sess) key := fileName err = DownloadFile(downloader, bucketName, key) if err != nil { log.Error("Failed to download file", zap.Error(err)) return "error downloading file", err } log.Info("Successfully downloaded file") return fmt.Sprintf("Received file: %s", fileName), nil }
原理说明
AWS Lambda运行时会自动为关联的IAM角色生成临时凭证,并通过环境变量和元数据服务暴露给函数进程。AWS Go SDK会自动按照凭证链顺序查找这些凭证,无需手动指定Access Key和Secret Key,完全符合生产环境的安全要求。
内容的提问来源于stack exchange,提问作者user2291499
相关产品推荐
相关产品推荐

