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 35 import (
36 "context"
37 "fmt"
38 "net/http"
39 "strings"
40 41 "github.com/aws/aws-sdk-go-v2/aws"
42 "github.com/aws/aws-sdk-go-v2/credentials/endpointcreds/internal/client"
43 "github.com/aws/smithy-go/middleware"
44 )
45 46 // ProviderName is the name of the credentials provider.
47 const ProviderName = `CredentialsEndpointProvider`
48 49 type getCredentialsAPIClient interface {
50 GetCredentials(context.Context, *client.GetCredentialsInput, ...func(*client.Options)) (*client.GetCredentialsOutput, error)
51 }
52 53 // Provider satisfies the aws.CredentialsProvider interface, and is a client to
54 // retrieve credentials from an arbitrary endpoint.
55 type Provider struct {
56 // The AWS Client to make HTTP requests to the endpoint with. The endpoint
57 // the request will be made to is provided by the aws.Config's
58 // EndpointResolver.
59 client getCredentialsAPIClient
60 61 options Options
62 }
63 64 // HTTPClient is a client for sending HTTP requests
65 type HTTPClient interface {
66 Do(*http.Request) (*http.Response, error)
67 }
68 69 // Options is structure of configurable options for Provider
70 type Options struct {
71 // Endpoint to retrieve credentials from. Required
72 Endpoint string
73 74 // HTTPClient to handle sending HTTP requests to the target endpoint.
75 HTTPClient HTTPClient
76 77 // Set of options to modify how the credentials operation is invoked.
78 APIOptions []func(*middleware.Stack) error
79 80 // The Retryer to be used for determining whether a failed requested should be retried
81 Retryer aws.Retryer
82 83 // Optional authorization token value if set will be used as the value of
84 // the Authorization header of the endpoint credential request.
85 //
86 // When constructed from environment, the provider will use the value of
87 // AWS_CONTAINER_AUTHORIZATION_TOKEN environment variable as the token
88 //
89 // Will be overridden if AuthorizationTokenProvider is configured
90 AuthorizationToken string
91 92 // Optional auth provider func to dynamically load the auth token from a file
93 // everytime a credential is retrieved
94 //
95 // When constructed from environment, the provider will read and use the content
96 // of the file pointed to by AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE environment variable
97 // as the auth token everytime credentials are retrieved
98 //
99 // Will override AuthorizationToken if configured
100 AuthorizationTokenProvider AuthTokenProvider
101 102 // The chain of providers that was used to create this provider
103 // These values are for reporting purposes and are not meant to be set up directly
104 CredentialSources []aws.CredentialSource
105 }
106 107 // AuthTokenProvider defines an interface to dynamically load a value to be passed
108 // for the Authorization header of a credentials request.
109 type AuthTokenProvider interface {
110 GetToken() (string, error)
111 }
112 113 // TokenProviderFunc is a func type implementing AuthTokenProvider interface
114 // and enables customizing token provider behavior
115 type TokenProviderFunc func() (string, error)
116 117 // GetToken func retrieves auth token according to TokenProviderFunc implementation
118 func (p TokenProviderFunc) GetToken() (string, error) {
119 return p()
120 }
121 122 // New returns a credentials Provider for retrieving AWS credentials
123 // from arbitrary endpoint.
124 func New(endpoint string, optFns ...func(*Options)) *Provider {
125 o := Options{
126 Endpoint: endpoint,
127 }
128 129 for _, fn := range optFns {
130 fn(&o)
131 }
132 133 p := &Provider{
134 client: client.New(client.Options{
135 HTTPClient: o.HTTPClient,
136 Endpoint: o.Endpoint,
137 APIOptions: o.APIOptions,
138 Retryer: o.Retryer,
139 }),
140 options: o,
141 }
142 143 return p
144 }
145 146 // Retrieve will attempt to request the credentials from the endpoint the Provider
147 // was configured for. And error will be returned if the retrieval fails.
148 func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
149 resp, err := p.getCredentials(ctx)
150 if err != nil {
151 return aws.Credentials{}, fmt.Errorf("failed to load credentials, %w", err)
152 }
153 154 creds := aws.Credentials{
155 AccessKeyID: resp.AccessKeyID,
156 SecretAccessKey: resp.SecretAccessKey,
157 SessionToken: resp.Token,
158 Source: ProviderName,
159 AccountID: resp.AccountID,
160 }
161 162 if resp.Expiration != nil {
163 creds.CanExpire = true
164 creds.Expires = *resp.Expiration
165 }
166 167 return creds, nil
168 }
169 170 func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) {
171 authToken, err := p.resolveAuthToken()
172 if err != nil {
173 return nil, fmt.Errorf("resolve auth token: %v", err)
174 }
175 176 return p.client.GetCredentials(ctx, &client.GetCredentialsInput{
177 AuthorizationToken: authToken,
178 })
179 }
180 181 func (p *Provider) resolveAuthToken() (string, error) {
182 authToken := p.options.AuthorizationToken
183 184 var err error
185 if p.options.AuthorizationTokenProvider != nil {
186 authToken, err = p.options.AuthorizationTokenProvider.GetToken()
187 if err != nil {
188 return "", err
189 }
190 }
191 192 if strings.ContainsAny(authToken, "\r\n") {
193 return "", fmt.Errorf("authorization token contains invalid newline sequence")
194 }
195 196 return authToken, nil
197 }
198 199 var _ aws.CredentialProviderSource = (*Provider)(nil)
200 201 // ProviderSources returns the credential chain that was used to construct this provider
202 func (p *Provider) ProviderSources() []aws.CredentialSource {
203 if p.options.CredentialSources == nil {
204 return []aws.CredentialSource{aws.CredentialSourceHTTP}
205 }
206 return p.options.CredentialSources
207 }
208