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