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// }
33package endpointcreds
34
35import (
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.
47const ProviderName = `CredentialsEndpointProvider`
48
49type 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.
55type 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
65type HTTPClient interface {
66 Do(*http.Request) (*http.Response, error)
67}
68
69// Options is structure of configurable options for Provider
70type 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
103// AuthTokenProvider defines an interface to dynamically load a value to be passed
104// for the Authorization header of a credentials request.
105type AuthTokenProvider interface {
106 GetToken() (string, error)
107}
108
109// TokenProviderFunc is a func type implementing AuthTokenProvider interface
110// and enables customizing token provider behavior
111type TokenProviderFunc func() (string, error)
112
113// GetToken func retrieves auth token according to TokenProviderFunc implementation
114func (p TokenProviderFunc) GetToken() (string, error) {
115 return p()
116}
117
118// New returns a credentials Provider for retrieving AWS credentials
119// from arbitrary endpoint.
120func New(endpoint string, optFns ...func(*Options)) *Provider {
121 o := Options{
122 Endpoint: endpoint,
123 }
124
125 for _, fn := range optFns {
126 fn(&o)
127 }
128
129 p := &Provider{
130 client: client.New(client.Options{
131 HTTPClient: o.HTTPClient,
132 Endpoint: o.Endpoint,
133 APIOptions: o.APIOptions,
134 Retryer: o.Retryer,
135 }),
136 options: o,
137 }
138
139 return p
140}
141
142// Retrieve will attempt to request the credentials from the endpoint the Provider
143// was configured for. And error will be returned if the retrieval fails.
144func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
145 resp, err := p.getCredentials(ctx)
146 if err != nil {
147 return aws.Credentials{}, fmt.Errorf("failed to load credentials, %w", err)
148 }
149
150 creds := aws.Credentials{
151 AccessKeyID: resp.AccessKeyID,
152 SecretAccessKey: resp.SecretAccessKey,
153 SessionToken: resp.Token,
154 Source: ProviderName,
155 AccountID: resp.AccountID,
156 }
157
158 if resp.Expiration != nil {
159 creds.CanExpire = true
160 creds.Expires = *resp.Expiration
161 }
162
163 return creds, nil
164}
165
166func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) {
167 authToken, err := p.resolveAuthToken()
168 if err != nil {
169 return nil, fmt.Errorf("resolve auth token: %v", err)
170 }
171
172 return p.client.GetCredentials(ctx, &client.GetCredentialsInput{
173 AuthorizationToken: authToken,
174 })
175}
176
177func (p *Provider) resolveAuthToken() (string, error) {
178 authToken := p.options.AuthorizationToken
179
180 var err error
181 if p.options.AuthorizationTokenProvider != nil {
182 authToken, err = p.options.AuthorizationTokenProvider.GetToken()
183 if err != nil {
184 return "", err
185 }
186 }
187
188 if strings.ContainsAny(authToken, "\r\n") {
189 return "", fmt.Errorf("authorization token contains invalid newline sequence")
190 }
191
192 return authToken, nil
193}