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"
25 // ErrCodeSharedConfig represents an error that occurs in the shared
26 // configuration logic
27 ErrCodeSharedConfig = "SharedConfigErr"
30 // ErrSharedConfigSourceCollision will be returned if a section contains both
31 // source_profile and credential_source
32 var ErrSharedConfigSourceCollision = awserr.New(ErrCodeSharedConfig, "only source profile or credential source can be specified, not both", nil)
34 // ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment
35 // variables are empty and Environment was set as the credential source
36 var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil)
38 // ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided
39 var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil)
41 // A Session provides a central location to create service clients from and
42 // store configurations and request handlers for those services.
44 // Sessions are safe to create service clients concurrently, but it is not safe
45 // to mutate the Session concurrently.
47 // The Session satisfies the service client's client.ConfigProvider.
50 Handlers request.Handlers
53 // New creates a new instance of the handlers merging in the provided configs
54 // on top of the SDK's default configurations. Once the Session is created it
55 // can be mutated to modify the Config or Handlers. The Session is safe to be
56 // read concurrently, but it should not be written to concurrently.
58 // If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New
59 // method could now encounter an error when loading the configuration. When
60 // The environment variable is set, and an error occurs, New will return a
61 // session that will fail all requests reporting the error that occurred while
62 // loading the session. Use NewSession to get the error when creating the
65 // If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
66 // the shared config file (~/.aws/config) will also be loaded, in addition to
67 // the shared credentials file (~/.aws/credentials). Values set in both the
68 // shared config, and shared credentials will be taken from the shared
71 // Deprecated: Use NewSession functions to create sessions instead. NewSession
72 // has the same functionality as New except an error can be returned when the
73 // func is called instead of waiting to receive an error until a request is made.
74 func New(cfgs ...*aws.Config) *Session {
75 // load initial config from environment
76 envCfg := loadEnvConfig()
78 if envCfg.EnableSharedConfig {
81 s, err := NewSessionWithOptions(Options{
83 SharedConfigState: SharedConfigEnable,
86 // Old session.New expected all errors to be discovered when
87 // a request is made, and would report the errors then. This
88 // needs to be replicated if an error occurs while creating
90 msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " +
91 "Use session.NewSession to handle errors occurring during session creation."
93 // Session creation failed, need to report the error and prevent
94 // any requests from succeeding.
95 s = &Session{Config: defaults.Config()}
96 s.Config.MergeIn(cfgs...)
97 s.Config.Logger.Log("ERROR:", msg, "Error:", err)
98 s.Handlers.Validate.PushBack(func(r *request.Request) {
106 s := deprecatedNewSession(cfgs...)
107 if envCfg.CSMEnabled {
108 err := enableCSM(&s.Handlers, envCfg.CSMClientID,
109 envCfg.CSMHost, envCfg.CSMPort, s.Config.Logger)
111 err = fmt.Errorf("failed to enable CSM, %v", err)
112 s.Config.Logger.Log("ERROR:", err.Error())
113 s.Handlers.Validate.PushBack(func(r *request.Request) {
122 // NewSession returns a new Session created from SDK defaults, config files,
123 // environment, and user provided config files. Once the Session is created
124 // it can be mutated to modify the Config or Handlers. The Session is safe to
125 // be read concurrently, but it should not be written to concurrently.
127 // If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
128 // the shared config file (~/.aws/config) will also be loaded in addition to
129 // the shared credentials file (~/.aws/credentials). Values set in both the
130 // shared config, and shared credentials will be taken from the shared
131 // credentials file. Enabling the Shared Config will also allow the Session
132 // to be built with retrieving credentials with AssumeRole set in the config.
134 // See the NewSessionWithOptions func for information on how to override or
135 // control through code how the Session will be created. Such as specifying the
136 // config profile, and controlling if shared config is enabled or not.
137 func NewSession(cfgs ...*aws.Config) (*Session, error) {
139 opts.Config.MergeIn(cfgs...)
141 return NewSessionWithOptions(opts)
144 // SharedConfigState provides the ability to optionally override the state
145 // of the session's creation based on the shared config being enabled or
147 type SharedConfigState int
150 // SharedConfigStateFromEnv does not override any state of the
151 // AWS_SDK_LOAD_CONFIG env var. It is the default value of the
152 // SharedConfigState type.
153 SharedConfigStateFromEnv SharedConfigState = iota
155 // SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value
156 // and disables the shared config functionality.
159 // SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value
160 // and enables the shared config functionality.
164 // Options provides the means to control how a Session is created and what
165 // configuration values will be loaded.
167 type Options struct {
168 // Provides config values for the SDK to use when creating service clients
169 // and making API requests to services. Any value set in with this field
170 // will override the associated value provided by the SDK defaults,
171 // environment or config files where relevant.
173 // If not set, configuration values from from SDK defaults, environment,
174 // config will be used.
177 // Overrides the config profile the Session should be created from. If not
178 // set the value of the environment variable will be loaded (AWS_PROFILE,
179 // or AWS_DEFAULT_PROFILE if the Shared Config is enabled).
181 // If not set and environment variables are not set the "default"
182 // (DefaultSharedConfigProfile) will be used as the profile to load the
183 // session config from.
186 // Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG
187 // environment variable. By default a Session will be created using the
188 // value provided by the AWS_SDK_LOAD_CONFIG environment variable.
190 // Setting this value to SharedConfigEnable or SharedConfigDisable
191 // will allow you to override the AWS_SDK_LOAD_CONFIG environment variable
192 // and enable or disable the shared config functionality.
193 SharedConfigState SharedConfigState
195 // Ordered list of files the session will load configuration from.
196 // It will override environment variable AWS_SHARED_CREDENTIALS_FILE, AWS_CONFIG_FILE.
197 SharedConfigFiles []string
199 // When the SDK's shared config is configured to assume a role with MFA
200 // this option is required in order to provide the mechanism that will
201 // retrieve the MFA token. There is no default value for this field. If
202 // it is not set an error will be returned when creating the session.
204 // This token provider will be called when ever the assumed role's
205 // credentials need to be refreshed. Within the context of service clients
206 // all sharing the same session the SDK will ensure calls to the token
207 // provider are atomic. When sharing a token provider across multiple
208 // sessions additional synchronization logic is needed to ensure the
209 // token providers do not introduce race conditions. It is recommend to
210 // share the session where possible.
212 // stscreds.StdinTokenProvider is a basic implementation that will prompt
213 // from stdin for the MFA token code.
215 // This field is only used if the shared configuration is enabled, and
216 // the config enables assume role wit MFA via the mfa_serial field.
217 AssumeRoleTokenProvider func() (string, error)
219 // When the SDK's shared config is configured to assume a role this option
220 // may be provided to set the expiry duration of the STS credentials.
221 // Defaults to 15 minutes if not set as documented in the
222 // stscreds.AssumeRoleProvider.
223 AssumeRoleDuration time.Duration
225 // Reader for a custom Credentials Authority (CA) bundle in PEM format that
226 // the SDK will use instead of the default system's root CA bundle. Use this
227 // only if you want to replace the CA bundle the SDK uses for TLS requests.
229 // Enabling this option will attempt to merge the Transport into the SDK's HTTP
230 // client. If the client's Transport is not a http.Transport an error will be
231 // returned. If the Transport's TLS config is set this option will cause the SDK
232 // to overwrite the Transport's TLS config's RootCAs value. If the CA
233 // bundle reader contains multiple certificates all of them will be loaded.
235 // The Session option CustomCABundle is also available when creating sessions
236 // to also enable this feature. CustomCABundle session option field has priority
237 // over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
238 CustomCABundle io.Reader
240 // The handlers that the session and all API clients will be created with.
241 // This must be a complete set of handlers. Use the defaults.Handlers()
242 // function to initialize this value before changing the handlers to be
244 Handlers request.Handlers
247 // NewSessionWithOptions returns a new Session created from SDK defaults, config files,
248 // environment, and user provided config files. This func uses the Options
249 // values to configure how the Session is created.
251 // If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
252 // the shared config file (~/.aws/config) will also be loaded in addition to
253 // the shared credentials file (~/.aws/credentials). Values set in both the
254 // shared config, and shared credentials will be taken from the shared
255 // credentials file. Enabling the Shared Config will also allow the Session
256 // to be built with retrieving credentials with AssumeRole set in the config.
258 // // Equivalent to session.New
259 // sess := session.Must(session.NewSessionWithOptions(session.Options{}))
261 // // Specify profile to load for the session's config
262 // sess := session.Must(session.NewSessionWithOptions(session.Options{
263 // Profile: "profile_name",
266 // // Specify profile for config and region for requests
267 // sess := session.Must(session.NewSessionWithOptions(session.Options{
268 // Config: aws.Config{Region: aws.String("us-east-1")},
269 // Profile: "profile_name",
272 // // Force enable Shared Config support
273 // sess := session.Must(session.NewSessionWithOptions(session.Options{
274 // SharedConfigState: session.SharedConfigEnable,
276 func NewSessionWithOptions(opts Options) (*Session, error) {
278 if opts.SharedConfigState == SharedConfigEnable {
279 envCfg = loadSharedEnvConfig()
281 envCfg = loadEnvConfig()
284 if len(opts.Profile) > 0 {
285 envCfg.Profile = opts.Profile
288 switch opts.SharedConfigState {
289 case SharedConfigDisable:
290 envCfg.EnableSharedConfig = false
291 case SharedConfigEnable:
292 envCfg.EnableSharedConfig = true
295 // Only use AWS_CA_BUNDLE if session option is not provided.
296 if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil {
297 f, err := os.Open(envCfg.CustomCABundle)
299 return nil, awserr.New("LoadCustomCABundleError",
300 "failed to open custom CA bundle PEM file", err)
303 opts.CustomCABundle = f
306 return newSession(opts, envCfg, &opts.Config)
309 // Must is a helper function to ensure the Session is valid and there was no
310 // error when calling a NewSession function.
312 // This helper is intended to be used in variable initialization to load the
313 // Session and configuration at startup. Such as:
315 // var sess = session.Must(session.NewSession())
316 func Must(sess *Session, err error) *Session {
324 func deprecatedNewSession(cfgs ...*aws.Config) *Session {
325 cfg := defaults.Config()
326 handlers := defaults.Handlers()
328 // Apply the passed in configs so the configuration can be applied to the
329 // default credential chain
331 if cfg.EndpointResolver == nil {
332 // An endpoint resolver is required for a session to be able to provide
333 // endpoints for service client configurations.
334 cfg.EndpointResolver = endpoints.DefaultResolver()
336 cfg.Credentials = defaults.CredChain(cfg, handlers)
338 // Reapply any passed in configs to override credentials if set
350 func enableCSM(handlers *request.Handlers,
351 clientID, host, port string,
355 logger.Log("Enabling CSM")
358 r, err := csm.Start(clientID, csm.AddressWithDefaults(host, port))
362 r.InjectHandlers(handlers)
367 func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
368 cfg := defaults.Config()
370 handlers := opts.Handlers
371 if handlers.IsEmpty() {
372 handlers = defaults.Handlers()
375 // Get a merged version of the user provided config to determine if
377 userCfg := &aws.Config{}
378 userCfg.MergeIn(cfgs...)
381 // Ordered config files will be loaded in with later files overwriting
382 // previous config file values.
383 var cfgFiles []string
384 if opts.SharedConfigFiles != nil {
385 cfgFiles = opts.SharedConfigFiles
387 cfgFiles = []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
388 if !envCfg.EnableSharedConfig {
389 // The shared config file (~/.aws/config) is only loaded if instructed
390 // to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
391 cfgFiles = cfgFiles[1:]
395 // Load additional config from file(s)
396 sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles, envCfg.EnableSharedConfig)
398 if _, ok := err.(SharedConfigProfileNotExistsError); !ok {
403 if err := mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers, opts); err != nil {
413 if envCfg.CSMEnabled {
414 err := enableCSM(&s.Handlers, envCfg.CSMClientID,
415 envCfg.CSMHost, envCfg.CSMPort, s.Config.Logger)
421 // Setup HTTP client with custom cert bundle if enabled
422 if opts.CustomCABundle != nil {
423 if err := loadCustomCABundle(s, opts.CustomCABundle); err != nil {
431 func loadCustomCABundle(s *Session, bundle io.Reader) error {
432 var t *http.Transport
433 switch v := s.Config.HTTPClient.Transport.(type) {
434 case *http.Transport:
437 if s.Config.HTTPClient.Transport != nil {
438 return awserr.New("LoadCustomCABundleError",
439 "unable to load custom CA bundle, HTTPClient's transport unsupported type", nil)
443 // Nil transport implies `http.DefaultTransport` should be used. Since
444 // the SDK cannot modify, nor copy the `DefaultTransport` specifying
445 // the values the next closest behavior.
446 t = getCABundleTransport()
449 p, err := loadCertPool(bundle)
453 if t.TLSClientConfig == nil {
454 t.TLSClientConfig = &tls.Config{}
456 t.TLSClientConfig.RootCAs = p
458 s.Config.HTTPClient.Transport = t
463 func loadCertPool(r io.Reader) (*x509.CertPool, error) {
464 b, err := ioutil.ReadAll(r)
466 return nil, awserr.New("LoadCustomCABundleError",
467 "failed to read custom CA bundle PEM file", err)
470 p := x509.NewCertPool()
471 if !p.AppendCertsFromPEM(b) {
472 return nil, awserr.New("LoadCustomCABundleError",
473 "failed to load custom CA bundle PEM file", err)
479 func mergeConfigSrcs(cfg, userCfg *aws.Config,
480 envCfg envConfig, sharedCfg sharedConfig,
481 handlers request.Handlers,
485 // Region if not already set by user
486 if len(aws.StringValue(cfg.Region)) == 0 {
487 if len(envCfg.Region) > 0 {
488 cfg.WithRegion(envCfg.Region)
489 } else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
490 cfg.WithRegion(sharedCfg.Region)
494 if cfg.EnableEndpointDiscovery == nil {
495 if envCfg.EnableEndpointDiscovery != nil {
496 cfg.WithEndpointDiscovery(*envCfg.EnableEndpointDiscovery)
497 } else if envCfg.EnableSharedConfig && sharedCfg.EnableEndpointDiscovery != nil {
498 cfg.WithEndpointDiscovery(*sharedCfg.EnableEndpointDiscovery)
502 // Configure credentials if not already set by the user when creating the
504 if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
505 creds, err := resolveCredentials(cfg, envCfg, sharedCfg, handlers, sessOpts)
509 cfg.Credentials = creds
515 func initHandlers(s *Session) {
516 // Add the Validate parameter handler if it is not disabled.
517 s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
518 if !aws.BoolValue(s.Config.DisableParamValidation) {
519 s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
523 // Copy creates and returns a copy of the current Session, coping the config
524 // and handlers. If any additional configs are provided they will be merged
525 // on top of the Session's copied config.
527 // // Create a copy of the current Session, configured for the us-west-2 region.
528 // sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
529 func (s *Session) Copy(cfgs ...*aws.Config) *Session {
530 newSession := &Session{
531 Config: s.Config.Copy(cfgs...),
532 Handlers: s.Handlers.Copy(),
535 initHandlers(newSession)
540 // ClientConfig satisfies the client.ConfigProvider interface and is used to
541 // configure the service client instances. Passing the Session to the service
542 // client's constructor (New) will use this method to configure the client.
543 func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
544 // Backwards compatibility, the error will be eaten if user calls ClientConfig
545 // directly. All SDK services will use ClientconfigWithError.
546 cfg, _ := s.clientConfigWithErr(serviceName, cfgs...)
551 func (s *Session) clientConfigWithErr(serviceName string, cfgs ...*aws.Config) (client.Config, error) {
554 var resolved endpoints.ResolvedEndpoint
557 region := aws.StringValue(s.Config.Region)
559 if endpoint := aws.StringValue(s.Config.Endpoint); len(endpoint) != 0 {
560 resolved.URL = endpoints.AddScheme(endpoint, aws.BoolValue(s.Config.DisableSSL))
561 resolved.SigningRegion = region
563 resolved, err = s.Config.EndpointResolver.EndpointFor(
565 func(opt *endpoints.Options) {
566 opt.DisableSSL = aws.BoolValue(s.Config.DisableSSL)
567 opt.UseDualStack = aws.BoolValue(s.Config.UseDualStack)
569 // Support the condition where the service is modeled but its
570 // endpoint metadata is not available.
571 opt.ResolveUnknownService = true
576 return client.Config{
578 Handlers: s.Handlers,
579 Endpoint: resolved.URL,
580 SigningRegion: resolved.SigningRegion,
581 SigningNameDerived: resolved.SigningNameDerived,
582 SigningName: resolved.SigningName,
586 // ClientConfigNoResolveEndpoint is the same as ClientConfig with the exception
587 // that the EndpointResolver will not be used to resolve the endpoint. The only
588 // endpoint set must come from the aws.Config.Endpoint field.
589 func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Config {
592 var resolved endpoints.ResolvedEndpoint
594 region := aws.StringValue(s.Config.Region)
596 if ep := aws.StringValue(s.Config.Endpoint); len(ep) > 0 {
597 resolved.URL = endpoints.AddScheme(ep, aws.BoolValue(s.Config.DisableSSL))
598 resolved.SigningRegion = region
601 return client.Config{
603 Handlers: s.Handlers,
604 Endpoint: resolved.URL,
605 SigningRegion: resolved.SigningRegion,
606 SigningNameDerived: resolved.SigningNameDerived,
607 SigningName: resolved.SigningName,