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
30// ErrSharedConfigSourceCollision will be returned if a section contains both
31// source_profile and credential_source
32var ErrSharedConfigSourceCollision = awserr.New(ErrCodeSharedConfig, "only source profile or credential source can be specified, not both", nil)
33
34// ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment
35// variables are empty and Environment was set as the credential source
36var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil)
37
38// ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided
39var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil)
40
41// A Session provides a central location to create service clients from and
42// store configurations and request handlers for those services.
43//
44// Sessions are safe to create service clients concurrently, but it is not safe
45// to mutate the Session concurrently.
46//
47// The Session satisfies the service client's client.ConfigProvider.
48type Session struct {
49	Config   *aws.Config
50	Handlers request.Handlers
51}
52
53// New creates a new instance of the handlers merging in the provided configs
54// on top of the SDK's default configurations. Once the Session is created it
55// can be mutated to modify the Config or Handlers. The Session is safe to be
56// read concurrently, but it should not be written to concurrently.
57//
58// If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New
59// method could now encounter an error when loading the configuration. When
60// The environment variable is set, and an error occurs, New will return a
61// session that will fail all requests reporting the error that occurred while
62// loading the session. Use NewSession to get the error when creating the
63// session.
64//
65// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
66// the shared config file (~/.aws/config) will also be loaded, in addition to
67// the shared credentials file (~/.aws/credentials). Values set in both the
68// shared config, and shared credentials will be taken from the shared
69// credentials file.
70//
71// Deprecated: Use NewSession functions to create sessions instead. NewSession
72// has the same functionality as New except an error can be returned when the
73// func is called instead of waiting to receive an error until a request is made.
74func New(cfgs ...*aws.Config) *Session {
75	// load initial config from environment
76	envCfg := loadEnvConfig()
77
78	if envCfg.EnableSharedConfig {
79		var cfg aws.Config
80		cfg.MergeIn(cfgs...)
81		s, err := NewSessionWithOptions(Options{
82			Config:            cfg,
83			SharedConfigState: SharedConfigEnable,
84		})
85		if err != nil {
86			// Old session.New expected all errors to be discovered when
87			// a request is made, and would report the errors then. This
88			// needs to be replicated if an error occurs while creating
89			// the session.
90			msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " +
91				"Use session.NewSession to handle errors occurring during session creation."
92
93			// Session creation failed, need to report the error and prevent
94			// any requests from succeeding.
95			s = &Session{Config: defaults.Config()}
96			s.Config.MergeIn(cfgs...)
97			s.Config.Logger.Log("ERROR:", msg, "Error:", err)
98			s.Handlers.Validate.PushBack(func(r *request.Request) {
99				r.Error = err
100			})
101		}
102
103		return s
104	}
105
106	s := deprecatedNewSession(cfgs...)
107
108	if csmCfg, err := loadCSMConfig(envCfg, []string{}); err != nil {
109		if l := s.Config.Logger; l != nil {
110			l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err))
111		}
112	} else if csmCfg.Enabled {
113		err := enableCSM(&s.Handlers, csmCfg, s.Config.Logger)
114		if err != nil {
115			err = fmt.Errorf("failed to enable CSM, %v", err)
116			s.Config.Logger.Log("ERROR:", err.Error())
117			s.Handlers.Validate.PushBack(func(r *request.Request) {
118				r.Error = err
119			})
120		}
121	}
122
123	return s
124}
125
126// NewSession returns a new Session created from SDK defaults, config files,
127// environment, and user provided config files. Once the Session is created
128// it can be mutated to modify the Config or Handlers. The Session is safe to
129// be read concurrently, but it should not be written to concurrently.
130//
131// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
132// the shared config file (~/.aws/config) will also be loaded in addition to
133// the shared credentials file (~/.aws/credentials). Values set in both the
134// shared config, and shared credentials will be taken from the shared
135// credentials file. Enabling the Shared Config will also allow the Session
136// to be built with retrieving credentials with AssumeRole set in the config.
137//
138// See the NewSessionWithOptions func for information on how to override or
139// control through code how the Session will be created. Such as specifying the
140// config profile, and controlling if shared config is enabled or not.
141func NewSession(cfgs ...*aws.Config) (*Session, error) {
142	opts := Options{}
143	opts.Config.MergeIn(cfgs...)
144
145	return NewSessionWithOptions(opts)
146}
147
148// SharedConfigState provides the ability to optionally override the state
149// of the session's creation based on the shared config being enabled or
150// disabled.
151type SharedConfigState int
152
153const (
154	// SharedConfigStateFromEnv does not override any state of the
155	// AWS_SDK_LOAD_CONFIG env var. It is the default value of the
156	// SharedConfigState type.
157	SharedConfigStateFromEnv SharedConfigState = iota
158
159	// SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value
160	// and disables the shared config functionality.
161	SharedConfigDisable
162
163	// SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value
164	// and enables the shared config functionality.
165	SharedConfigEnable
166)
167
168// Options provides the means to control how a Session is created and what
169// configuration values will be loaded.
170//
171type Options struct {
172	// Provides config values for the SDK to use when creating service clients
173	// and making API requests to services. Any value set in with this field
174	// will override the associated value provided by the SDK defaults,
175	// environment or config files where relevant.
176	//
177	// If not set, configuration values from from SDK defaults, environment,
178	// config will be used.
179	Config aws.Config
180
181	// Overrides the config profile the Session should be created from. If not
182	// set the value of the environment variable will be loaded (AWS_PROFILE,
183	// or AWS_DEFAULT_PROFILE if the Shared Config is enabled).
184	//
185	// If not set and environment variables are not set the "default"
186	// (DefaultSharedConfigProfile) will be used as the profile to load the
187	// session config from.
188	Profile string
189
190	// Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG
191	// environment variable. By default a Session will be created using the
192	// value provided by the AWS_SDK_LOAD_CONFIG environment variable.
193	//
194	// Setting this value to SharedConfigEnable or SharedConfigDisable
195	// will allow you to override the AWS_SDK_LOAD_CONFIG environment variable
196	// and enable or disable the shared config functionality.
197	SharedConfigState SharedConfigState
198
199	// Ordered list of files the session will load configuration from.
200	// It will override environment variable AWS_SHARED_CREDENTIALS_FILE, AWS_CONFIG_FILE.
201	SharedConfigFiles []string
202
203	// When the SDK's shared config is configured to assume a role with MFA
204	// this option is required in order to provide the mechanism that will
205	// retrieve the MFA token. There is no default value for this field. If
206	// it is not set an error will be returned when creating the session.
207	//
208	// This token provider will be called when ever the assumed role's
209	// credentials need to be refreshed. Within the context of service clients
210	// all sharing the same session the SDK will ensure calls to the token
211	// provider are atomic. When sharing a token provider across multiple
212	// sessions additional synchronization logic is needed to ensure the
213	// token providers do not introduce race conditions. It is recommend to
214	// share the session where possible.
215	//
216	// stscreds.StdinTokenProvider is a basic implementation that will prompt
217	// from stdin for the MFA token code.
218	//
219	// This field is only used if the shared configuration is enabled, and
220	// the config enables assume role wit MFA via the mfa_serial field.
221	AssumeRoleTokenProvider func() (string, error)
222
223	// When the SDK's shared config is configured to assume a role this option
224	// may be provided to set the expiry duration of the STS credentials.
225	// Defaults to 15 minutes if not set as documented in the
226	// stscreds.AssumeRoleProvider.
227	AssumeRoleDuration time.Duration
228
229	// Reader for a custom Credentials Authority (CA) bundle in PEM format that
230	// the SDK will use instead of the default system's root CA bundle. Use this
231	// only if you want to replace the CA bundle the SDK uses for TLS requests.
232	//
233	// Enabling this option will attempt to merge the Transport into the SDK's HTTP
234	// client. If the client's Transport is not a http.Transport an error will be
235	// returned. If the Transport's TLS config is set this option will cause the SDK
236	// to overwrite the Transport's TLS config's  RootCAs value. If the CA
237	// bundle reader contains multiple certificates all of them will be loaded.
238	//
239	// The Session option CustomCABundle is also available when creating sessions
240	// to also enable this feature. CustomCABundle session option field has priority
241	// over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
242	CustomCABundle io.Reader
243
244	// The handlers that the session and all API clients will be created with.
245	// This must be a complete set of handlers. Use the defaults.Handlers()
246	// function to initialize this value before changing the handlers to be
247	// used by the SDK.
248	Handlers request.Handlers
249}
250
251// NewSessionWithOptions returns a new Session created from SDK defaults, config files,
252// environment, and user provided config files. This func uses the Options
253// values to configure how the Session is created.
254//
255// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
256// the shared config file (~/.aws/config) will also be loaded in addition to
257// the shared credentials file (~/.aws/credentials). Values set in both the
258// shared config, and shared credentials will be taken from the shared
259// credentials file. Enabling the Shared Config will also allow the Session
260// to be built with retrieving credentials with AssumeRole set in the config.
261//
262//     // Equivalent to session.New
263//     sess := session.Must(session.NewSessionWithOptions(session.Options{}))
264//
265//     // Specify profile to load for the session's config
266//     sess := session.Must(session.NewSessionWithOptions(session.Options{
267//          Profile: "profile_name",
268//     }))
269//
270//     // Specify profile for config and region for requests
271//     sess := session.Must(session.NewSessionWithOptions(session.Options{
272//          Config: aws.Config{Region: aws.String("us-east-1")},
273//          Profile: "profile_name",
274//     }))
275//
276//     // Force enable Shared Config support
277//     sess := session.Must(session.NewSessionWithOptions(session.Options{
278//         SharedConfigState: session.SharedConfigEnable,
279//     }))
280func NewSessionWithOptions(opts Options) (*Session, error) {
281	var envCfg envConfig
282	if opts.SharedConfigState == SharedConfigEnable {
283		envCfg = loadSharedEnvConfig()
284	} else {
285		envCfg = loadEnvConfig()
286	}
287
288	if len(opts.Profile) != 0 {
289		envCfg.Profile = opts.Profile
290	}
291
292	switch opts.SharedConfigState {
293	case SharedConfigDisable:
294		envCfg.EnableSharedConfig = false
295	case SharedConfigEnable:
296		envCfg.EnableSharedConfig = true
297	}
298
299	// Only use AWS_CA_BUNDLE if session option is not provided.
300	if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil {
301		f, err := os.Open(envCfg.CustomCABundle)
302		if err != nil {
303			return nil, awserr.New("LoadCustomCABundleError",
304				"failed to open custom CA bundle PEM file", err)
305		}
306		defer f.Close()
307		opts.CustomCABundle = f
308	}
309
310	return newSession(opts, envCfg, &opts.Config)
311}
312
313// Must is a helper function to ensure the Session is valid and there was no
314// error when calling a NewSession function.
315//
316// This helper is intended to be used in variable initialization to load the
317// Session and configuration at startup. Such as:
318//
319//     var sess = session.Must(session.NewSession())
320func Must(sess *Session, err error) *Session {
321	if err != nil {
322		panic(err)
323	}
324
325	return sess
326}
327
328func deprecatedNewSession(cfgs ...*aws.Config) *Session {
329	cfg := defaults.Config()
330	handlers := defaults.Handlers()
331
332	// Apply the passed in configs so the configuration can be applied to the
333	// default credential chain
334	cfg.MergeIn(cfgs...)
335	if cfg.EndpointResolver == nil {
336		// An endpoint resolver is required for a session to be able to provide
337		// endpoints for service client configurations.
338		cfg.EndpointResolver = endpoints.DefaultResolver()
339	}
340	cfg.Credentials = defaults.CredChain(cfg, handlers)
341
342	// Reapply any passed in configs to override credentials if set
343	cfg.MergeIn(cfgs...)
344
345	s := &Session{
346		Config:   cfg,
347		Handlers: handlers,
348	}
349
350	initHandlers(s)
351	return s
352}
353
354func enableCSM(handlers *request.Handlers, cfg csmConfig, logger aws.Logger) error {
355	if logger != nil {
356		logger.Log("Enabling CSM")
357	}
358
359	r, err := csm.Start(cfg.ClientID, csm.AddressWithDefaults(cfg.Host, cfg.Port))
360	if err != nil {
361		return err
362	}
363	r.InjectHandlers(handlers)
364
365	return nil
366}
367
368func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
369	cfg := defaults.Config()
370
371	handlers := opts.Handlers
372	if handlers.IsEmpty() {
373		handlers = defaults.Handlers()
374	}
375
376	// Get a merged version of the user provided config to determine if
377	// credentials were.
378	userCfg := &aws.Config{}
379	userCfg.MergeIn(cfgs...)
380	cfg.MergeIn(userCfg)
381
382	// Ordered config files will be loaded in with later files overwriting
383	// previous config file values.
384	var cfgFiles []string
385	if opts.SharedConfigFiles != nil {
386		cfgFiles = opts.SharedConfigFiles
387	} else {
388		cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
389		if !envCfg.EnableSharedConfig {
390			// The shared config file (~/.aws/config) is only loaded if instructed
391			// to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
392			cfgFiles = cfgFiles[1:]
393		}
394	}
395
396	// Load additional config from file(s)
397	sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig)
398	if err != nil {
399		if len(envCfg.Profile) == 0 && !envCfg.EnableSharedConfig && (envCfg.Creds.HasKeys() || userCfg.Credentials != nil) {
400			// Special case where the user has not explicitly specified an AWS_PROFILE,
401			// or session.Options.profile, shared config is not enabled, and the
402			// environment has credentials, allow the shared config file to fail to
403			// load since the user has already provided credentials, and nothing else
404			// is required to be read file. Github(aws/aws-sdk-go#2455)
405		} else if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
406			return nil, err
407		}
408	}
409
410	if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil {
411		return nil, err
412	}
413
414	s := &Session{
415		Config:   cfg,
416		Handlers: handlers,
417	}
418
419	initHandlers(s)
420
421	if csmCfg, err := loadCSMConfig(envCfg, cfgFiles); err != nil {
422		if l := s.Config.Logger; l != nil {
423			l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err))
424		}
425	} else if csmCfg.Enabled {
426		err = enableCSM(&s.Handlers, csmCfg, s.Config.Logger)
427		if err != nil {
428			return nil, err
429		}
430	}
431
432	// Setup HTTP client with custom cert bundle if enabled
433	if opts.CustomCABundle != nil {
434		if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil {
435			return nil, err
436		}
437	}
438
439	return s, nil
440}
441
442type csmConfig struct {
443	Enabled  bool
444	Host     string
445	Port     string
446	ClientID string
447}
448
449var csmProfileName = "aws_csm"
450
451func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) {
452	if envCfg.CSMEnabled != nil {
453		if *envCfg.CSMEnabled {
454			return csmConfig{
455				Enabled:  true,
456				ClientID: envCfg.CSMClientID,
457				Host:     envCfg.CSMHost,
458				Port:     envCfg.CSMPort,
459			}, nil
460		}
461		return csmConfig{}, nil
462	}
463
464	sharedCfg, err := loadSharedConfig(csmProfileName, cfgFiles, false)
465	if err != nil {
466		if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
467			return csmConfig{}, err
468		}
469	}
470	if sharedCfg.CSMEnabled != nil && *sharedCfg.CSMEnabled == true {
471		return csmConfig{
472			Enabled:  true,
473			ClientID: sharedCfg.CSMClientID,
474			Host:     sharedCfg.CSMHost,
475			Port:     sharedCfg.CSMPort,
476		}, nil
477	}
478
479	return csmConfig{}, nil
480}
481
482func loadCustomCABundle(s *Session, bundle io.Reader) error {
483	var t *http.Transport
484	switch v := s.Config.HTTPClient.Transport.(type) {
485	case *http.Transport:
486		t = v
487	default:
488		if s.Config.HTTPClient.Transport != nil {
489			return awserr.New("LoadCustomCABundleError",
490				"unable to load custom CA bundle, HTTPClient's transport unsupported type", nil)
491		}
492	}
493	if t == nil {
494		// Nil transport implies `http.DefaultTransport` should be used. Since
495		// the SDK cannot modify, nor copy the `DefaultTransport` specifying
496		// the values the next closest behavior.
497		t = getCABundleTransport()
498	}
499
500	p, err := loadCertPool(bundle)
501	if err != nil {
502		return err
503	}
504	if t.TLSClientConfig == nil {
505		t.TLSClientConfig = &tls.Config{}
506	}
507	t.TLSClientConfig.RootCAs = p
508
509	s.Config.HTTPClient.Transport = t
510
511	return nil
512}
513
514func loadCertPool(r io.Reader) (*x509.CertPool, error) {
515	b, err := ioutil.ReadAll(r)
516	if err != nil {
517		return nil, awserr.New("LoadCustomCABundleError",
518			"failed to read custom CA bundle PEM file", err)
519	}
520
521	p := x509.NewCertPool()
522	if !p.AppendCertsFromPEM(b) {
523		return nil, awserr.New("LoadCustomCABundleError",
524			"failed to load custom CA bundle PEM file", err)
525	}
526
527	return p, nil
528}
529
530func mergeConfigSrcs(cfg, userCfg *aws.Config,
531	envCfg envConfig, sharedCfg sharedConfig,
532	handlers request.Handlers,
533	sessOpts Options,
534) error {
535
536	// Region if not already set by user
537	if len(aws.StringValue(cfg.Region)) == 0 {
538		if len(envCfg.Region) > 0 {
539			cfg.WithRegion(envCfg.Region)
540		} else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
541			cfg.WithRegion(sharedCfg.Region)
542		}
543	}
544
545	if cfg.EnableEndpointDiscovery == nil {
546		if envCfg.EnableEndpointDiscovery != nil {
547			cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery)
548		} else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil {
549			cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery)
550		}
551	}
552
553	// Configure credentials if not already set by the user when creating the
554	// Session.
555	if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
556		creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts)
557		if err != nil {
558			return err
559		}
560		cfg.Credentials = creds
561	}
562
563	return nil
564}
565
566func initHandlers(s *Session) {
567	// Add the Validate parameter handler if it is not disabled.
568	s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
569	if !aws.BoolValue(s.Config.DisableParamValidation) {
570		s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
571	}
572}
573
574// Copy creates and returns a copy of the current Session, coping the config
575// and handlers. If any additional configs are provided they will be merged
576// on top of the Session's copied config.
577//
578//     // Create a copy of the current Session, configured for the us-west-2 region.
579//     sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
580func (s *Session) Copy(cfgs ...*aws.Config) *Session {
581	newSession := &Session{
582		Config:   s.Config.Copy(cfgs...),
583		Handlers: s.Handlers.Copy(),
584	}
585
586	initHandlers(newSession)
587
588	return newSession
589}
590
591// ClientConfig satisfies the client.ConfigProvider interface and is used to
592// configure the service client instances. Passing the Session to the service
593// client's constructor (New) will use this method to configure the client.
594func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
595	// Backwards compatibility, the error will be eaten if user calls ClientConfig
596	// directly. All SDK services will use ClientconfigWithError.
597	cfg, _ := s.clientConfigWithErr(serviceName, cfgs...)
598
599	return cfg
600}
601
602func (s *Session) clientConfigWithErr(serviceName string, cfgs ...*aws.Config) (client.Config, error) {
603	s = s.Copy(cfgs...)
604
605	var resolved endpoints.ResolvedEndpoint
606	var err error
607
608	region := aws.StringValue(s.Config.Region)
609
610	if endpoint := aws.StringValue(s.Config.Endpoint); len(endpoint) != 0 {
611		resolved.URL = endpoints.AddScheme(endpoint, aws.BoolValue(s.Config.DisableSSL))
612		resolved.SigningRegion = region
613	} else {
614		resolved, err = s.Config.EndpointResolver.EndpointFor(
615			serviceName, region,
616			func(opt *endpoints.Options) {
617				opt.DisableSSL = aws.BoolValue(s.Config.DisableSSL)
618				opt.UseDualStack = aws.BoolValue(s.Config.UseDualStack)
619
620				// Support the condition where the service is modeled but its
621				// endpoint metadata is not available.
622				opt.ResolveUnknownService = true
623			},
624		)
625	}
626
627	return client.Config{
628		Config:             s.Config,
629		Handlers:           s.Handlers,
630		Endpoint:           resolved.URL,
631		SigningRegion:      resolved.SigningRegion,
632		SigningNameDerived: resolved.SigningNameDerived,
633		SigningName:        resolved.SigningName,
634	}, err
635}
636
637// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception
638// that the EndpointResolver will not be used to resolve the endpoint. The only
639// endpoint set must come from the aws.Config.Endpoint field.
640func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config {
641	s = s.Copy(cfgs...)
642
643	var resolved endpoints.ResolvedEndpoint
644
645	region := aws.StringValue(s.Config.Region)
646
647	if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 {
648		resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL))
649		resolved.SigningRegion = region
650	}
651
652	return client.Config{
653		Config:             s.Config,
654		Handlers:           s.Handlers,
655		Endpoint:           resolved.URL,
656		SigningRegion:      resolved.SigningRegion,
657		SigningNameDerived: resolved.SigningNameDerived,
658		SigningName:        resolved.SigningName,
659	}
660}
661