provider.go (6334B)
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 }