API 使用 Go 进行 IAM 身份验证的网关 HTTP 客户端请求
API Gateway HTTP client request with IAM auth with Go
你好 Whosebug AWS Gophers,
I'm implementing a CLI with the excellent cobra/viper packages from spf13。我们有一个 Athena 数据库,前面是一个 API 网关端点,它使用 IAM 进行身份验证。
也就是说,为了使用 Postman 与其端点进行交互,我必须将 AWS Signature
定义为授权方法,定义相应的 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/server 示例,拜托,那里有很多。
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}, ...)
下面的解决方案使用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
}
我自己还很新(第 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 客户端几乎一样,所以也许尝试使用它看看你如何?
不幸的是,自从编写了接受的答案以来,库似乎已经更新,解决方案不再相同。经过反复试验,这似乎是处理签名的最新方法(使用 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`
}
你好 Whosebug AWS Gophers,
I'm implementing a CLI with the excellent cobra/viper packages from spf13。我们有一个 Athena 数据库,前面是一个 API 网关端点,它使用 IAM 进行身份验证。
也就是说,为了使用 Postman 与其端点进行交互,我必须将 AWS Signature
定义为授权方法,定义相应的 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/server 示例,拜托,那里有很多。
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}, ...)
下面的解决方案使用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
}
我自己还很新(第 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 客户端几乎一样,所以也许尝试使用它看看你如何?
不幸的是,自从编写了接受的答案以来,库似乎已经更新,解决方案不再相同。经过反复试验,这似乎是处理签名的最新方法(使用 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`
}