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