session.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. package session
  2. import (
  3. "fmt"
  4. "github.com/aws/aws-sdk-go/aws"
  5. "github.com/aws/aws-sdk-go/aws/awserr"
  6. "github.com/aws/aws-sdk-go/aws/client"
  7. "github.com/aws/aws-sdk-go/aws/corehandlers"
  8. "github.com/aws/aws-sdk-go/aws/credentials"
  9. "github.com/aws/aws-sdk-go/aws/credentials/stscreds"
  10. "github.com/aws/aws-sdk-go/aws/defaults"
  11. "github.com/aws/aws-sdk-go/aws/request"
  12. "github.com/aws/aws-sdk-go/private/endpoints"
  13. )
  14. // A Session provides a central location to create service clients from and
  15. // store configurations and request handlers for those services.
  16. //
  17. // Sessions are safe to create service clients concurrently, but it is not safe
  18. // to mutate the Session concurrently.
  19. //
  20. // The Session satisfies the service client's client.ClientConfigProvider.
  21. type Session struct {
  22. Config *aws.Config
  23. Handlers request.Handlers
  24. }
  25. // New creates a new instance of the handlers merging in the provided configs
  26. // on top of the SDK's default configurations. Once the Session is created it
  27. // can be mutated to modify the Config or Handlers. The Session is safe to be
  28. // read concurrently, but it should not be written to concurrently.
  29. //
  30. // If the AWS_SDK_LOAD_CONFIG environment is set to a truthy value, the New
  31. // method could now encounter an error when loading the configuration. When
  32. // The environment variable is set, and an error occurs, New will return a
  33. // session that will fail all requests reporting the error that occured while
  34. // loading the session. Use NewSession to get the error when creating the
  35. // session.
  36. //
  37. // If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
  38. // the shared config file (~/.aws/config) will also be loaded, in addition to
  39. // the shared credentials file (~/.aws/config). Values set in both the
  40. // shared config, and shared credentials will be taken from the shared
  41. // credentials file.
  42. //
  43. // Deprecated: Use NewSession functiions to create sessions instead. NewSession
  44. // has the same functionality as New except an error can be returned when the
  45. // func is called instead of waiting to receive an error until a request is made.
  46. func New(cfgs ...*aws.Config) *Session {
  47. // load initial config from environment
  48. envCfg := loadEnvConfig()
  49. if envCfg.EnableSharedConfig {
  50. s, err := newSession(envCfg, cfgs...)
  51. if err != nil {
  52. // Old session.New expected all errors to be discovered when
  53. // a request is made, and would report the errors then. This
  54. // needs to be replicated if an error occurs while creating
  55. // the session.
  56. msg := "failed to create session with AWS_SDK_LOAD_CONFIG enabled. " +
  57. "Use session.NewSession to handle errors occuring during session creation."
  58. // Session creation failed, need to report the error and prevent
  59. // any requests from succeeding.
  60. s = &Session{Config: defaults.Config()}
  61. s.Config.MergeIn(cfgs...)
  62. s.Config.Logger.Log("ERROR:", msg, "Error:", err)
  63. s.Handlers.Validate.PushBack(func(r *request.Request) {
  64. r.Error = err
  65. })
  66. }
  67. return s
  68. }
  69. return oldNewSession(cfgs...)
  70. }
  71. // NewSession returns a new Session created from SDK defaults, config files,
  72. // environment, and user provided config files. Once the Session is created
  73. // it can be mutated to modify the Config or Handlers. The Session is safe to
  74. // be read concurrently, but it should not be written to concurrently.
  75. //
  76. // If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
  77. // the shared config file (~/.aws/config) will also be loaded in addition to
  78. // the shared credentials file (~/.aws/config). Values set in both the
  79. // shared config, and shared credentials will be taken from the shared
  80. // credentials file. Enabling the Shared Config will also allow the Session
  81. // to be built with retrieving credentials with AssumeRole set in the config.
  82. //
  83. // See the NewSessionWithOptions func for information on how to override or
  84. // control through code how the Session will be created. Such as specifing the
  85. // config profile, and controlling if shared config is enabled or not.
  86. func NewSession(cfgs ...*aws.Config) (*Session, error) {
  87. envCfg := loadEnvConfig()
  88. return newSession(envCfg, cfgs...)
  89. }
  90. // SharedConfigState provides the ability to optionally override the state
  91. // of the session's creation based on the shared config being enabled or
  92. // disabled.
  93. type SharedConfigState int
  94. const (
  95. // SharedConfigStateFromEnv does not override any state of the
  96. // AWS_SDK_LOAD_CONFIG env var. It is the default value of the
  97. // SharedConfigState type.
  98. SharedConfigStateFromEnv SharedConfigState = iota
  99. // SharedConfigDisable overrides the AWS_SDK_LOAD_CONFIG env var value
  100. // and disables the shared config functionality.
  101. SharedConfigDisable
  102. // SharedConfigEnable overrides the AWS_SDK_LOAD_CONFIG env var value
  103. // and enables the shared config functionality.
  104. SharedConfigEnable
  105. )
  106. // Options provides the means to control how a Session is created and what
  107. // configuration values will be loaded.
  108. //
  109. type Options struct {
  110. // Provides config values for the SDK to use when creating service clients
  111. // and making API requests to services. Any value set in with this field
  112. // will override the associated value provided by the SDK defaults,
  113. // environment or config files where relevent.
  114. //
  115. // If not set, configuration values from from SDK defaults, environment,
  116. // config will be used.
  117. Config aws.Config
  118. // Overrides the config profile the Session should be created from. If not
  119. // set the value of the environment variable will be loaded (AWS_PROFILE,
  120. // or AWS_DEFAULT_PROFILE if the Shared Config is enabled).
  121. //
  122. // If not set and environment variables are not set the "default"
  123. // (DefaultSharedConfigProfile) will be used as the profile to load the
  124. // session config from.
  125. Profile string
  126. // Instructs how the Session will be created based on the AWS_SDK_LOAD_CONFIG
  127. // environment variable. By default a Session will be created using the
  128. // value provided by the AWS_SDK_LOAD_CONFIG environment variable.
  129. //
  130. // Setting this value to SharedConfigEnable or SharedConfigDisable
  131. // will allow you to override the AWS_SDK_LOAD_CONFIG environment variable
  132. // and enable or disable the shared config functionality.
  133. SharedConfigState SharedConfigState
  134. }
  135. // NewSessionWithOptions returns a new Session created from SDK defaults, config files,
  136. // environment, and user provided config files. This func uses the Options
  137. // values to configure how the Session is created.
  138. //
  139. // If the AWS_SDK_LOAD_CONFIG environment variable is set to a truthy value
  140. // the shared config file (~/.aws/config) will also be loaded in addition to
  141. // the shared credentials file (~/.aws/config). Values set in both the
  142. // shared config, and shared credentials will be taken from the shared
  143. // credentials file. Enabling the Shared Config will also allow the Session
  144. // to be built with retrieving credentials with AssumeRole set in the config.
  145. //
  146. // // Equivalent to session.New
  147. // sess, err := session.NewSessionWithOptions(session.Options{})
  148. //
  149. // // Specify profile to load for the session's config
  150. // sess, err := session.NewSessionWithOptions(session.Options{
  151. // Profile: "profile_name",
  152. // })
  153. //
  154. // // Specify profile for config and region for requests
  155. // sess, err := session.NewSessionWithOptions(session.Options{
  156. // Config: aws.Config{Region: aws.String("us-east-1")},
  157. // Profile: "profile_name",
  158. // })
  159. //
  160. // // Force enable Shared Config support
  161. // sess, err := session.NewSessionWithOptions(session.Options{
  162. // SharedConfigState: SharedConfigEnable,
  163. // })
  164. func NewSessionWithOptions(opts Options) (*Session, error) {
  165. var envCfg envConfig
  166. if opts.SharedConfigState == SharedConfigEnable {
  167. envCfg = loadSharedEnvConfig()
  168. } else {
  169. envCfg = loadEnvConfig()
  170. }
  171. if len(opts.Profile) > 0 {
  172. envCfg.Profile = opts.Profile
  173. }
  174. switch opts.SharedConfigState {
  175. case SharedConfigDisable:
  176. envCfg.EnableSharedConfig = false
  177. case SharedConfigEnable:
  178. envCfg.EnableSharedConfig = true
  179. }
  180. return newSession(envCfg, &opts.Config)
  181. }
  182. // Must is a helper function to ensure the Session is valid and there was no
  183. // error when calling a NewSession function.
  184. //
  185. // This helper is intended to be used in variable initialization to load the
  186. // Session and configuration at startup. Such as:
  187. //
  188. // var sess = session.Must(session.NewSession())
  189. func Must(sess *Session, err error) *Session {
  190. if err != nil {
  191. panic(err)
  192. }
  193. return sess
  194. }
  195. func oldNewSession(cfgs ...*aws.Config) *Session {
  196. cfg := defaults.Config()
  197. handlers := defaults.Handlers()
  198. // Apply the passed in configs so the configuration can be applied to the
  199. // default credential chain
  200. cfg.MergeIn(cfgs...)
  201. cfg.Credentials = defaults.CredChain(cfg, handlers)
  202. // Reapply any passed in configs to override credentials if set
  203. cfg.MergeIn(cfgs...)
  204. s := &Session{
  205. Config: cfg,
  206. Handlers: handlers,
  207. }
  208. initHandlers(s)
  209. return s
  210. }
  211. func newSession(envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
  212. cfg := defaults.Config()
  213. handlers := defaults.Handlers()
  214. // Get a merged version of the user provided config to determine if
  215. // credentials were.
  216. userCfg := &aws.Config{}
  217. userCfg.MergeIn(cfgs...)
  218. // Order config files will be loaded in with later files overwriting
  219. // previous config file values.
  220. cfgFiles := []string{envCfg.SharedConfigFile, envCfg.SharedCredentialsFile}
  221. if !envCfg.EnableSharedConfig {
  222. // The shared config file (~/.aws/config) is only loaded if instructed
  223. // to load via the envConfig.EnableSharedConfig (AWS_SDK_LOAD_CONFIG).
  224. cfgFiles = cfgFiles[1:]
  225. }
  226. // Load additional config from file(s)
  227. sharedCfg, err := loadSharedConfig(envCfg.Profile, cfgFiles)
  228. if err != nil {
  229. return nil, err
  230. }
  231. mergeConfigSrcs(cfg, userCfg, envCfg, sharedCfg, handlers)
  232. s := &Session{
  233. Config: cfg,
  234. Handlers: handlers,
  235. }
  236. initHandlers(s)
  237. return s, nil
  238. }
  239. func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg sharedConfig, handlers request.Handlers) {
  240. // Merge in user provided configuration
  241. cfg.MergeIn(userCfg)
  242. // Region if not already set by user
  243. if len(aws.StringValue(cfg.Region)) == 0 {
  244. if len(envCfg.Region) > 0 {
  245. cfg.WithRegion(envCfg.Region)
  246. } else if envCfg.EnableSharedConfig && len(sharedCfg.Region) > 0 {
  247. cfg.WithRegion(sharedCfg.Region)
  248. }
  249. }
  250. // Configure credentials if not already set
  251. if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
  252. if len(envCfg.Creds.AccessKeyID) > 0 {
  253. cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
  254. envCfg.Creds,
  255. )
  256. } else if envCfg.EnableSharedConfig && len(sharedCfg.AssumeRole.RoleARN) > 0 && sharedCfg.AssumeRoleSource != nil {
  257. cfgCp := *cfg
  258. cfgCp.Credentials = credentials.NewStaticCredentialsFromCreds(
  259. sharedCfg.AssumeRoleSource.Creds,
  260. )
  261. cfg.Credentials = stscreds.NewCredentials(
  262. &Session{
  263. Config: &cfgCp,
  264. Handlers: handlers.Copy(),
  265. },
  266. sharedCfg.AssumeRole.RoleARN,
  267. func(opt *stscreds.AssumeRoleProvider) {
  268. opt.RoleSessionName = sharedCfg.AssumeRole.RoleSessionName
  269. if len(sharedCfg.AssumeRole.ExternalID) > 0 {
  270. opt.ExternalID = aws.String(sharedCfg.AssumeRole.ExternalID)
  271. }
  272. // MFA not supported
  273. },
  274. )
  275. } else if len(sharedCfg.Creds.AccessKeyID) > 0 {
  276. cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
  277. sharedCfg.Creds,
  278. )
  279. } else {
  280. // Fallback to default credentials provider, include mock errors
  281. // for the credential chain so user can identify why credentials
  282. // failed to be retrieved.
  283. cfg.Credentials = credentials.NewCredentials(&credentials.ChainProvider{
  284. VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
  285. Providers: []credentials.Provider{
  286. &credProviderError{Err: awserr.New("EnvAccessKeyNotFound", "failed to find credentials in the environment.", nil)},
  287. &credProviderError{Err: awserr.New("SharedCredsLoad", fmt.Sprintf("failed to load profile, %s.", envCfg.Profile), nil)},
  288. defaults.RemoteCredProvider(*cfg, handlers),
  289. },
  290. })
  291. }
  292. }
  293. }
  294. type credProviderError struct {
  295. Err error
  296. }
  297. var emptyCreds = credentials.Value{}
  298. func (c credProviderError) Retrieve() (credentials.Value, error) {
  299. return credentials.Value{}, c.Err
  300. }
  301. func (c credProviderError) IsExpired() bool {
  302. return true
  303. }
  304. func initHandlers(s *Session) {
  305. // Add the Validate parameter handler if it is not disabled.
  306. s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
  307. if !aws.BoolValue(s.Config.DisableParamValidation) {
  308. s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
  309. }
  310. }
  311. // Copy creates and returns a copy of the current Session, coping the config
  312. // and handlers. If any additional configs are provided they will be merged
  313. // on top of the Session's copied config.
  314. //
  315. // // Create a copy of the current Session, configured for the us-west-2 region.
  316. // sess.Copy(&aws.Config{Region: aws.String("us-west-2")})
  317. func (s *Session) Copy(cfgs ...*aws.Config) *Session {
  318. newSession := &Session{
  319. Config: s.Config.Copy(cfgs...),
  320. Handlers: s.Handlers.Copy(),
  321. }
  322. initHandlers(newSession)
  323. return newSession
  324. }
  325. // ClientConfig satisfies the client.ConfigProvider interface and is used to
  326. // configure the service client instances. Passing the Session to the service
  327. // client's constructor (New) will use this method to configure the client.
  328. func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
  329. s = s.Copy(cfgs...)
  330. endpoint, signingRegion := endpoints.NormalizeEndpoint(
  331. aws.StringValue(s.Config.Endpoint),
  332. serviceName,
  333. aws.StringValue(s.Config.Region),
  334. aws.BoolValue(s.Config.DisableSSL),
  335. aws.BoolValue(s.Config.UseDualStack),
  336. )
  337. return client.Config{
  338. Config: s.Config,
  339. Handlers: s.Handlers,
  340. Endpoint: endpoint,
  341. SigningRegion: signingRegion,
  342. }
  343. }