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 endpoint value should
287	// include the URI scheme. If the scheme is not present it will be defaulted to http.
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	// Specifies the EC2 Instance Metadata Service default endpoint selection mode (IPv4 or IPv6)
303	//
304	// AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE=IPv6
305	EC2IMDSEndpointMode endpoints.EC2IMDSEndpointModeState
306}
307
308// NewSessionWithOptions returns a new Session created from SDK defaults, config files,
309// environment, and user provided config files. This func uses the Options
310// values to configure how the Session is created.
311//
312// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
313// the shared config file (~/.aws/config) will also be loaded in addition to
314// the shared credentials file (~/.aws/credentials). Values set in both the
315// shared config, and shared credentials will be taken from the shared
316// credentials file. Enabling the Shared Config will also allow the Session
317// to be built with retrieving credentials with AssumeRole set in the config.
318//
319//     // Equivalent to session.New
320//     sess := session.Must(session.NewSessionWithOptions(session.Options{}))
321//
322//     // Specify profile to load for the session's config
323//     sess := session.Must(session.NewSessionWithOptions(session.Options{
324//          Profile: "profile_name",
325//     }))
326//
327//     // Specify profile for config and region for requests
328//     sess := session.Must(session.NewSessionWithOptions(session.Options{
329//          Config: aws.Config{Region: aws.String("us-east-1")},
330//          Profile: "profile_name",
331//     }))
332//
333//     // Force enable Shared Config support
334//     sess := session.Must(session.NewSessionWithOptions(session.Options{
335//         SharedConfigState: session.SharedConfigEnable,
336//     }))
337func NewSessionWithOptions(opts Options) (*Session, error) {
338	var envCfg envConfig
339	var err error
340	if opts.SharedConfigState == SharedConfigEnable {
341		envCfg, err = loadSharedEnvConfig()
342		if err != nil {
343			return nil, fmt.Errorf("failed to load shared config, %v", err)
344		}
345	} else {
346		envCfg, err = loadEnvConfig()
347		if err != nil {
348			return nil, fmt.Errorf("failed to load environment config, %v", err)
349		}
350	}
351
352	if len(opts.Profile) != 0 {
353		envCfg.Profile = opts.Profile
354	}
355
356	switch opts.SharedConfigState {
357	case SharedConfigDisable:
358		envCfg.EnableSharedConfig = false
359	case SharedConfigEnable:
360		envCfg.EnableSharedConfig = true
361	}
362
363	return newSession(opts, envCfg, &opts.Config)
364}
365
366// Must is a helper function to ensure the Session is valid and there was no
367// error when calling a NewSession function.
368//
369// This helper is intended to be used in variable initialization to load the
370// Session and configuration at startup. Such as:
371//
372//     var sess = session.Must(session.NewSession())
373func Must(sess *Session, err error) *Session {
374	if err != nil {
375		panic(err)
376	}
377
378	return sess
379}
380
381// Wraps the endpoint resolver with a resolver that will return a custom
382// endpoint for EC2 IMDS.
383func wrapEC2IMDSEndpoint(resolver endpoints.Resolver, endpoint string, mode endpoints.EC2IMDSEndpointModeState) endpoints.Resolver {
384	return endpoints.ResolverFunc(
385		func(service, region string, opts ...func(*endpoints.Options)) (
386			endpoints.ResolvedEndpoint, error,
387		) {
388			if service == ec2MetadataServiceID && len(endpoint) > 0 {
389				return endpoints.ResolvedEndpoint{
390					URL:           endpoint,
391					SigningName:   ec2MetadataServiceID,
392					SigningRegion: region,
393				}, nil
394			} else if service == ec2MetadataServiceID {
395				opts = append(opts, func(o *endpoints.Options) {
396					o.EC2MetadataEndpointMode = mode
397				})
398			}
399			return resolver.EndpointFor(service, region, opts...)
400		})
401}
402
403func deprecatedNewSession(envCfg envConfig, cfgs ...*aws.Config) *Session {
404	cfg := defaults.Config()
405	handlers := defaults.Handlers()
406
407	// Apply the passed in configs so the configuration can be applied to the
408	// default credential chain
409	cfg.MergeIn(cfgs...)
410	if cfg.EndpointResolver == nil {
411		// An endpoint resolver is required for a session to be able to provide
412		// endpoints for service client configurations.
413		cfg.EndpointResolver = endpoints.DefaultResolver()
414	}
415
416	if !(len(envCfg.EC2IMDSEndpoint) == 0 && envCfg.EC2IMDSEndpointMode == endpoints.EC2IMDSEndpointModeStateUnset) {
417		cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, envCfg.EC2IMDSEndpoint, envCfg.EC2IMDSEndpointMode)
418	}
419
420	cfg.Credentials = defaults.CredChain(cfg, handlers)
421
422	// Reapply any passed in configs to override credentials if set
423	cfg.MergeIn(cfgs...)
424
425	s := &Session{
426		Config:   cfg,
427		Handlers: handlers,
428		options: Options{
429			EC2IMDSEndpoint: envCfg.EC2IMDSEndpoint,
430		},
431	}
432
433	initHandlers(s)
434	return s
435}
436
437func enableCSM(handlers *request.Handlers, cfg csmConfig, logger aws.Logger) error {
438	if logger != nil {
439		logger.Log("Enabling CSM")
440	}
441
442	r, err := csm.Start(cfg.ClientID, csm.AddressWithDefaults(cfg.Host, cfg.Port))
443	if err != nil {
444		return err
445	}
446	r.InjectHandlers(handlers)
447
448	return nil
449}
450
451func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
452	cfg := defaults.Config()
453
454	handlers := opts.Handlers
455	if handlers.IsEmpty() {
456		handlers = defaults.Handlers()
457	}
458
459	// Get a merged version of the user provided config to determine if
460	// credentials were.
461	userCfg := &aws.Config{}
462	userCfg.MergeIn(cfgs...)
463	cfg.MergeIn(userCfg)
464
465	// Ordered config files will be loaded in with later files overwriting
466	// previous config file values.
467	var cfgFiles []string
468	if opts.SharedConfigFiles != nil {
469		cfgFiles = opts.SharedConfigFiles
470	} else {
471		cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
472		if !envCfg.EnableSharedConfig {
473			// The shared config file (~/.aws/config) is only loaded if instructed
474			// to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
475			cfgFiles = cfgFiles[1:]
476		}
477	}
478
479	// Load additional config from file(s)
480	sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig)
481	if err != nil {
482		if len(envCfg.Profile) == 0 && !envCfg.EnableSharedConfig && (envCfg.Creds.HasKeys() || userCfg.Credentials != nil) {
483			// Special case where the user has not explicitly specified an AWS_PROFILE,
484			// or session.Options.profile, shared config is not enabled, and the
485			// environment has credentials, allow the shared config file to fail to
486			// load since the user has already provided credentials, and nothing else
487			// is required to be read file. Github(aws/aws-sdk-go#2455)
488		} else if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
489			return nil, err
490		}
491	}
492
493	if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil {
494		return nil, err
495	}
496
497	if err := setTLSOptions(&opts, cfg, envCfg, sharedCfg); err != nil {
498		return nil, err
499	}
500
501	s := &Session{
502		Config:   cfg,
503		Handlers: handlers,
504		options:  opts,
505	}
506
507	initHandlers(s)
508
509	if csmCfg, err := loadCSMConfig(envCfg, cfgFiles); err != nil {
510		if l := s.Config.Logger; l != nil {
511			l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err))
512		}
513	} else if csmCfg.Enabled {
514		err = enableCSM(&s.Handlers, csmCfg, s.Config.Logger)
515		if err != nil {
516			return nil, err
517		}
518	}
519
520	return s, nil
521}
522
523type csmConfig struct {
524	Enabled  bool
525	Host     string
526	Port     string
527	ClientID string
528}
529
530var csmProfileName = "aws_csm"
531
532func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) {
533	if envCfg.CSMEnabled != nil {
534		if *envCfg.CSMEnabled {
535			return csmConfig{
536				Enabled:  true,
537				ClientID: envCfg.CSMClientID,
538				Host:     envCfg.CSMHost,
539				Port:     envCfg.CSMPort,
540			}, nil
541		}
542		return csmConfig{}, nil
543	}
544
545	sharedCfg, err := loadSharedConfig(csmProfileName, cfgFiles, false)
546	if err != nil {
547		if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
548			return csmConfig{}, err
549		}
550	}
551	if sharedCfg.CSMEnabled != nil && *sharedCfg.CSMEnabled == true {
552		return csmConfig{
553			Enabled:  true,
554			ClientID: sharedCfg.CSMClientID,
555			Host:     sharedCfg.CSMHost,
556			Port:     sharedCfg.CSMPort,
557		}, nil
558	}
559
560	return csmConfig{}, nil
561}
562
563func setTLSOptions(opts *Options, cfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig) error {
564	// CA Bundle can be specified in both environment variable shared config file.
565	var caBundleFilename = envCfg.CustomCABundle
566	if len(caBundleFilename) == 0 {
567		caBundleFilename = sharedCfg.CustomCABundle
568	}
569
570	// Only use environment value if session option is not provided.
571	customTLSOptions := map[string]struct {
572		filename string
573		field    *io.Reader
574		errCode  string
575	}{
576		"custom CA bundle PEM":   {filename: caBundleFilename, field: &opts.CustomCABundle, errCode: ErrCodeLoadCustomCABundle},
577		"custom client TLS cert": {filename: envCfg.ClientTLSCert, field: &opts.ClientTLSCert, errCode: ErrCodeLoadClientTLSCert},
578		"custom client TLS key":  {filename: envCfg.ClientTLSKey, field: &opts.ClientTLSKey, errCode: ErrCodeLoadClientTLSCert},
579	}
580	for name, v := range customTLSOptions {
581		if len(v.filename) != 0 && *v.field == nil {
582			f, err := os.Open(v.filename)
583			if err != nil {
584				return awserr.New(v.errCode, fmt.Sprintf("failed to open %s file", name), err)
585			}
586			defer f.Close()
587			*v.field = f
588		}
589	}
590
591	// Setup HTTP client with custom cert bundle if enabled
592	if opts.CustomCABundle != nil {
593		if err := loadCustomCABundle(cfg.HTTPClient, opts.CustomCABundle); err != nil {
594			return err
595		}
596	}
597
598	// Setup HTTP client TLS certificate and key for client TLS authentication.
599	if opts.ClientTLSCert != nil && opts.ClientTLSKey != nil {
600		if err := loadClientTLSCert(cfg.HTTPClient, opts.ClientTLSCert, opts.ClientTLSKey); err != nil {
601			return err
602		}
603	} else if opts.ClientTLSCert == nil && opts.ClientTLSKey == nil {
604		// Do nothing if neither values are available.
605
606	} else {
607		return awserr.New(ErrCodeLoadClientTLSCert,
608			fmt.Sprintf("client TLS cert(%t) and key(%t) must both be provided",
609				opts.ClientTLSCert != nil, opts.ClientTLSKey != nil), nil)
610	}
611
612	return nil
613}
614
615func getHTTPTransport(client *http.Client) (*http.Transport, error) {
616	var t *http.Transport
617	switch v := client.Transport.(type) {
618	case *http.Transport:
619		t = v
620	default:
621		if client.Transport != nil {
622			return nil, fmt.Errorf("unsupported transport, %T", client.Transport)
623		}
624	}
625	if t == nil {
626		// Nil transport implies `http.DefaultTransport` should be used. Since
627		// the SDK cannot modify, nor copy the `DefaultTransport` specifying
628		// the values the next closest behavior.
629		t = getCustomTransport()
630	}
631
632	return t, nil
633}
634
635func loadCustomCABundle(client *http.Client, bundle io.Reader) error {
636	t, err := getHTTPTransport(client)
637	if err != nil {
638		return awserr.New(ErrCodeLoadCustomCABundle,
639			"unable to load custom CA bundle, HTTPClient's transport unsupported type", err)
640	}
641
642	p, err := loadCertPool(bundle)
643	if err != nil {
644		return err
645	}
646	if t.TLSClientConfig == nil {
647		t.TLSClientConfig = &tls.Config{}
648	}
649	t.TLSClientConfig.RootCAs = p
650
651	client.Transport = t
652
653	return nil
654}
655
656func loadCertPool(r io.Reader) (*x509.CertPool, error) {
657	b, err := ioutil.ReadAll(r)
658	if err != nil {
659		return nil, awserr.New(ErrCodeLoadCustomCABundle,
660			"failed to read custom CA bundle PEM file", err)
661	}
662
663	p := x509.NewCertPool()
664	if !p.AppendCertsFromPEM(b) {
665		return nil, awserr.New(ErrCodeLoadCustomCABundle,
666			"failed to load custom CA bundle PEM file", err)
667	}
668
669	return p, nil
670}
671
672func loadClientTLSCert(client *http.Client, certFile, keyFile io.Reader) error {
673	t, err := getHTTPTransport(client)
674	if err != nil {
675		return awserr.New(ErrCodeLoadClientTLSCert,
676			"unable to get usable HTTP transport from client", err)
677	}
678
679	cert, err := ioutil.ReadAll(certFile)
680	if err != nil {
681		return awserr.New(ErrCodeLoadClientTLSCert,
682			"unable to get read client TLS cert file", err)
683	}
684
685	key, err := ioutil.ReadAll(keyFile)
686	if err != nil {
687		return awserr.New(ErrCodeLoadClientTLSCert,
688			"unable to get read client TLS key file", err)
689	}
690
691	clientCert, err := tls.X509KeyPair(cert, key)
692	if err != nil {
693		return awserr.New(ErrCodeLoadClientTLSCert,
694			"unable to load x509 key pair from client cert", err)
695	}
696
697	tlsCfg := t.TLSClientConfig
698	if tlsCfg == nil {
699		tlsCfg = &tls.Config{}
700	}
701
702	tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
703
704	t.TLSClientConfig = tlsCfg
705	client.Transport = t
706
707	return nil
708}
709
710func mergeConfigSrcs(cfg, userCfg *aws.Config,
711	envCfg envConfig, sharedCfg sharedConfig,
712	handlers request.Handlers,
713	sessOpts Options,
714) error {
715
716	// Region if not already set by user
717	if len(aws.StringValue(cfg.Region)) == 0 {
718		if len(envCfg.Region) > 0 {
719			cfg.WithRegion(envCfg.Region)
720		} else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
721			cfg.WithRegion(sharedCfg.Region)
722		}
723	}
724
725	if cfg.EnableEndpointDiscovery == nil {
726		if envCfg.EnableEndpointDiscovery != nil {
727			cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery)
728		} else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil {
729			cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery)
730		}
731	}
732
733	// Regional Endpoint flag for STS endpoint resolving
734	mergeSTSRegionalEndpointConfig(cfg, []endpoints.STSRegionalEndpoint{
735		userCfg.STSRegionalEndpoint,
736		envCfg.STSRegionalEndpoint,
737		sharedCfg.STSRegionalEndpoint,
738		endpoints.LegacySTSEndpoint,
739	})
740
741	// Regional Endpoint flag for S3 endpoint resolving
742	mergeS3UsEast1RegionalEndpointConfig(cfg, []endpoints.S3UsEast1RegionalEndpoint{
743		userCfg.S3UsEast1RegionalEndpoint,
744		envCfg.S3UsEast1RegionalEndpoint,
745		sharedCfg.S3UsEast1RegionalEndpoint,
746		endpoints.LegacyS3UsEast1Endpoint,
747	})
748
749	var ec2IMDSEndpoint string
750	for _, v := range []string{
751		sessOpts.EC2IMDSEndpoint,
752		envCfg.EC2IMDSEndpoint,
753		sharedCfg.EC2IMDSEndpoint,
754	} {
755		if len(v) != 0 {
756			ec2IMDSEndpoint = v
757			break
758		}
759	}
760
761	var endpointMode endpoints.EC2IMDSEndpointModeState
762	for _, v := range []endpoints.EC2IMDSEndpointModeState{
763		sessOpts.EC2IMDSEndpointMode,
764		envCfg.EC2IMDSEndpointMode,
765		sharedCfg.EC2IMDSEndpointMode,
766	} {
767		if v != endpoints.EC2IMDSEndpointModeStateUnset {
768			endpointMode = v
769			break
770		}
771	}
772
773	if len(ec2IMDSEndpoint) != 0 || endpointMode != endpoints.EC2IMDSEndpointModeStateUnset {
774		cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, ec2IMDSEndpoint, endpointMode)
775	}
776
777	// Configure credentials if not already set by the user when creating the
778	// Session.
779	if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
780		creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts)
781		if err != nil {
782			return err
783		}
784		cfg.Credentials = creds
785	}
786
787	cfg.S3UseARNRegion = userCfg.S3UseARNRegion
788	if cfg.S3UseARNRegion == nil {
789		cfg.S3UseARNRegion = &envCfg.S3UseARNRegion
790	}
791	if cfg.S3UseARNRegion == nil {
792		cfg.S3UseARNRegion = &sharedCfg.S3UseARNRegion
793	}
794
795	return nil
796}
797
798func mergeSTSRegionalEndpointConfig(cfg *aws.Config, values []endpoints.STSRegionalEndpoint) {
799	for _, v := range values {
800		if v != endpoints.UnsetSTSEndpoint {
801			cfg.STSRegionalEndpoint = v
802			break
803		}
804	}
805}
806
807func mergeS3UsEast1RegionalEndpointConfig(cfg *aws.Config, values []endpoints.S3UsEast1RegionalEndpoint) {
808	for _, v := range values {
809		if v != endpoints.UnsetS3UsEast1Endpoint {
810			cfg.S3UsEast1RegionalEndpoint = v
811			break
812		}
813	}
814}
815
816func initHandlers(s *Session) {
817	// Add the Validate parameter handler if it is not disabled.
818	s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
819	if !aws.BoolValue(s.Config.DisableParamValidation) {
820		s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
821	}
822}
823
824// Copy creates and returns a copy of the current Session, copying the config
825// and handlers. If any additional configs are provided they will be merged
826// on top of the Session's copied config.
827//
828//     // Create a copy of the current Session, configured for the us-west-2 region.
829//     sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
830func (s *Session) Copy(cfgs ...*aws.Config) *Session {
831	newSession := &Session{
832		Config:   s.Config.Copy(cfgs...),
833		Handlers: s.Handlers.Copy(),
834		options:  s.options,
835	}
836
837	initHandlers(newSession)
838
839	return newSession
840}
841
842// ClientConfig satisfies the client.ConfigProvider interface and is used to
843// configure the service client instances. Passing the Session to the service
844// client's constructor (New) will use this method to configure the client.
845func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Config {
846	s = s.Copy(cfgs...)
847
848	region := aws.StringValue(s.Config.Region)
849	resolved, err := s.resolveEndpoint(service, region, s.Config)
850	if err != nil {
851		s.Handlers.Validate.PushBack(func(r *request.Request) {
852			if len(r.ClientInfo.Endpoint) != 0 {
853				// Error occurred while resolving endpoint, but the request
854				// being invoked has had an endpoint specified after the client
855				// was created.
856				return
857			}
858			r.Error = err
859		})
860	}
861
862	return client.Config{
863		Config:             s.Config,
864		Handlers:           s.Handlers,
865		PartitionID:        resolved.PartitionID,
866		Endpoint:           resolved.URL,
867		SigningRegion:      resolved.SigningRegion,
868		SigningNameDerived: resolved.SigningNameDerived,
869		SigningName:        resolved.SigningName,
870	}
871}
872
873const ec2MetadataServiceID = "ec2metadata"
874
875func (s *Session) resolveEndpoint(service, region string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) {
876
877	if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 {
878		return endpoints.ResolvedEndpoint{
879			URL:           endpoints.AddScheme(ep, aws.BoolValue(cfg.DisableSSL)),
880			SigningRegion: region,
881		}, nil
882	}
883
884	resolved, err := cfg.EndpointResolver.EndpointFor(service, region,
885		func(opt *endpoints.Options) {
886			opt.DisableSSL = aws.BoolValue(cfg.DisableSSL)
887			opt.UseDualStack = aws.BoolValue(cfg.UseDualStack)
888			// Support for STSRegionalEndpoint where the STSRegionalEndpoint is
889			// provided in envConfig or sharedConfig with envConfig getting
890			// precedence.
891			opt.STSRegionalEndpoint = cfg.STSRegionalEndpoint
892
893			// Support for S3UsEast1RegionalEndpoint where the S3UsEast1RegionalEndpoint is
894			// provided in envConfig or sharedConfig with envConfig getting
895			// precedence.
896			opt.S3UsEast1RegionalEndpoint = cfg.S3UsEast1RegionalEndpoint
897
898			// Support the condition where the service is modeled but its
899			// endpoint metadata is not available.
900			opt.ResolveUnknownService = true
901		},
902	)
903	if err != nil {
904		return endpoints.ResolvedEndpoint{}, err
905	}
906
907	return resolved, nil
908}
909
910// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception
911// that the EndpointResolver will not be used to resolve the endpoint. The only
912// endpoint set must come from the aws.Config.Endpoint field.
913func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config {
914	s = s.Copy(cfgs...)
915
916	var resolved endpoints.ResolvedEndpoint
917	if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 {
918		resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL))
919		resolved.SigningRegion = aws.StringValue(s.Config.Region)
920	}
921
922	return client.Config{
923		Config:             s.Config,
924		Handlers:           s.Handlers,
925		Endpoint:           resolved.URL,
926		SigningRegion:      resolved.SigningRegion,
927		SigningNameDerived: resolved.SigningNameDerived,
928		SigningName:        resolved.SigningName,
929	}
930}
931
932// logDeprecatedNewSessionError function enables error handling for session
933func (s *Session) logDeprecatedNewSessionError(msg string, err error, cfgs []*aws.Config) {
934	// Session creation failed, need to report the error and prevent
935	// any requests from succeeding.
936	s.Config.MergeIn(cfgs...)
937	s.Config.Logger.Log("ERROR:", msg, "Error:", err)
938	s.Handlers.Validate.PushBack(func(r *request.Request) {
939		r.Error = err
940	})
941}
942