provider.go (5760B)
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 103 // AuthTokenProvider defines an interface to dynamically load a value to be passed 104 // for the Authorization header of a credentials request. 105 type AuthTokenProvider interface { 106 GetToken() (string, error) 107 } 108 109 // TokenProviderFunc is a func type implementing AuthTokenProvider interface 110 // and enables customizing token provider behavior 111 type TokenProviderFunc func() (string, error) 112 113 // GetToken func retrieves auth token according to TokenProviderFunc implementation 114 func (p TokenProviderFunc) GetToken() (string, error) { 115 return p() 116 } 117 118 // New returns a credentials Provider for retrieving AWS credentials 119 // from arbitrary endpoint. 120 func 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. 144 func (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 } 156 157 if resp.Expiration != nil { 158 creds.CanExpire = true 159 creds.Expires = *resp.Expiration 160 } 161 162 return creds, nil 163 } 164 165 func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) { 166 authToken, err := p.resolveAuthToken() 167 if err != nil { 168 return nil, fmt.Errorf("resolve auth token: %v", err) 169 } 170 171 return p.client.GetCredentials(ctx, &client.GetCredentialsInput{ 172 AuthorizationToken: authToken, 173 }) 174 } 175 176 func (p *Provider) resolveAuthToken() (string, error) { 177 authToken := p.options.AuthorizationToken 178 179 var err error 180 if p.options.AuthorizationTokenProvider != nil { 181 authToken, err = p.options.AuthorizationTokenProvider.GetToken() 182 if err != nil { 183 return "", err 184 } 185 } 186 187 if strings.ContainsAny(authToken, "\r\n") { 188 return "", fmt.Errorf("authorization token contains invalid newline sequence") 189 } 190 191 return authToken, nil 192 }