1package session
2
3import (
4	"crypto/tls"
5	"crypto/x509"
6	"fmt"
7	"io"
8	"io/ioutil"
9	"net/http"
10	"os"
11	"strings"
12	"time"
13
14	"github.com/aws/aws-sdk-go/aws"
15	"github.com/aws/aws-sdk-go/aws/awserr"
16	"github.com/aws/aws-sdk-go/aws/client"
17	"github.com/aws/aws-sdk-go/aws/corehandlers"
18	"github.com/aws/aws-sdk-go/aws/credentials"
19	"github.com/aws/aws-sdk-go/aws/csm"
20	"github.com/aws/aws-sdk-go/aws/defaults"
21	"github.com/aws/aws-sdk-go/aws/endpoints"
22	"github.com/aws/aws-sdk-go/aws/request"
23)
24
25const (
26	// ErrCodeSharedConfig represents an error that occurs in the shared
27	// configuration logic
28	ErrCodeSharedConfig = "SharedConfigErr"
29
30	// ErrCodeLoadCustomCABundle error code for unable to load custom CA bundle.
31	ErrCodeLoadCustomCABundle = "LoadCustomCABundleError"
32
33	// ErrCodeLoadClientTLSCert error code for unable to load client TLS
34	// certificate or key
35	ErrCodeLoadClientTLSCert = "LoadClientTLSCertError"
36)
37
38// ErrSharedConfigSourceCollision will be returned if a section contains both
39// source_profile and credential_source
40var ErrSharedConfigSourceCollision = awserr.New(ErrCodeSharedConfig, "only one credential type may be specified per profile: source profile, credential source, credential process, web identity token, or sso", nil)
41
42// ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment
43// variables are empty and Environment was set as the credential source
44var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil)
45
46// ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided
47var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil)
48
49// A Session provides a central location to create service clients from and
50// store configurations and request handlers for those services.
51//
52// Sessions are safe to create service clients concurrently, but it is not safe
53// to mutate the Session concurrently.
54//
55// The Session satisfies the service client's client.ConfigProvider.
56type Session struct {
57	Config   *aws.Config
58	Handlers request.Handlers
59
60	options Options
61}
62
63// New creates a new instance of the handlers merging in the provided configs
64// on top of the SDK's default configurations. Once the Session is created it
65// can be mutated to modify the Config or Handlers. The Session is safe to be
66// read concurrently, but it should not be written to concurrently.
67//
68// If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New
69// method could now encounter an error when loading the configuration. When
70// The environment variable is set, and an error occurs, New will return a
71// session that will fail all requests reporting the error that occurred while
72// loading the session. Use NewSession to get the error when creating the
73// session.
74//
75// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
76// the shared config file (~/.aws/config) will also be loaded, in addition to
77// the shared credentials file (~/.aws/credentials). Values set in both the
78// shared config, and shared credentials will be taken from the shared
79// credentials file.
80//
81// Deprecated: Use NewSession functions to create sessions instead. NewSession
82// has the same functionality as New except an error can be returned when the
83// func is called instead of waiting to receive an error until a request is made.
84func New(cfgs ...*aws.Config) *Session {
85	// load initial config from environment
86	envCfg, envErr := loadEnvConfig()
87
88	if envCfg.EnableSharedConfig {
89		var cfg aws.Config
90		cfg.MergeIn(cfgs...)
91		s, err := NewSessionWithOptions(Options{
92			Config:            cfg,
93			SharedConfigState: SharedConfigEnable,
94		})
95		if err != nil {
96			// Old session.New expected all errors to be discovered when
97			// a request is made, and would report the errors then. This
98			// needs to be replicated if an error occurs while creating
99			// the session.
100			msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " +
101				"Use session.NewSession to handle errors occurring during session creation."
102
103			// Session creation failed, need to report the error and prevent
104			// any requests from succeeding.
105			s = &Session{Config: defaults.Config()}
106			s.logDeprecatedNewSessionError(msg, err, cfgs)
107		}
108
109		return s
110	}
111
112	s := deprecatedNewSession(envCfg, cfgs...)
113	if envErr != nil {
114		msg := "failed to load env config"
115		s.logDeprecatedNewSessionError(msg, envErr, cfgs)
116	}
117
118	if csmCfg, err := loadCSMConfig(envCfg, []string{}); err != nil {
119		if l := s.Config.Logger; l != nil {
120			l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err))
121		}
122	} else if csmCfg.Enabled {
123		err := enableCSM(&s.Handlers, csmCfg, s.Config.Logger)
124		if err != nil {
125			msg := "failed to enable CSM"
126			s.logDeprecatedNewSessionError(msg, err, cfgs)
127		}
128	}
129
130	return s
131}
132
133// NewSession returns a new Session created from SDK defaults, config files,
134// environment, and user provided config files. Once the Session is created
135// it can be mutated to modify the Config or Handlers. The Session is safe to
136// be read concurrently, but it should not be written to concurrently.
137//
138// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
139// the shared config file (~/.aws/config) will also be loaded in addition to
140// the shared credentials file (~/.aws/credentials). Values set in both the
141// shared config, and shared credentials will be taken from the shared
142// credentials file. Enabling the Shared Config will also allow the Session
143// to be built with retrieving credentials with AssumeRole set in the config.
144//
145// See the NewSessionWithOptions func for information on how to override or
146// control through code how the Session will be created, such as specifying the
147// config profile, and controlling if shared config is enabled or not.
148func NewSession(cfgs ...*aws.Config) (*Session, error) {
149	opts := Options{}
150	opts.Config.MergeIn(cfgs...)
151
152	return NewSessionWithOptions(opts)
153}
154
155// SharedConfigState provides the ability to optionally override the state
156// of the session's creation based on the shared config being enabled or
157// disabled.
158type SharedConfigState int
159
160const (
161	// SharedConfigStateFromEnv does not override any state of the
162	// AWS_SDK_LOAD_CONFIG env var. It is the default value of the
163	// SharedConfigState type.
164	SharedConfigStateFromEnv SharedConfigState = iota
165
166	// SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value
167	// and disables the shared config functionality.
168	SharedConfigDisable
169
170	// SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value
171	// and enables the shared config functionality.
172	SharedConfigEnable
173)
174
175// Options provides the means to control how a Session is created and what
176// configuration values will be loaded.
177//
178type Options struct {
179	// Provides config values for the SDK to use when creating service clients
180	// and making API requests to services. Any value set in with this field
181	// will override the associated value provided by the SDK defaults,
182	// environment or config files where relevant.
183	//
184	// If not set, configuration values from from SDK defaults, environment,
185	// config will be used.
186	Config aws.Config
187
188	// Overrides the config profile the Session should be created from. If not
189	// set the value of the environment variable will be loaded (AWS_PROFILE,
190	// or AWS_DEFAULT_PROFILE if the Shared Config is enabled).
191	//
192	// If not set and environment variables are not set the "default"
193	// (DefaultSharedConfigProfile) will be used as the profile to load the
194	// session config from.
195	Profile string
196
197	// Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG
198	// environment variable. By default a Session will be created using the
199	// value provided by the AWS_SDK_LOAD_CONFIG environment variable.
200	//
201	// Setting this value to SharedConfigEnable or SharedConfigDisable
202	// will allow you to override the AWS_SDK_LOAD_CONFIG environment variable
203	// and enable or disable the shared config functionality.
204	SharedConfigState SharedConfigState
205
206	// Ordered list of files the session will load configuration from.
207	// It will override environment variable AWS_SHARED_CREDENTIALS_FILE, AWS_CONFIG_FILE.
208	SharedConfigFiles []string
209
210	// When the SDK's shared config is configured to assume a role with MFA
211	// this option is required in order to provide the mechanism that will
212	// retrieve the MFA token. There is no default value for this field. If
213	// it is not set an error will be returned when creating the session.
214	//
215	// This token provider will be called when ever the assumed role's
216	// credentials need to be refreshed. Within the context of service clients
217	// all sharing the same session the SDK will ensure calls to the token
218	// provider are atomic. When sharing a token provider across multiple
219	// sessions additional synchronization logic is needed to ensure the
220	// token providers do not introduce race conditions. It is recommend to
221	// share the session where possible.
222	//
223	// stscreds.StdinTokenProvider is a basic implementation that will prompt
224	// from stdin for the MFA token code.
225	//
226	// This field is only used if the shared configuration is enabled, and
227	// the config enables assume role wit MFA via the mfa_serial field.
228	AssumeRoleTokenProvider func() (string, error)
229
230	// When the SDK's shared config is configured to assume a role this option
231	// may be provided to set the expiry duration of the STS credentials.
232	// Defaults to 15 minutes if not set as documented in the
233	// stscreds.AssumeRoleProvider.
234	AssumeRoleDuration time.Duration
235
236	// Reader for a custom Credentials Authority (CA) bundle in PEM format that
237	// the SDK will use instead of the default system's root CA bundle. Use this
238	// only if you want to replace the CA bundle the SDK uses for TLS requests.
239	//
240	// HTTP Client's Transport concrete implementation must be a http.Transport
241	// or creating the session will fail.
242	//
243	// If the Transport's TLS config is set this option will cause the SDK
244	// to overwrite the Transport's TLS config's  RootCAs value. If the CA
245	// bundle reader contains multiple certificates all of them will be loaded.
246	//
247	// Can also be specified via the environment variable:
248	//
249	//  AWS_CA_BUNDLE=$HOME/ca_bundle
250	//
251	// Can also be specified via the shared config field:
252	//
253	//  ca_bundle = $HOME/ca_bundle
254	CustomCABundle io.Reader
255
256	// Reader for the TLC client certificate that should be used by the SDK's
257	// HTTP transport when making requests. The certificate must be paired with
258	// a TLS client key file. Will be ignored if both are not provided.
259	//
260	// HTTP Client's Transport concrete implementation must be a http.Transport
261	// or creating the session will fail.
262	//
263	// Can also be specified via the environment variable:
264	//
265	//  AWS_SDK_GO_CLIENT_TLS_CERT=$HOME/my_client_cert
266	ClientTLSCert io.Reader
267
268	// Reader for the TLC client key that should be used by the SDK's HTTP
269	// transport when making requests. The key must be paired with a TLS client
270	// certificate file. Will be ignored if both are not provided.
271	//
272	// HTTP Client's Transport concrete implementation must be a http.Transport
273	// or creating the session will fail.
274	//
275	// Can also be specified via the environment variable:
276	//
277	//  AWS_SDK_GO_CLIENT_TLS_KEY=$HOME/my_client_key
278	ClientTLSKey io.Reader
279
280	// The handlers that the session and all API clients will be created with.
281	// This must be a complete set of handlers. Use the defaults.Handlers()
282	// function to initialize this value before changing the handlers to be
283	// used by the SDK.
284	Handlers request.Handlers
285
286	// Allows specifying a custom endpoint to be used by the EC2 IMDS client
287	// when making requests to the EC2 IMDS API. The endpoint value should
288	// include the URI scheme. If the scheme is not present it will be defaulted to http.
289	//
290	// If unset, will the EC2 IMDS client will use its default endpoint.
291	//
292	// Can also be specified via the environment variable,
293	// AWS_EC2_METADATA_SERVICE_ENDPOINT.
294	//
295	//   AWS_EC2_METADATA_SERVICE_ENDPOINT=http://169.254.169.254
296	//
297	// If using an URL with an IPv6 address literal, the IPv6 address
298	// component must be enclosed in square brackets.
299	//
300	//   AWS_EC2_METADATA_SERVICE_ENDPOINT=http://[::1]
301	EC2IMDSEndpoint string
302
303	// Specifies the EC2 Instance Metadata Service default endpoint selection mode (IPv4 or IPv6)
304	//
305	// AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE=IPv6
306	EC2IMDSEndpointMode endpoints.EC2IMDSEndpointModeState
307}
308
309// NewSessionWithOptions returns a new Session created from SDK defaults, config files,
310// environment, and user provided config files. This func uses the Options
311// values to configure how the Session is created.
312//
313// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
314// the shared config file (~/.aws/config) will also be loaded in addition to
315// the shared credentials file (~/.aws/credentials). Values set in both the
316// shared config, and shared credentials will be taken from the shared
317// credentials file. Enabling the Shared Config will also allow the Session
318// to be built with retrieving credentials with AssumeRole set in the config.
319//
320//     // Equivalent to session.New
321//     sess := session.Must(session.NewSessionWithOptions(session.Options{}))
322//
323//     // Specify profile to load for the session's config
324//     sess := session.Must(session.NewSessionWithOptions(session.Options{
325//          Profile: "profile_name",
326//     }))
327//
328//     // Specify profile for config and region for requests
329//     sess := session.Must(session.NewSessionWithOptions(session.Options{
330//          Config: aws.Config{Region: aws.String("us-east-1")},
331//          Profile: "profile_name",
332//     }))
333//
334//     // Force enable Shared Config support
335//     sess := session.Must(session.NewSessionWithOptions(session.Options{
336//         SharedConfigState: session.SharedConfigEnable,
337//     }))
338func NewSessionWithOptions(opts Options) (*Session, error) {
339	var envCfg envConfig
340	var err error
341	if opts.SharedConfigState == SharedConfigEnable {
342		envCfg, err = loadSharedEnvConfig()
343		if err != nil {
344			return nil, fmt.Errorf("failed to load shared config, %v", err)
345		}
346	} else {
347		envCfg, err = loadEnvConfig()
348		if err != nil {
349			return nil, fmt.Errorf("failed to load environment config, %v", err)
350		}
351	}
352
353	if len(opts.Profile) != 0 {
354		envCfg.Profile = opts.Profile
355	}
356
357	switch opts.SharedConfigState {
358	case SharedConfigDisable:
359		envCfg.EnableSharedConfig = false
360	case SharedConfigEnable:
361		envCfg.EnableSharedConfig = true
362	}
363
364	return newSession(opts, envCfg, &opts.Config)
365}
366
367// Must is a helper function to ensure the Session is valid and there was no
368// error when calling a NewSession function.
369//
370// This helper is intended to be used in variable initialization to load the
371// Session and configuration at startup. Such as:
372//
373//     var sess = session.Must(session.NewSession())
374func Must(sess *Session, err error) *Session {
375	if err != nil {
376		panic(err)
377	}
378
379	return sess
380}
381
382// Wraps the endpoint resolver with a resolver that will return a custom
383// endpoint for EC2 IMDS.
384func wrapEC2IMDSEndpoint(resolver endpoints.Resolver, endpoint string, mode endpoints.EC2IMDSEndpointModeState) endpoints.Resolver {
385	return endpoints.ResolverFunc(
386		func(service, region string, opts ...func(*endpoints.Options)) (
387			endpoints.ResolvedEndpoint, error,
388		) {
389			if service == ec2MetadataServiceID && len(endpoint) > 0 {
390				return endpoints.ResolvedEndpoint{
391					URL:           endpoint,
392					SigningName:   ec2MetadataServiceID,
393					SigningRegion: region,
394				}, nil
395			} else if service == ec2MetadataServiceID {
396				opts = append(opts, func(o *endpoints.Options) {
397					o.EC2MetadataEndpointMode = mode
398				})
399			}
400			return resolver.EndpointFor(service, region, opts...)
401		})
402}
403
404func deprecatedNewSession(envCfg envConfig, cfgs ...*aws.Config) *Session {
405	cfg := defaults.Config()
406	handlers := defaults.Handlers()
407
408	// Apply the passed in configs so the configuration can be applied to the
409	// default credential chain
410	cfg.MergeIn(cfgs...)
411	if cfg.EndpointResolver == nil {
412		// An endpoint resolver is required for a session to be able to provide
413		// endpoints for service client configurations.
414		cfg.EndpointResolver = endpoints.DefaultResolver()
415	}
416
417	if !(len(envCfg.EC2IMDSEndpoint) == 0 && envCfg.EC2IMDSEndpointMode == endpoints.EC2IMDSEndpointModeStateUnset) {
418		cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, envCfg.EC2IMDSEndpoint, envCfg.EC2IMDSEndpointMode)
419	}
420
421	cfg.Credentials = defaults.CredChain(cfg, handlers)
422
423	// Reapply any passed in configs to override credentials if set
424	cfg.MergeIn(cfgs...)
425
426	s := &Session{
427		Config:   cfg,
428		Handlers: handlers,
429		options: Options{
430			EC2IMDSEndpoint: envCfg.EC2IMDSEndpoint,
431		},
432	}
433
434	initHandlers(s)
435	return s
436}
437
438func enableCSM(handlers *request.Handlers, cfg csmConfig, logger aws.Logger) error {
439	if logger != nil {
440		logger.Log("Enabling CSM")
441	}
442
443	r, err := csm.Start(cfg.ClientID, csm.AddressWithDefaults(cfg.Host, cfg.Port))
444	if err != nil {
445		return err
446	}
447	r.InjectHandlers(handlers)
448
449	return nil
450}
451
452func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
453	cfg := defaults.Config()
454
455	handlers := opts.Handlers
456	if handlers.IsEmpty() {
457		handlers = defaults.Handlers()
458	}
459
460	// Get a merged version of the user provided config to determine if
461	// credentials were.
462	userCfg := &aws.Config{}
463	userCfg.MergeIn(cfgs...)
464	cfg.MergeIn(userCfg)
465
466	// Ordered config files will be loaded in with later files overwriting
467	// previous config file values.
468	var cfgFiles []string
469	if opts.SharedConfigFiles != nil {
470		cfgFiles = opts.SharedConfigFiles
471	} else {
472		cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
473		if !envCfg.EnableSharedConfig {
474			// The shared config file (~/.aws/config) is only loaded if instructed
475			// to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
476			cfgFiles = cfgFiles[1:]
477		}
478	}
479
480	// Load additional config from file(s)
481	sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig)
482	if err != nil {
483		if len(envCfg.Profile) == 0 && !envCfg.EnableSharedConfig && (envCfg.Creds.HasKeys() || userCfg.Credentials != nil) {
484			// Special case where the user has not explicitly specified an AWS_PROFILE,
485			// or session.Options.profile, shared config is not enabled, and the
486			// environment has credentials, allow the shared config file to fail to
487			// load since the user has already provided credentials, and nothing else
488			// is required to be read file. Github(aws/aws-sdk-go#2455)
489		} else if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
490			return nil, err
491		}
492	}
493
494	if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil {
495		return nil, err
496	}
497
498	if err := setTLSOptions(&opts, cfg, envCfg, sharedCfg); err != nil {
499		return nil, err
500	}
501
502	s := &Session{
503		Config:   cfg,
504		Handlers: handlers,
505		options:  opts,
506	}
507
508	initHandlers(s)
509
510	if csmCfg, err := loadCSMConfig(envCfg, cfgFiles); err != nil {
511		if l := s.Config.Logger; l != nil {
512			l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err))
513		}
514	} else if csmCfg.Enabled {
515		err = enableCSM(&s.Handlers, csmCfg, s.Config.Logger)
516		if err != nil {
517			return nil, err
518		}
519	}
520
521	return s, nil
522}
523
524type csmConfig struct {
525	Enabled  bool
526	Host     string
527	Port     string
528	ClientID string
529}
530
531var csmProfileName = "aws_csm"
532
533func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) {
534	if envCfg.CSMEnabled != nil {
535		if *envCfg.CSMEnabled {
536			return csmConfig{
537				Enabled:  true,
538				ClientID: envCfg.CSMClientID,
539				Host:     envCfg.CSMHost,
540				Port:     envCfg.CSMPort,
541			}, nil
542		}
543		return csmConfig{}, nil
544	}
545
546	sharedCfg, err := loadSharedConfig(csmProfileName, cfgFiles, false)
547	if err != nil {
548		if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
549			return csmConfig{}, err
550		}
551	}
552	if sharedCfg.CSMEnabled != nil && *sharedCfg.CSMEnabled == true {
553		return csmConfig{
554			Enabled:  true,
555			ClientID: sharedCfg.CSMClientID,
556			Host:     sharedCfg.CSMHost,
557			Port:     sharedCfg.CSMPort,
558		}, nil
559	}
560
561	return csmConfig{}, nil
562}
563
564func setTLSOptions(opts *Options, cfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig) error {
565	// CA Bundle can be specified in both environment variable shared config file.
566	var caBundleFilename = envCfg.CustomCABundle
567	if len(caBundleFilename) == 0 {
568		caBundleFilename = sharedCfg.CustomCABundle
569	}
570
571	// Only use environment value if session option is not provided.
572	customTLSOptions := map[string]struct {
573		filename string
574		field    *io.Reader
575		errCode  string
576	}{
577		"custom CA bundle PEM":   {filename: caBundleFilename, field: &opts.CustomCABundle, errCode: ErrCodeLoadCustomCABundle},
578		"custom client TLS cert": {filename: envCfg.ClientTLSCert, field: &opts.ClientTLSCert, errCode: ErrCodeLoadClientTLSCert},
579		"custom client TLS key":  {filename: envCfg.ClientTLSKey, field: &opts.ClientTLSKey, errCode: ErrCodeLoadClientTLSCert},
580	}
581	for name, v := range customTLSOptions {
582		if len(v.filename) != 0 && *v.field == nil {
583			f, err := os.Open(v.filename)
584			if err != nil {
585				return awserr.New(v.errCode, fmt.Sprintf("failed to open %s file", name), err)
586			}
587			defer f.Close()
588			*v.field = f
589		}
590	}
591
592	// Setup HTTP client with custom cert bundle if enabled
593	if opts.CustomCABundle != nil {
594		if err := loadCustomCABundle(cfg.HTTPClient, opts.CustomCABundle); err != nil {
595			return err
596		}
597	}
598
599	// Setup HTTP client TLS certificate and key for client TLS authentication.
600	if opts.ClientTLSCert != nil && opts.ClientTLSKey != nil {
601		if err := loadClientTLSCert(cfg.HTTPClient, opts.ClientTLSCert, opts.ClientTLSKey); err != nil {
602			return err
603		}
604	} else if opts.ClientTLSCert == nil && opts.ClientTLSKey == nil {
605		// Do nothing if neither values are available.
606
607	} else {
608		return awserr.New(ErrCodeLoadClientTLSCert,
609			fmt.Sprintf("client TLS cert(%t) and key(%t) must both be provided",
610				opts.ClientTLSCert != nil, opts.ClientTLSKey != nil), nil)
611	}
612
613	return nil
614}
615
616func getHTTPTransport(client *http.Client) (*http.Transport, error) {
617	var t *http.Transport
618	switch v := client.Transport.(type) {
619	case *http.Transport:
620		t = v
621	default:
622		if client.Transport != nil {
623			return nil, fmt.Errorf("unsupported transport, %T", client.Transport)
624		}
625	}
626	if t == nil {
627		// Nil transport implies `http.DefaultTransport` should be used. Since
628		// the SDK cannot modify, nor copy the `DefaultTransport` specifying
629		// the values the next closest behavior.
630		t = getCustomTransport()
631	}
632
633	return t, nil
634}
635
636func loadCustomCABundle(client *http.Client, bundle io.Reader) error {
637	t, err := getHTTPTransport(client)
638	if err != nil {
639		return awserr.New(ErrCodeLoadCustomCABundle,
640			"unable to load custom CA bundle, HTTPClient's transport unsupported type", err)
641	}
642
643	p, err := loadCertPool(bundle)
644	if err != nil {
645		return err
646	}
647	if t.TLSClientConfig == nil {
648		t.TLSClientConfig = &tls.Config{}
649	}
650	t.TLSClientConfig.RootCAs = p
651
652	client.Transport = t
653
654	return nil
655}
656
657func loadCertPool(r io.Reader) (*x509.CertPool, error) {
658	b, err := ioutil.ReadAll(r)
659	if err != nil {
660		return nil, awserr.New(ErrCodeLoadCustomCABundle,
661			"failed to read custom CA bundle PEM file", err)
662	}
663
664	p := x509.NewCertPool()
665	if !p.AppendCertsFromPEM(b) {
666		return nil, awserr.New(ErrCodeLoadCustomCABundle,
667			"failed to load custom CA bundle PEM file", err)
668	}
669
670	return p, nil
671}
672
673func loadClientTLSCert(client *http.Client, certFile, keyFile io.Reader) error {
674	t, err := getHTTPTransport(client)
675	if err != nil {
676		return awserr.New(ErrCodeLoadClientTLSCert,
677			"unable to get usable HTTP transport from client", err)
678	}
679
680	cert, err := ioutil.ReadAll(certFile)
681	if err != nil {
682		return awserr.New(ErrCodeLoadClientTLSCert,
683			"unable to get read client TLS cert file", err)
684	}
685
686	key, err := ioutil.ReadAll(keyFile)
687	if err != nil {
688		return awserr.New(ErrCodeLoadClientTLSCert,
689			"unable to get read client TLS key file", err)
690	}
691
692	clientCert, err := tls.X509KeyPair(cert, key)
693	if err != nil {
694		return awserr.New(ErrCodeLoadClientTLSCert,
695			"unable to load x509 key pair from client cert", err)
696	}
697
698	tlsCfg := t.TLSClientConfig
699	if tlsCfg == nil {
700		tlsCfg = &tls.Config{}
701	}
702
703	tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
704
705	t.TLSClientConfig = tlsCfg
706	client.Transport = t
707
708	return nil
709}
710
711func mergeConfigSrcs(cfg, userCfg *aws.Config,
712	envCfg envConfig, sharedCfg sharedConfig,
713	handlers request.Handlers,
714	sessOpts Options,
715) error {
716
717	// Region if not already set by user
718	if len(aws.StringValue(cfg.Region)) == 0 {
719		if len(envCfg.Region) > 0 {
720			cfg.WithRegion(envCfg.Region)
721		} else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
722			cfg.WithRegion(sharedCfg.Region)
723		}
724	}
725
726	if cfg.EnableEndpointDiscovery == nil {
727		if envCfg.EnableEndpointDiscovery != nil {
728			cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery)
729		} else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil {
730			cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery)
731		}
732	}
733
734	// Regional Endpoint flag for STS endpoint resolving
735	mergeSTSRegionalEndpointConfig(cfg, []endpoints.STSRegionalEndpoint{
736		userCfg.STSRegionalEndpoint,
737		envCfg.STSRegionalEndpoint,
738		sharedCfg.STSRegionalEndpoint,
739		endpoints.LegacySTSEndpoint,
740	})
741
742	// Regional Endpoint flag for S3 endpoint resolving
743	mergeS3UsEast1RegionalEndpointConfig(cfg, []endpoints.S3UsEast1RegionalEndpoint{
744		userCfg.S3UsEast1RegionalEndpoint,
745		envCfg.S3UsEast1RegionalEndpoint,
746		sharedCfg.S3UsEast1RegionalEndpoint,
747		endpoints.LegacyS3UsEast1Endpoint,
748	})
749
750	var ec2IMDSEndpoint string
751	for _, v := range []string{
752		sessOpts.EC2IMDSEndpoint,
753		envCfg.EC2IMDSEndpoint,
754		sharedCfg.EC2IMDSEndpoint,
755	} {
756		if len(v) != 0 {
757			ec2IMDSEndpoint = v
758			break
759		}
760	}
761
762	var endpointMode endpoints.EC2IMDSEndpointModeState
763	for _, v := range []endpoints.EC2IMDSEndpointModeState{
764		sessOpts.EC2IMDSEndpointMode,
765		envCfg.EC2IMDSEndpointMode,
766		sharedCfg.EC2IMDSEndpointMode,
767	} {
768		if v != endpoints.EC2IMDSEndpointModeStateUnset {
769			endpointMode = v
770			break
771		}
772	}
773
774	if len(ec2IMDSEndpoint) != 0 || endpointMode != endpoints.EC2IMDSEndpointModeStateUnset {
775		cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, ec2IMDSEndpoint, endpointMode)
776	}
777
778	// Configure credentials if not already set by the user when creating the
779	// Session.
780	if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
781		creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts)
782		if err != nil {
783			return err
784		}
785		cfg.Credentials = creds
786	}
787
788	cfg.S3UseARNRegion = userCfg.S3UseARNRegion
789	if cfg.S3UseARNRegion == nil {
790		cfg.S3UseARNRegion = &envCfg.S3UseARNRegion
791	}
792	if cfg.S3UseARNRegion == nil {
793		cfg.S3UseARNRegion = &sharedCfg.S3UseARNRegion
794	}
795
796	for _, v := range []endpoints.DualStackEndpointState{userCfg.UseDualStackEndpoint, envCfg.UseDualStackEndpoint, sharedCfg.UseDualStackEndpoint} {
797		if v != endpoints.DualStackEndpointStateUnset {
798			cfg.UseDualStackEndpoint = v
799			break
800		}
801	}
802
803	for _, v := range []endpoints.FIPSEndpointState{userCfg.UseFIPSEndpoint, envCfg.UseFIPSEndpoint, sharedCfg.UseFIPSEndpoint} {
804		if v != endpoints.FIPSEndpointStateUnset {
805			cfg.UseFIPSEndpoint = v
806			break
807		}
808	}
809
810	return nil
811}
812
813func mergeSTSRegionalEndpointConfig(cfg *aws.Config, values []endpoints.STSRegionalEndpoint) {
814	for _, v := range values {
815		if v != endpoints.UnsetSTSEndpoint {
816			cfg.STSRegionalEndpoint = v
817			break
818		}
819	}
820}
821
822func mergeS3UsEast1RegionalEndpointConfig(cfg *aws.Config, values []endpoints.S3UsEast1RegionalEndpoint) {
823	for _, v := range values {
824		if v != endpoints.UnsetS3UsEast1Endpoint {
825			cfg.S3UsEast1RegionalEndpoint = v
826			break
827		}
828	}
829}
830
831func initHandlers(s *Session) {
832	// Add the Validate parameter handler if it is not disabled.
833	s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
834	if !aws.BoolValue(s.Config.DisableParamValidation) {
835		s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
836	}
837}
838
839// Copy creates and returns a copy of the current Session, copying the config
840// and handlers. If any additional configs are provided they will be merged
841// on top of the Session's copied config.
842//
843//     // Create a copy of the current Session, configured for the us-west-2 region.
844//     sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
845func (s *Session) Copy(cfgs ...*aws.Config) *Session {
846	newSession := &Session{
847		Config:   s.Config.Copy(cfgs...),
848		Handlers: s.Handlers.Copy(),
849		options:  s.options,
850	}
851
852	initHandlers(newSession)
853
854	return newSession
855}
856
857// ClientConfig satisfies the client.ConfigProvider interface and is used to
858// configure the service client instances. Passing the Session to the service
859// client's constructor (New) will use this method to configure the client.
860func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Config {
861	s = s.Copy(cfgs...)
862
863	resolvedRegion := normalizeRegion(s.Config)
864
865	region := aws.StringValue(s.Config.Region)
866	resolved, err := s.resolveEndpoint(service, region, resolvedRegion, s.Config)
867	if err != nil {
868		s.Handlers.Validate.PushBack(func(r *request.Request) {
869			if len(r.ClientInfo.Endpoint) != 0 {
870				// Error occurred while resolving endpoint, but the request
871				// being invoked has had an endpoint specified after the client
872				// was created.
873				return
874			}
875			r.Error = err
876		})
877	}
878
879	return client.Config{
880		Config:             s.Config,
881		Handlers:           s.Handlers,
882		PartitionID:        resolved.PartitionID,
883		Endpoint:           resolved.URL,
884		SigningRegion:      resolved.SigningRegion,
885		SigningNameDerived: resolved.SigningNameDerived,
886		SigningName:        resolved.SigningName,
887		ResolvedRegion:     resolvedRegion,
888	}
889}
890
891const ec2MetadataServiceID = "ec2metadata"
892
893func (s *Session) resolveEndpoint(service, region, resolvedRegion string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) {
894
895	if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 {
896		return endpoints.ResolvedEndpoint{
897			URL:           endpoints.AddScheme(ep, aws.BoolValue(cfg.DisableSSL)),
898			SigningRegion: region,
899		}, nil
900	}
901
902	resolved, err := cfg.EndpointResolver.EndpointFor(service, region,
903		func(opt *endpoints.Options) {
904			opt.DisableSSL = aws.BoolValue(cfg.DisableSSL)
905
906			opt.UseDualStack = aws.BoolValue(cfg.UseDualStack)
907			opt.UseDualStackEndpoint = cfg.UseDualStackEndpoint
908
909			opt.UseFIPSEndpoint = cfg.UseFIPSEndpoint
910
911			// Support for STSRegionalEndpoint where the STSRegionalEndpoint is
912			// provided in envConfig or sharedConfig with envConfig getting
913			// precedence.
914			opt.STSRegionalEndpoint = cfg.STSRegionalEndpoint
915
916			// Support for S3UsEast1RegionalEndpoint where the S3UsEast1RegionalEndpoint is
917			// provided in envConfig or sharedConfig with envConfig getting
918			// precedence.
919			opt.S3UsEast1RegionalEndpoint = cfg.S3UsEast1RegionalEndpoint
920
921			// Support the condition where the service is modeled but its
922			// endpoint metadata is not available.
923			opt.ResolveUnknownService = true
924
925			opt.ResolvedRegion = resolvedRegion
926
927			opt.Logger = cfg.Logger
928			opt.LogDeprecated = cfg.LogLevel.Matches(aws.LogDebugWithDeprecated)
929		},
930	)
931	if err != nil {
932		return endpoints.ResolvedEndpoint{}, err
933	}
934
935	return resolved, nil
936}
937
938// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception
939// that the EndpointResolver will not be used to resolve the endpoint. The only
940// endpoint set must come from the aws.Config.Endpoint field.
941func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config {
942	s = s.Copy(cfgs...)
943
944	resolvedRegion := normalizeRegion(s.Config)
945
946	var resolved endpoints.ResolvedEndpoint
947	if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 {
948		resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL))
949		resolved.SigningRegion = aws.StringValue(s.Config.Region)
950	}
951
952	return client.Config{
953		Config:             s.Config,
954		Handlers:           s.Handlers,
955		Endpoint:           resolved.URL,
956		SigningRegion:      resolved.SigningRegion,
957		SigningNameDerived: resolved.SigningNameDerived,
958		SigningName:        resolved.SigningName,
959		ResolvedRegion:     resolvedRegion,
960	}
961}
962
963// logDeprecatedNewSessionError function enables error handling for session
964func (s *Session) logDeprecatedNewSessionError(msg string, err error, cfgs []*aws.Config) {
965	// Session creation failed, need to report the error and prevent
966	// any requests from succeeding.
967	s.Config.MergeIn(cfgs...)
968	s.Config.Logger.Log("ERROR:", msg, "Error:", err)
969	s.Handlers.Validate.PushBack(func(r *request.Request) {
970		r.Error = err
971	})
972}
973
974// normalizeRegion resolves / normalizes the configured region (converts pseudo fips regions), and modifies the provided
975// config to have the equivalent options for resolution and returns the resolved region name.
976func normalizeRegion(cfg *aws.Config) (resolved string) {
977	const fipsInfix = "-fips-"
978	const fipsPrefix = "-fips"
979	const fipsSuffix = "fips-"
980
981	region := aws.StringValue(cfg.Region)
982
983	if strings.Contains(region, fipsInfix) ||
984		strings.Contains(region, fipsPrefix) ||
985		strings.Contains(region, fipsSuffix) {
986		resolved = strings.Replace(strings.Replace(strings.Replace(
987			region, fipsInfix, "-", -1), fipsPrefix, "", -1), fipsSuffix, "", -1)
988		cfg.UseFIPSEndpoint = endpoints.FIPSEndpointStateEnabled
989	}
990
991	return resolved
992}
993