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