【问题标题】:API Gateway HTTP client request with IAM auth with GoAPI Gateway HTTP 客户端请求与 IAM 身份验证与 Go
【发布时间】:2019-10-03 09:49:37
【问题描述】:

你好 StackOverflow AWS Gophers,

I'm implementing 一个具有出色 cobra/viper packages from spf13 的 CLI。我们有一个 Athena 数据库,前面有一个 API Gateway 端点,它通过 IAM 进行身份验证。

也就是说,为了使用 Postman 与其端点交互,我必须将 AWS Signature 定义为 Authorization 方法,定义相应的 AWS id/secret 然后在 Headers 中会有 X-Amz-Security-Token 等。没有什么异常,按预期工作。

由于我是 Go 新手,看到没有使用 aws-sdk-go 本身执行此简单 HTTP GET 请求的示例,我感到有点震惊......我正在尝试使用共享凭据提供程序( ~/.aws/credentials),如 S3 客户端 Go code snippets from re:Invent 2015 所示:

req := request.New(nil)

我怎样才能在 2019 年完成这个看似简单的壮举,而不必求助于自己煮熟的 net/http 并因此不得不手动阅读 ~/.aws/credentials 或更糟的是,使用 os.Getenv 和其他丑陋的黑客?

作为客户端交互的任何 Go 代码示例都会非常有帮助。请不要使用 Golang Lambda/服务器示例,那里有很多。

【问题讨论】:

  • 你无法从this函数获取信息对吧?
  • 是的,没错,这个函数是我应该写的,最好是aws-sdk-go

标签: amazon-web-services go aws-api-gateway aws-sdk-go go-cobra


【解决方案1】:

以下解决方案使用 aws-sdk-go-v2 https://github.com/aws/aws-sdk-go-v2

// A AWS SDK session is created because the HTTP API is secured using a
// IAM authorizer. As such, we need AWS client credentials and a
// session to properly sign the request.
cfg, err := external.LoadDefaultAWSConfig(
    external.WithSharedConfigProfile(profile),
)
if err != nil {
    fmt.Println("unable to create an AWS session for the provided profile")
    return
}


req, _ := http.NewRequest(http.MethodGet, "", nil)
req = req.WithContext(ctx)
signer := v4.NewSigner(cfg.Credentials)
_, err = signer.Sign(req, nil, "execute-api", cfg.Region, time.Now())
if err != nil {
    fmt.Printf("failed to sign request: (%v)\n", err)
    return
}

res, err := http.DefaultClient.Do(req)
if err != nil {
    fmt.Printf("failed to call remote service: (%v)\n", err)
    return
}

defer res.Body.Close()
if res.StatusCode != 200 {
    fmt.Printf("service returned a status not 200: (%d)\n", res.StatusCode)
    return
}

【讨论】:

  • 感谢@craftsource,我看到这个 sn-p 使用的是 aws-sdk-go-v2 而不是 v1...我去找到合适的,但粘贴时我得到 cannot use cfg.Credentials (type "github.com/aws/aws-sdk-go-v2/aws".CredentialsProvider) as type *credentials.Credentials in argument to v4.NewSigner不幸的是:/
  • 您需要使用 aws-sdk-go-v2 v4 签名器。
  • 非常感谢!我没有导入正确的 sdk,import v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4",我们走吧 :)
【解决方案2】:

不幸的是,自从编写了接受的答案以来,似乎库已经更新,并且解决方案不再相同。经过一些试验和错误,这似乎是处理签名的最新方法(使用https://pkg.go.dev/github.com/aws/aws-sdk-go-v2):

import (
    "context"
    "net/http"
    "time"

    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
)

func main() {
    // Context is not being used in this example.
    cfg, err := config.LoadDefaultConfig(context.TODO())

    if err != nil {
        // Handle error.
    }

    credentials, err := cfg.Credentials.Retrieve(context.TODO())

    if err != nil {
        // Handle error.
    }

    // The signer requires a payload hash. This hash is for an empty payload.
    hash := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
    req, _ := http.NewRequest(http.MethodGet, "api-gw-url", nil)
    signer := v4.NewSigner()
    err = signer.SignHTTP(context.TODO(), credentials, req, hash, "execute-api", cfg.Region, time.Now())

    if err != nil {
        // Handle error.
    }

    // Use `req`
}

【讨论】:

    【解决方案3】:

    request.New 的第一个参数是 aws.Config,您可以在其中发送凭据。

    https://github.com/aws/aws-sdk-go/blob/master/aws/request/request.go#L99 https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config

    创建凭证对象有多种方式:https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html

    例如使用静态值:

    creds:= credentials.NewStaticCredentials("AKID", "SECRET_KEY", "TOKEN")
    req := request.New(aws.Config{Credentials: creds}, ...)
    

    【讨论】:

    • 正确,在代码中定义静态凭据是一个坏主意,因此我按照文档中所说的:“当您在不提供任何凭据参数的情况下初始化新服务客户端时,SDK 使用默认凭据提供程序链来查找 AWS 凭证。” ...将nil 传递给新方法,对吗?不幸的是,这不起作用。
    • 我想 2019 年的新方法是:req := request.New(defaults.Get().Config, metadata.ClientInfo, defaults.Get().Handlers, client.DefaultRetryer()),但仍然没有具体的简单示例,不幸的是:/
    【解决方案4】:

    我自己很新(第 3 天学习),但是通过观看您发布的 S3 示例视频并阅读源代码(用于 s3 服务和请求模块),这是我的理解(我希望有帮助)。

    如果您查看 s3.New() 函数的代码 aws-sdk-go/service/s3/service.go

    func New(p client.ConfigProvider, cfgs ...*aws.Config) *S3 {
    c := p.ClientConfig(EndpointsID, cfgs...)
    return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, .SigningName) }
    

    相对于 request.New() 函数aws-sdk-go/aws/request/request.go

    func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
    retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request { ...
    

    正如您在 s3 场景中看到的,*aws.Config 结构是一个指针,因此可能在其他地方初始化/填充。与 aws.Config 是参数的请求函数相反。所以我猜测请求模块可能是一个非常低级的模块,它不会自动获取共享凭据。

    现在,看到您将与 API 网关交互,我专门查看了该服务,看看是否有类似的东西。我看了 aws-sdk-go/service/apigateway/service.go

    func New(p client.ConfigProvider, cfgs ...*aws.Config) *APIGateway {
    c := p.ClientConfig(EndpointsID, cfgs...)
    return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName) }...
    

    它看起来与 s3 客户端几乎相同,所以也许可以尝试使用它,看看效果如何?

    【讨论】:

    • 您好 Grizzle,感谢您的演练,但 Craftsource 的回答在几天前被接受,这意味着我设法使其与 aws-sdk-go-v2 SDK(较新版本的 AWS Go SDK)一起工作。你可以在这里查看我的实际工作代码:github.com/umccr/cli/blob/master/cmd/find.go#L46
    猜你喜欢
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    • 2016-01-09
    • 1970-01-01
    • 2020-07-11
    • 1970-01-01
    • 1970-01-01
    • 2011-03-01
    相关资源
    最近更新 更多