provider.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // Package endpointcreds provides support for retrieving credentials from an
  2. // arbitrary HTTP endpoint.
  3. //
  4. // The credentials endpoint Provider can receive both static and refreshable
  5. // credentials that will expire. Credentials are static when an "Expiration"
  6. // value is not provided in the endpoint's response.
  7. //
  8. // Static credentials will never expire once they have been retrieved. The format
  9. // of the static credentials response:
  10. //
  11. // {
  12. // "AccessKeyId" : "MUA...",
  13. // "SecretAccessKey" : "/7PC5om....",
  14. // }
  15. //
  16. // Refreshable credentials will expire within the "ExpiryWindow" of the Expiration
  17. // value in the response. The format of the refreshable credentials response:
  18. //
  19. // {
  20. // "AccessKeyId" : "MUA...",
  21. // "SecretAccessKey" : "/7PC5om....",
  22. // "Token" : "AQoDY....=",
  23. // "Expiration" : "2016-02-25T06:03:31Z"
  24. // }
  25. //
  26. // Errors should be returned in the following format and only returned with 400
  27. // or 500 HTTP status codes.
  28. //
  29. // {
  30. // "code": "ErrorCode",
  31. // "message": "Helpful error message."
  32. // }
  33. package endpointcreds
  34. import (
  35. "context"
  36. "fmt"
  37. "net/http"
  38. "strings"
  39. "github.com/aws/aws-sdk-go-v2/aws"
  40. "github.com/aws/aws-sdk-go-v2/credentials/endpointcreds/internal/client"
  41. "github.com/aws/smithy-go/middleware"
  42. )
  43. // ProviderName is the name of the credentials provider.
  44. const ProviderName = `CredentialsEndpointProvider`
  45. type getCredentialsAPIClient interface {
  46. GetCredentials(context.Context, *client.GetCredentialsInput, ...func(*client.Options)) (*client.GetCredentialsOutput, error)
  47. }
  48. // Provider satisfies the aws.CredentialsProvider interface, and is a client to
  49. // retrieve credentials from an arbitrary endpoint.
  50. type Provider struct {
  51. // The AWS Client to make HTTP requests to the endpoint with. The endpoint
  52. // the request will be made to is provided by the aws.Config's
  53. // EndpointResolver.
  54. client getCredentialsAPIClient
  55. options Options
  56. }
  57. // HTTPClient is a client for sending HTTP requests
  58. type HTTPClient interface {
  59. Do(*http.Request) (*http.Response, error)
  60. }
  61. // Options is structure of configurable options for Provider
  62. type Options struct {
  63. // Endpoint to retrieve credentials from. Required
  64. Endpoint string
  65. // HTTPClient to handle sending HTTP requests to the target endpoint.
  66. HTTPClient HTTPClient
  67. // Set of options to modify how the credentials operation is invoked.
  68. APIOptions []func(*middleware.Stack) error
  69. // The Retryer to be used for determining whether a failed requested should be retried
  70. Retryer aws.Retryer
  71. // Optional authorization token value if set will be used as the value of
  72. // the Authorization header of the endpoint credential request.
  73. //
  74. // When constructed from environment, the provider will use the value of
  75. // AWS_CONTAINER_AUTHORIZATION_TOKEN environment variable as the token
  76. //
  77. // Will be overridden if AuthorizationTokenProvider is configured
  78. AuthorizationToken string
  79. // Optional auth provider func to dynamically load the auth token from a file
  80. // everytime a credential is retrieved
  81. //
  82. // When constructed from environment, the provider will read and use the content
  83. // of the file pointed to by AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE environment variable
  84. // as the auth token everytime credentials are retrieved
  85. //
  86. // Will override AuthorizationToken if configured
  87. AuthorizationTokenProvider AuthTokenProvider
  88. // The chain of providers that was used to create this provider
  89. // These values are for reporting purposes and are not meant to be set up directly
  90. CredentialSources []aws.CredentialSource
  91. }
  92. // AuthTokenProvider defines an interface to dynamically load a value to be passed
  93. // for the Authorization header of a credentials request.
  94. type AuthTokenProvider interface {
  95. GetToken() (string, error)
  96. }
  97. // TokenProviderFunc is a func type implementing AuthTokenProvider interface
  98. // and enables customizing token provider behavior
  99. type TokenProviderFunc func() (string, error)
  100. // GetToken func retrieves auth token according to TokenProviderFunc implementation
  101. func (p TokenProviderFunc) GetToken() (string, error) {
  102. return p()
  103. }
  104. // New returns a credentials Provider for retrieving AWS credentials
  105. // from arbitrary endpoint.
  106. func New(endpoint string, optFns ...func(*Options)) *Provider {
  107. o := Options{
  108. Endpoint: endpoint,
  109. }
  110. for _, fn := range optFns {
  111. fn(&o)
  112. }
  113. p := &Provider{
  114. client: client.New(client.Options{
  115. HTTPClient: o.HTTPClient,
  116. Endpoint: o.Endpoint,
  117. APIOptions: o.APIOptions,
  118. Retryer: o.Retryer,
  119. }),
  120. options: o,
  121. }
  122. return p
  123. }
  124. // Retrieve will attempt to request the credentials from the endpoint the Provider
  125. // was configured for. And error will be returned if the retrieval fails.
  126. func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
  127. resp, err := p.getCredentials(ctx)
  128. if err != nil {
  129. return aws.Credentials{}, fmt.Errorf("failed to load credentials, %w", err)
  130. }
  131. creds := aws.Credentials{
  132. AccessKeyID: resp.AccessKeyID,
  133. SecretAccessKey: resp.SecretAccessKey,
  134. SessionToken: resp.Token,
  135. Source: ProviderName,
  136. AccountID: resp.AccountID,
  137. }
  138. if resp.Expiration != nil {
  139. creds.CanExpire = true
  140. creds.Expires = *resp.Expiration
  141. }
  142. return creds, nil
  143. }
  144. func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) {
  145. authToken, err := p.resolveAuthToken()
  146. if err != nil {
  147. return nil, fmt.Errorf("resolve auth token: %v", err)
  148. }
  149. return p.client.GetCredentials(ctx, &client.GetCredentialsInput{
  150. AuthorizationToken: authToken,
  151. })
  152. }
  153. func (p *Provider) resolveAuthToken() (string, error) {
  154. authToken := p.options.AuthorizationToken
  155. var err error
  156. if p.options.AuthorizationTokenProvider != nil {
  157. authToken, err = p.options.AuthorizationTokenProvider.GetToken()
  158. if err != nil {
  159. return "", err
  160. }
  161. }
  162. if strings.ContainsAny(authToken, "\r\n") {
  163. return "", fmt.Errorf("authorization token contains invalid newline sequence")
  164. }
  165. return authToken, nil
  166. }
  167. var _ aws.CredentialProviderSource = (*Provider)(nil)
  168. // ProviderSources returns the credential chain that was used to construct this provider
  169. func (p *Provider) ProviderSources() []aws.CredentialSource {
  170. if p.options.CredentialSources == nil {
  171. return []aws.CredentialSource{aws.CredentialSourceHTTP}
  172. }
  173. return p.options.CredentialSources
  174. }