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