1package session 2 3import ( 4 "crypto/tls" 5 "crypto/x509" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "os" 11 "strings" 12 "time" 13 14 "github.com/aws/aws-sdk-go/aws" 15 "github.com/aws/aws-sdk-go/aws/awserr" 16 "github.com/aws/aws-sdk-go/aws/client" 17 "github.com/aws/aws-sdk-go/aws/corehandlers" 18 "github.com/aws/aws-sdk-go/aws/credentials" 19 "github.com/aws/aws-sdk-go/aws/csm" 20 "github.com/aws/aws-sdk-go/aws/defaults" 21 "github.com/aws/aws-sdk-go/aws/endpoints" 22 "github.com/aws/aws-sdk-go/aws/request" 23) 24 25const ( 26 // ErrCodeSharedConfig represents an error that occurs in the shared 27 // configuration logic 28 ErrCodeSharedConfig = "SharedConfigErr" 29 30 // ErrCodeLoadCustomCABundle error code for unable to load custom CA bundle. 31 ErrCodeLoadCustomCABundle = "LoadCustomCABundleError" 32 33 // ErrCodeLoadClientTLSCert error code for unable to load client TLS 34 // certificate or key 35 ErrCodeLoadClientTLSCert = "LoadClientTLSCertError" 36) 37 38// ErrSharedConfigSourceCollision will be returned if a section contains both 39// source_profile and credential_source 40var 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) 41 42// ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment 43// variables are empty and Environment was set as the credential source 44var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil) 45 46// ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided 47var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil) 48 49// A Session provides a central location to create service clients from and 50// store configurations and request handlers for those services. 51// 52// Sessions are safe to create service clients concurrently, but it is not safe 53// to mutate the Session concurrently. 54// 55// The Session satisfies the service client's client.ConfigProvider. 56type Session struct { 57 Config *aws.Config 58 Handlers request.Handlers 59 60 options Options 61} 62 63// New creates a new instance of the handlers merging in the provided configs 64// on top of the SDK's default configurations. Once the Session is created it 65// can be mutated to modify the Config or Handlers. The Session is safe to be 66// read concurrently, but it should not be written to concurrently. 67// 68// If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New 69// method could now encounter an error when loading the configuration. When 70// The environment variable is set, and an error occurs, New will return a 71// session that will fail all requests reporting the error that occurred while 72// loading the session. Use NewSession to get the error when creating the 73// session. 74// 75// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value 76// the shared config file (~/.aws/config) will also be loaded, in addition to 77// the shared credentials file (~/.aws/credentials). Values set in both the 78// shared config, and shared credentials will be taken from the shared 79// credentials file. 80// 81// Deprecated: Use NewSession functions to create sessions instead. NewSession 82// has the same functionality as New except an error can be returned when the 83// func is called instead of waiting to receive an error until a request is made. 84func New(cfgs ...*aws.Config) *Session { 85 // load initial config from environment 86 envCfg, envErr := loadEnvConfig() 87 88 if envCfg.EnableSharedConfig { 89 var cfg aws.Config 90 cfg.MergeIn(cfgs...) 91 s, err := NewSessionWithOptions(Options{ 92 Config: cfg, 93 SharedConfigState: SharedConfigEnable, 94 }) 95 if err != nil { 96 // Old session.New expected all errors to be discovered when 97 // a request is made, and would report the errors then. This 98 // needs to be replicated if an error occurs while creating 99 // the session. 100 msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " + 101 "Use session.NewSession to handle errors occurring during session creation." 102 103 // Session creation failed, need to report the error and prevent 104 // any requests from succeeding. 105 s = &Session{Config: defaults.Config()} 106 s.logDeprecatedNewSessionError(msg, err, cfgs) 107 } 108 109 return s 110 } 111 112 s := deprecatedNewSession(envCfg, cfgs...) 113 if envErr != nil { 114 msg := "failed to load env config" 115 s.logDeprecatedNewSessionError(msg, envErr, cfgs) 116 } 117 118 if csmCfg, err := loadCSMConfig(envCfg, []string{}); err != nil { 119 if l := s.Config.Logger; l != nil { 120 l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err)) 121 } 122 } else if csmCfg.Enabled { 123 err := enableCSM(&s.Handlers, csmCfg, s.Config.Logger) 124 if err != nil { 125 msg := "failed to enable CSM" 126 s.logDeprecatedNewSessionError(msg, err, cfgs) 127 } 128 } 129 130 return s 131} 132 133// NewSession returns a new Session created from SDK defaults, config files, 134// environment, and user provided config files. Once the Session is created 135// it can be mutated to modify the Config or Handlers. The Session is safe to 136// be read concurrently, but it should not be written to concurrently. 137// 138// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value 139// the shared config file (~/.aws/config) will also be loaded in addition to 140// the shared credentials file (~/.aws/credentials). Values set in both the 141// shared config, and shared credentials will be taken from the shared 142// credentials file. Enabling the Shared Config will also allow the Session 143// to be built with retrieving credentials with AssumeRole set in the config. 144// 145// See the NewSessionWithOptions func for information on how to override or 146// control through code how the Session will be created, such as specifying the 147// config profile, and controlling if shared config is enabled or not. 148func NewSession(cfgs ...*aws.Config) (*Session, error) { 149 opts := Options{} 150 opts.Config.MergeIn(cfgs...) 151 152 return NewSessionWithOptions(opts) 153} 154 155// SharedConfigState provides the ability to optionally override the state 156// of the session's creation based on the shared config being enabled or 157// disabled. 158type SharedConfigState int 159 160const ( 161 // SharedConfigStateFromEnv does not override any state of the 162 // AWS_SDK_LOAD_CONFIG env var. It is the default value of the 163 // SharedConfigState type. 164 SharedConfigStateFromEnv SharedConfigState = iota 165 166 // SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value 167 // and disables the shared config functionality. 168 SharedConfigDisable 169 170 // SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value 171 // and enables the shared config functionality. 172 SharedConfigEnable 173) 174 175// Options provides the means to control how a Session is created and what 176// configuration values will be loaded. 177// 178type Options struct { 179 // Provides config values for the SDK to use when creating service clients 180 // and making API requests to services. Any value set in with this field 181 // will override the associated value provided by the SDK defaults, 182 // environment or config files where relevant. 183 // 184 // If not set, configuration values from from SDK defaults, environment, 185 // config will be used. 186 Config aws.Config 187 188 // Overrides the config profile the Session should be created from. If not 189 // set the value of the environment variable will be loaded (AWS_PROFILE, 190 // or AWS_DEFAULT_PROFILE if the Shared Config is enabled). 191 // 192 // If not set and environment variables are not set the "default" 193 // (DefaultSharedConfigProfile) will be used as the profile to load the 194 // session config from. 195 Profile string 196 197 // Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG 198 // environment variable. By default a Session will be created using the 199 // value provided by the AWS_SDK_LOAD_CONFIG environment variable. 200 // 201 // Setting this value to SharedConfigEnable or SharedConfigDisable 202 // will allow you to override the AWS_SDK_LOAD_CONFIG environment variable 203 // and enable or disable the shared config functionality. 204 SharedConfigState SharedConfigState 205 206 // Ordered list of files the session will load configuration from. 207 // It will override environment variable AWS_SHARED_CREDENTIALS_FILE, AWS_CONFIG_FILE. 208 SharedConfigFiles []string 209 210 // When the SDK's shared config is configured to assume a role with MFA 211 // this option is required in order to provide the mechanism that will 212 // retrieve the MFA token. There is no default value for this field. If 213 // it is not set an error will be returned when creating the session. 214 // 215 // This token provider will be called when ever the assumed role's 216 // credentials need to be refreshed. Within the context of service clients 217 // all sharing the same session the SDK will ensure calls to the token 218 // provider are atomic. When sharing a token provider across multiple 219 // sessions additional synchronization logic is needed to ensure the 220 // token providers do not introduce race conditions. It is recommend to 221 // share the session where possible. 222 // 223 // stscreds.StdinTokenProvider is a basic implementation that will prompt 224 // from stdin for the MFA token code. 225 // 226 // This field is only used if the shared configuration is enabled, and 227 // the config enables assume role wit MFA via the mfa_serial field. 228 AssumeRoleTokenProvider func() (string, error) 229 230 // When the SDK's shared config is configured to assume a role this option 231 // may be provided to set the expiry duration of the STS credentials. 232 // Defaults to 15 minutes if not set as documented in the 233 // stscreds.AssumeRoleProvider. 234 AssumeRoleDuration time.Duration 235 236 // Reader for a custom Credentials Authority (CA) bundle in PEM format that 237 // the SDK will use instead of the default system's root CA bundle. Use this 238 // only if you want to replace the CA bundle the SDK uses for TLS requests. 239 // 240 // HTTP Client's Transport concrete implementation must be a http.Transport 241 // or creating the session will fail. 242 // 243 // If the Transport's TLS config is set this option will cause the SDK 244 // to overwrite the Transport's TLS config's RootCAs value. If the CA 245 // bundle reader contains multiple certificates all of them will be loaded. 246 // 247 // Can also be specified via the environment variable: 248 // 249 // AWS_CA_BUNDLE=$HOME/ca_bundle 250 // 251 // Can also be specified via the shared config field: 252 // 253 // ca_bundle = $HOME/ca_bundle 254 CustomCABundle io.Reader 255 256 // Reader for the TLC client certificate that should be used by the SDK's 257 // HTTP transport when making requests. The certificate must be paired with 258 // a TLS client key file. Will be ignored if both are not provided. 259 // 260 // HTTP Client's Transport concrete implementation must be a http.Transport 261 // or creating the session will fail. 262 // 263 // Can also be specified via the environment variable: 264 // 265 // AWS_SDK_GO_CLIENT_TLS_CERT=$HOME/my_client_cert 266 ClientTLSCert io.Reader 267 268 // Reader for the TLC client key that should be used by the SDK's HTTP 269 // transport when making requests. The key must be paired with a TLS client 270 // certificate file. Will be ignored if both are not provided. 271 // 272 // HTTP Client's Transport concrete implementation must be a http.Transport 273 // or creating the session will fail. 274 // 275 // Can also be specified via the environment variable: 276 // 277 // AWS_SDK_GO_CLIENT_TLS_KEY=$HOME/my_client_key 278 ClientTLSKey io.Reader 279 280 // The handlers that the session and all API clients will be created with. 281 // This must be a complete set of handlers. Use the defaults.Handlers() 282 // function to initialize this value before changing the handlers to be 283 // used by the SDK. 284 Handlers request.Handlers 285 286 // Allows specifying a custom endpoint to be used by the EC2 IMDS client 287 // when making requests to the EC2 IMDS API. The endpoint value should 288 // include the URI scheme. If the scheme is not present it will be defaulted to http. 289 // 290 // If unset, will the EC2 IMDS client will use its default endpoint. 291 // 292 // Can also be specified via the environment variable, 293 // AWS_EC2_METADATA_SERVICE_ENDPOINT. 294 // 295 // AWS_EC2_METADATA_SERVICE_ENDPOINT=http://169.254.169.254 296 // 297 // If using an URL with an IPv6 address literal, the IPv6 address 298 // component must be enclosed in square brackets. 299 // 300 // AWS_EC2_METADATA_SERVICE_ENDPOINT=http://[::1] 301 EC2IMDSEndpoint string 302 303 // Specifies the EC2 Instance Metadata Service default endpoint selection mode (IPv4 or IPv6) 304 // 305 // AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE=IPv6 306 EC2IMDSEndpointMode endpoints.EC2IMDSEndpointModeState 307} 308 309// NewSessionWithOptions returns a new Session created from SDK defaults, config files, 310// environment, and user provided config files. This func uses the Options 311// values to configure how the Session is created. 312// 313// If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value 314// the shared config file (~/.aws/config) will also be loaded in addition to 315// the shared credentials file (~/.aws/credentials). Values set in both the 316// shared config, and shared credentials will be taken from the shared 317// credentials file. Enabling the Shared Config will also allow the Session 318// to be built with retrieving credentials with AssumeRole set in the config. 319// 320// // Equivalent to session.New 321// sess := session.Must(session.NewSessionWithOptions(session.Options{})) 322// 323// // Specify profile to load for the session's config 324// sess := session.Must(session.NewSessionWithOptions(session.Options{ 325// Profile: "profile_name", 326// })) 327// 328// // Specify profile for config and region for requests 329// sess := session.Must(session.NewSessionWithOptions(session.Options{ 330// Config: aws.Config{Region: aws.String("us-east-1")}, 331// Profile: "profile_name", 332// })) 333// 334// // Force enable Shared Config support 335// sess := session.Must(session.NewSessionWithOptions(session.Options{ 336// SharedConfigState: session.SharedConfigEnable, 337// })) 338func NewSessionWithOptions(opts Options) (*Session, error) { 339 var envCfg envConfig 340 var err error 341 if opts.SharedConfigState == SharedConfigEnable { 342 envCfg, err = loadSharedEnvConfig() 343 if err != nil { 344 return nil, fmt.Errorf("failed to load shared config, %v", err) 345 } 346 } else { 347 envCfg, err = loadEnvConfig() 348 if err != nil { 349 return nil, fmt.Errorf("failed to load environment config, %v", err) 350 } 351 } 352 353 if len(opts.Profile) != 0 { 354 envCfg.Profile = opts.Profile 355 } 356 357 switch opts.SharedConfigState { 358 case SharedConfigDisable: 359 envCfg.EnableSharedConfig = false 360 case SharedConfigEnable: 361 envCfg.EnableSharedConfig = true 362 } 363 364 return newSession(opts, envCfg, &opts.Config) 365} 366 367// Must is a helper function to ensure the Session is valid and there was no 368// error when calling a NewSession function. 369// 370// This helper is intended to be used in variable initialization to load the 371// Session and configuration at startup. Such as: 372// 373// var sess = session.Must(session.NewSession()) 374func Must(sess *Session, err error) *Session { 375 if err != nil { 376 panic(err) 377 } 378 379 return sess 380} 381 382// Wraps the endpoint resolver with a resolver that will return a custom 383// endpoint for EC2 IMDS. 384func wrapEC2IMDSEndpoint(resolver endpoints.Resolver, endpoint string, mode endpoints.EC2IMDSEndpointModeState) endpoints.Resolver { 385 return endpoints.ResolverFunc( 386 func(service, region string, opts ...func(*endpoints.Options)) ( 387 endpoints.ResolvedEndpoint, error, 388 ) { 389 if service == ec2MetadataServiceID && len(endpoint) > 0 { 390 return endpoints.ResolvedEndpoint{ 391 URL: endpoint, 392 SigningName: ec2MetadataServiceID, 393 SigningRegion: region, 394 }, nil 395 } else if service == ec2MetadataServiceID { 396 opts = append(opts, func(o *endpoints.Options) { 397 o.EC2MetadataEndpointMode = mode 398 }) 399 } 400 return resolver.EndpointFor(service, region, opts...) 401 }) 402} 403 404func deprecatedNewSession(envCfg envConfig, cfgs ...*aws.Config) *Session { 405 cfg := defaults.Config() 406 handlers := defaults.Handlers() 407 408 // Apply the passed in configs so the configuration can be applied to the 409 // default credential chain 410 cfg.MergeIn(cfgs...) 411 if cfg.EndpointResolver == nil { 412 // An endpoint resolver is required for a session to be able to provide 413 // endpoints for service client configurations. 414 cfg.EndpointResolver = endpoints.DefaultResolver() 415 } 416 417 if !(len(envCfg.EC2IMDSEndpoint) == 0 && envCfg.EC2IMDSEndpointMode == endpoints.EC2IMDSEndpointModeStateUnset) { 418 cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, envCfg.EC2IMDSEndpoint, envCfg.EC2IMDSEndpointMode) 419 } 420 421 cfg.Credentials = defaults.CredChain(cfg, handlers) 422 423 // Reapply any passed in configs to override credentials if set 424 cfg.MergeIn(cfgs...) 425 426 s := &Session{ 427 Config: cfg, 428 Handlers: handlers, 429 options: Options{ 430 EC2IMDSEndpoint: envCfg.EC2IMDSEndpoint, 431 }, 432 } 433 434 initHandlers(s) 435 return s 436} 437 438func enableCSM(handlers *request.Handlers, cfg csmConfig, logger aws.Logger) error { 439 if logger != nil { 440 logger.Log("Enabling CSM") 441 } 442 443 r, err := csm.Start(cfg.ClientID, csm.AddressWithDefaults(cfg.Host, cfg.Port)) 444 if err != nil { 445 return err 446 } 447 r.InjectHandlers(handlers) 448 449 return nil 450} 451 452func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) { 453 cfg := defaults.Config() 454 455 handlers := opts.Handlers 456 if handlers.IsEmpty() { 457 handlers = defaults.Handlers() 458 } 459 460 // Get a merged version of the user provided config to determine if 461 // credentials were. 462 userCfg := &aws.Config{} 463 userCfg.MergeIn(cfgs...) 464 cfg.MergeIn(userCfg) 465 466 // Ordered config files will be loaded in with later files overwriting 467 // previous config file values. 468 var cfgFiles []string 469 if opts.SharedConfigFiles != nil { 470 cfgFiles = opts.SharedConfigFiles 471 } else { 472 cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile} 473 if !envCfg.EnableSharedConfig { 474 // The shared config file (~/.aws/config) is only loaded if instructed 475 // to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG). 476 cfgFiles = cfgFiles[1:] 477 } 478 } 479 480 // Load additional config from file(s) 481 sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig) 482 if err != nil { 483 if len(envCfg.Profile) == 0 && !envCfg.EnableSharedConfig && (envCfg.Creds.HasKeys() || userCfg.Credentials != nil) { 484 // Special case where the user has not explicitly specified an AWS_PROFILE, 485 // or session.Options.profile, shared config is not enabled, and the 486 // environment has credentials, allow the shared config file to fail to 487 // load since the user has already provided credentials, and nothing else 488 // is required to be read file. Github(aws/aws-sdk-go#2455) 489 } else if _, ok := err.(SharedConfigProfileNotExistsError); !ok { 490 return nil, err 491 } 492 } 493 494 if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil { 495 return nil, err 496 } 497 498 if err := setTLSOptions(&opts, cfg, envCfg, sharedCfg); err != nil { 499 return nil, err 500 } 501 502 s := &Session{ 503 Config: cfg, 504 Handlers: handlers, 505 options: opts, 506 } 507 508 initHandlers(s) 509 510 if csmCfg, err := loadCSMConfig(envCfg, cfgFiles); err != nil { 511 if l := s.Config.Logger; l != nil { 512 l.Log(fmt.Sprintf("ERROR: failed to load CSM configuration, %v", err)) 513 } 514 } else if csmCfg.Enabled { 515 err = enableCSM(&s.Handlers, csmCfg, s.Config.Logger) 516 if err != nil { 517 return nil, err 518 } 519 } 520 521 return s, nil 522} 523 524type csmConfig struct { 525 Enabled bool 526 Host string 527 Port string 528 ClientID string 529} 530 531var csmProfileName = "aws_csm" 532 533func loadCSMConfig(envCfg envConfig, cfgFiles []string) (csmConfig, error) { 534 if envCfg.CSMEnabled != nil { 535 if *envCfg.CSMEnabled { 536 return csmConfig{ 537 Enabled: true, 538 ClientID: envCfg.CSMClientID, 539 Host: envCfg.CSMHost, 540 Port: envCfg.CSMPort, 541 }, nil 542 } 543 return csmConfig{}, nil 544 } 545 546 sharedCfg, err := loadSharedConfig(csmProfileName, cfgFiles, false) 547 if err != nil { 548 if _, ok := err.(SharedConfigProfileNotExistsError); !ok { 549 return csmConfig{}, err 550 } 551 } 552 if sharedCfg.CSMEnabled != nil && *sharedCfg.CSMEnabled == true { 553 return csmConfig{ 554 Enabled: true, 555 ClientID: sharedCfg.CSMClientID, 556 Host: sharedCfg.CSMHost, 557 Port: sharedCfg.CSMPort, 558 }, nil 559 } 560 561 return csmConfig{}, nil 562} 563 564func setTLSOptions(opts *Options, cfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig) error { 565 // CA Bundle can be specified in both environment variable shared config file. 566 var caBundleFilename = envCfg.CustomCABundle 567 if len(caBundleFilename) == 0 { 568 caBundleFilename = sharedCfg.CustomCABundle 569 } 570 571 // Only use environment value if session option is not provided. 572 customTLSOptions := map[string]struct { 573 filename string 574 field *io.Reader 575 errCode string 576 }{ 577 "custom CA bundle PEM": {filename: caBundleFilename, field: &opts.CustomCABundle, errCode: ErrCodeLoadCustomCABundle}, 578 "custom client TLS cert": {filename: envCfg.ClientTLSCert, field: &opts.ClientTLSCert, errCode: ErrCodeLoadClientTLSCert}, 579 "custom client TLS key": {filename: envCfg.ClientTLSKey, field: &opts.ClientTLSKey, errCode: ErrCodeLoadClientTLSCert}, 580 } 581 for name, v := range customTLSOptions { 582 if len(v.filename) != 0 && *v.field == nil { 583 f, err := os.Open(v.filename) 584 if err != nil { 585 return awserr.New(v.errCode, fmt.Sprintf("failed to open %s file", name), err) 586 } 587 defer f.Close() 588 *v.field = f 589 } 590 } 591 592 // Setup HTTP client with custom cert bundle if enabled 593 if opts.CustomCABundle != nil { 594 if err := loadCustomCABundle(cfg.HTTPClient, opts.CustomCABundle); err != nil { 595 return err 596 } 597 } 598 599 // Setup HTTP client TLS certificate and key for client TLS authentication. 600 if opts.ClientTLSCert != nil && opts.ClientTLSKey != nil { 601 if err := loadClientTLSCert(cfg.HTTPClient, opts.ClientTLSCert, opts.ClientTLSKey); err != nil { 602 return err 603 } 604 } else if opts.ClientTLSCert == nil && opts.ClientTLSKey == nil { 605 // Do nothing if neither values are available. 606 607 } else { 608 return awserr.New(ErrCodeLoadClientTLSCert, 609 fmt.Sprintf("client TLS cert(%t) and key(%t) must both be provided", 610 opts.ClientTLSCert != nil, opts.ClientTLSKey != nil), nil) 611 } 612 613 return nil 614} 615 616func getHTTPTransport(client *http.Client) (*http.Transport, error) { 617 var t *http.Transport 618 switch v := client.Transport.(type) { 619 case *http.Transport: 620 t = v 621 default: 622 if client.Transport != nil { 623 return nil, fmt.Errorf("unsupported transport, %T", client.Transport) 624 } 625 } 626 if t == nil { 627 // Nil transport implies `http.DefaultTransport` should be used. Since 628 // the SDK cannot modify, nor copy the `DefaultTransport` specifying 629 // the values the next closest behavior. 630 t = getCustomTransport() 631 } 632 633 return t, nil 634} 635 636func loadCustomCABundle(client *http.Client, bundle io.Reader) error { 637 t, err := getHTTPTransport(client) 638 if err != nil { 639 return awserr.New(ErrCodeLoadCustomCABundle, 640 "unable to load custom CA bundle, HTTPClient's transport unsupported type", err) 641 } 642 643 p, err := loadCertPool(bundle) 644 if err != nil { 645 return err 646 } 647 if t.TLSClientConfig == nil { 648 t.TLSClientConfig = &tls.Config{} 649 } 650 t.TLSClientConfig.RootCAs = p 651 652 client.Transport = t 653 654 return nil 655} 656 657func loadCertPool(r io.Reader) (*x509.CertPool, error) { 658 b, err := ioutil.ReadAll(r) 659 if err != nil { 660 return nil, awserr.New(ErrCodeLoadCustomCABundle, 661 "failed to read custom CA bundle PEM file", err) 662 } 663 664 p := x509.NewCertPool() 665 if !p.AppendCertsFromPEM(b) { 666 return nil, awserr.New(ErrCodeLoadCustomCABundle, 667 "failed to load custom CA bundle PEM file", err) 668 } 669 670 return p, nil 671} 672 673func loadClientTLSCert(client *http.Client, certFile, keyFile io.Reader) error { 674 t, err := getHTTPTransport(client) 675 if err != nil { 676 return awserr.New(ErrCodeLoadClientTLSCert, 677 "unable to get usable HTTP transport from client", err) 678 } 679 680 cert, err := ioutil.ReadAll(certFile) 681 if err != nil { 682 return awserr.New(ErrCodeLoadClientTLSCert, 683 "unable to get read client TLS cert file", err) 684 } 685 686 key, err := ioutil.ReadAll(keyFile) 687 if err != nil { 688 return awserr.New(ErrCodeLoadClientTLSCert, 689 "unable to get read client TLS key file", err) 690 } 691 692 clientCert, err := tls.X509KeyPair(cert, key) 693 if err != nil { 694 return awserr.New(ErrCodeLoadClientTLSCert, 695 "unable to load x509 key pair from client cert", err) 696 } 697 698 tlsCfg := t.TLSClientConfig 699 if tlsCfg == nil { 700 tlsCfg = &tls.Config{} 701 } 702 703 tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert) 704 705 t.TLSClientConfig = tlsCfg 706 client.Transport = t 707 708 return nil 709} 710 711func mergeConfigSrcs(cfg, userCfg *aws.Config, 712 envCfg envConfig, sharedCfg sharedConfig, 713 handlers request.Handlers, 714 sessOpts Options, 715) error { 716 717 // Region if not already set by user 718 if len(aws.StringValue(cfg.Region)) == 0 { 719 if len(envCfg.Region) > 0 { 720 cfg.WithRegion(envCfg.Region) 721 } else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 { 722 cfg.WithRegion(sharedCfg.Region) 723 } 724 } 725 726 if cfg.EnableEndpointDiscovery == nil { 727 if envCfg.EnableEndpointDiscovery != nil { 728 cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery) 729 } else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil { 730 cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery) 731 } 732 } 733 734 // Regional Endpoint flag for STS endpoint resolving 735 mergeSTSRegionalEndpointConfig(cfg, []endpoints.STSRegionalEndpoint{ 736 userCfg.STSRegionalEndpoint, 737 envCfg.STSRegionalEndpoint, 738 sharedCfg.STSRegionalEndpoint, 739 endpoints.LegacySTSEndpoint, 740 }) 741 742 // Regional Endpoint flag for S3 endpoint resolving 743 mergeS3UsEast1RegionalEndpointConfig(cfg, []endpoints.S3UsEast1RegionalEndpoint{ 744 userCfg.S3UsEast1RegionalEndpoint, 745 envCfg.S3UsEast1RegionalEndpoint, 746 sharedCfg.S3UsEast1RegionalEndpoint, 747 endpoints.LegacyS3UsEast1Endpoint, 748 }) 749 750 var ec2IMDSEndpoint string 751 for _, v := range []string{ 752 sessOpts.EC2IMDSEndpoint, 753 envCfg.EC2IMDSEndpoint, 754 sharedCfg.EC2IMDSEndpoint, 755 } { 756 if len(v) != 0 { 757 ec2IMDSEndpoint = v 758 break 759 } 760 } 761 762 var endpointMode endpoints.EC2IMDSEndpointModeState 763 for _, v := range []endpoints.EC2IMDSEndpointModeState{ 764 sessOpts.EC2IMDSEndpointMode, 765 envCfg.EC2IMDSEndpointMode, 766 sharedCfg.EC2IMDSEndpointMode, 767 } { 768 if v != endpoints.EC2IMDSEndpointModeStateUnset { 769 endpointMode = v 770 break 771 } 772 } 773 774 if len(ec2IMDSEndpoint) != 0 || endpointMode != endpoints.EC2IMDSEndpointModeStateUnset { 775 cfg.EndpointResolver = wrapEC2IMDSEndpoint(cfg.EndpointResolver, ec2IMDSEndpoint, endpointMode) 776 } 777 778 // Configure credentials if not already set by the user when creating the 779 // Session. 780 if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil { 781 creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts) 782 if err != nil { 783 return err 784 } 785 cfg.Credentials = creds 786 } 787 788 cfg.S3UseARNRegion = userCfg.S3UseARNRegion 789 if cfg.S3UseARNRegion == nil { 790 cfg.S3UseARNRegion = &envCfg.S3UseARNRegion 791 } 792 if cfg.S3UseARNRegion == nil { 793 cfg.S3UseARNRegion = &sharedCfg.S3UseARNRegion 794 } 795 796 for _, v := range []endpoints.DualStackEndpointState{userCfg.UseDualStackEndpoint, envCfg.UseDualStackEndpoint, sharedCfg.UseDualStackEndpoint} { 797 if v != endpoints.DualStackEndpointStateUnset { 798 cfg.UseDualStackEndpoint = v 799 break 800 } 801 } 802 803 for _, v := range []endpoints.FIPSEndpointState{userCfg.UseFIPSEndpoint, envCfg.UseFIPSEndpoint, sharedCfg.UseFIPSEndpoint} { 804 if v != endpoints.FIPSEndpointStateUnset { 805 cfg.UseFIPSEndpoint = v 806 break 807 } 808 } 809 810 return nil 811} 812 813func mergeSTSRegionalEndpointConfig(cfg *aws.Config, values []endpoints.STSRegionalEndpoint) { 814 for _, v := range values { 815 if v != endpoints.UnsetSTSEndpoint { 816 cfg.STSRegionalEndpoint = v 817 break 818 } 819 } 820} 821 822func mergeS3UsEast1RegionalEndpointConfig(cfg *aws.Config, values []endpoints.S3UsEast1RegionalEndpoint) { 823 for _, v := range values { 824 if v != endpoints.UnsetS3UsEast1Endpoint { 825 cfg.S3UsEast1RegionalEndpoint = v 826 break 827 } 828 } 829} 830 831func initHandlers(s *Session) { 832 // Add the Validate parameter handler if it is not disabled. 833 s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler) 834 if !aws.BoolValue(s.Config.DisableParamValidation) { 835 s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler) 836 } 837} 838 839// Copy creates and returns a copy of the current Session, copying the config 840// and handlers. If any additional configs are provided they will be merged 841// on top of the Session's copied config. 842// 843// // Create a copy of the current Session, configured for the us-west-2 region. 844// sess.Copy(&aws.Config{Region: aws.String("us-west-2")}) 845func (s *Session) Copy(cfgs ...*aws.Config) *Session { 846 newSession := &Session{ 847 Config: s.Config.Copy(cfgs...), 848 Handlers: s.Handlers.Copy(), 849 options: s.options, 850 } 851 852 initHandlers(newSession) 853 854 return newSession 855} 856 857// ClientConfig satisfies the client.ConfigProvider interface and is used to 858// configure the service client instances. Passing the Session to the service 859// client's constructor (New) will use this method to configure the client. 860func (s *Session) ClientConfig(service string, cfgs ...*aws.Config) client.Config { 861 s = s.Copy(cfgs...) 862 863 resolvedRegion := normalizeRegion(s.Config) 864 865 region := aws.StringValue(s.Config.Region) 866 resolved, err := s.resolveEndpoint(service, region, resolvedRegion, s.Config) 867 if err != nil { 868 s.Handlers.Validate.PushBack(func(r *request.Request) { 869 if len(r.ClientInfo.Endpoint) != 0 { 870 // Error occurred while resolving endpoint, but the request 871 // being invoked has had an endpoint specified after the client 872 // was created. 873 return 874 } 875 r.Error = err 876 }) 877 } 878 879 return client.Config{ 880 Config: s.Config, 881 Handlers: s.Handlers, 882 PartitionID: resolved.PartitionID, 883 Endpoint: resolved.URL, 884 SigningRegion: resolved.SigningRegion, 885 SigningNameDerived: resolved.SigningNameDerived, 886 SigningName: resolved.SigningName, 887 ResolvedRegion: resolvedRegion, 888 } 889} 890 891const ec2MetadataServiceID = "ec2metadata" 892 893func (s *Session) resolveEndpoint(service, region, resolvedRegion string, cfg *aws.Config) (endpoints.ResolvedEndpoint, error) { 894 895 if ep := aws.StringValue(cfg.Endpoint); len(ep) != 0 { 896 return endpoints.ResolvedEndpoint{ 897 URL: endpoints.AddScheme(ep, aws.BoolValue(cfg.DisableSSL)), 898 SigningRegion: region, 899 }, nil 900 } 901 902 resolved, err := cfg.EndpointResolver.EndpointFor(service, region, 903 func(opt *endpoints.Options) { 904 opt.DisableSSL = aws.BoolValue(cfg.DisableSSL) 905 906 opt.UseDualStack = aws.BoolValue(cfg.UseDualStack) 907 opt.UseDualStackEndpoint = cfg.UseDualStackEndpoint 908 909 opt.UseFIPSEndpoint = cfg.UseFIPSEndpoint 910 911 // Support for STSRegionalEndpoint where the STSRegionalEndpoint is 912 // provided in envConfig or sharedConfig with envConfig getting 913 // precedence. 914 opt.STSRegionalEndpoint = cfg.STSRegionalEndpoint 915 916 // Support for S3UsEast1RegionalEndpoint where the S3UsEast1RegionalEndpoint is 917 // provided in envConfig or sharedConfig with envConfig getting 918 // precedence. 919 opt.S3UsEast1RegionalEndpoint = cfg.S3UsEast1RegionalEndpoint 920 921 // Support the condition where the service is modeled but its 922 // endpoint metadata is not available. 923 opt.ResolveUnknownService = true 924 925 opt.ResolvedRegion = resolvedRegion 926 927 opt.Logger = cfg.Logger 928 opt.LogDeprecated = cfg.LogLevel.Matches(aws.LogDebugWithDeprecated) 929 }, 930 ) 931 if err != nil { 932 return endpoints.ResolvedEndpoint{}, err 933 } 934 935 return resolved, nil 936} 937 938// ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception 939// that the EndpointResolver will not be used to resolve the endpoint. The only 940// endpoint set must come from the aws.Config.Endpoint field. 941func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config { 942 s = s.Copy(cfgs...) 943 944 resolvedRegion := normalizeRegion(s.Config) 945 946 var resolved endpoints.ResolvedEndpoint 947 if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 { 948 resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL)) 949 resolved.SigningRegion = aws.StringValue(s.Config.Region) 950 } 951 952 return client.Config{ 953 Config: s.Config, 954 Handlers: s.Handlers, 955 Endpoint: resolved.URL, 956 SigningRegion: resolved.SigningRegion, 957 SigningNameDerived: resolved.SigningNameDerived, 958 SigningName: resolved.SigningName, 959 ResolvedRegion: resolvedRegion, 960 } 961} 962 963// logDeprecatedNewSessionError function enables error handling for session 964func (s *Session) logDeprecatedNewSessionError(msg string, err error, cfgs []*aws.Config) { 965 // Session creation failed, need to report the error and prevent 966 // any requests from succeeding. 967 s.Config.MergeIn(cfgs...) 968 s.Config.Logger.Log("ERROR:", msg, "Error:", err) 969 s.Handlers.Validate.PushBack(func(r *request.Request) { 970 r.Error = err 971 }) 972} 973 974// normalizeRegion resolves / normalizes the configured region (converts pseudo fips regions), and modifies the provided 975// config to have the equivalent options for resolution and returns the resolved region name. 976func normalizeRegion(cfg *aws.Config) (resolved string) { 977 const fipsInfix = "-fips-" 978 const fipsPrefix = "-fips" 979 const fipsSuffix = "fips-" 980 981 region := aws.StringValue(cfg.Region) 982 983 if strings.Contains(region, fipsInfix) || 984 strings.Contains(region, fipsPrefix) || 985 strings.Contains(region, fipsSuffix) { 986 resolved = strings.Replace(strings.Replace(strings.Replace( 987 region, fipsInfix, "-", -1), fipsPrefix, "", -1), fipsSuffix, "", -1) 988 cfg.UseFIPSEndpoint = endpoints.FIPSEndpointStateEnabled 989 } 990 991 return resolved 992} 993