github.com/aavshr/aws-sdk-go@v1.41.3/aws/session/session.go (about)

     1  package session
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"os"
    11  	"time"
    12  
    13  	"github.com/aavshr/aws-sdk-go/aws"
    14  	"github.com/aavshr/aws-sdk-go/aws/awserr"
    15  	"github.com/aavshr/aws-sdk-go/aws/client"
    16  	"github.com/aavshr/aws-sdk-go/aws/corehandlers"
    17  	"github.com/aavshr/aws-sdk-go/aws/credentials"
    18  	"github.com/aavshr/aws-sdk-go/aws/csm"
    19  	"github.com/aavshr/aws-sdk-go/aws/defaults"
    20  	"github.com/aavshr/aws-sdk-go/aws/endpoints"
    21  	"github.com/aavshr/aws-sdk-go/aws/request"
    22  )
    23  
    24  const (
    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
    39  var 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
    43  var 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
    46  var 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.
    55  type 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.
    83  func 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.
   147  func 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.
   157  type SharedConfigState int
   158  
   159  const (
   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  //
   177  type 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  //     }))
   337  func 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())
   373  func 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.
   383  func 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  
   403  func 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  
   437  func 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  
   451  func 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  
   523  type csmConfig struct {
   524  	Enabled  bool
   525  	Host     string
   526  	Port     string
   527  	ClientID string
   528  }
   529  
   530  var csmProfileName = "aws_csm"
   531  
   532  func 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  
   563  func 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  
   615  func 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  
   635  func 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  
   656  func 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  
   672  func 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  
   710  func 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  
   798  func 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  
   807  func 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  
   816  func 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")})
   830  func (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.
   845  func (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  
   873  const ec2MetadataServiceID = "ec2metadata"
   874  
   875  func (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.
   913  func (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
   933  func (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  }