shared_config.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. package session
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "github.com/aws/aws-sdk-go/aws/awserr"
  6. "github.com/aws/aws-sdk-go/aws/credentials"
  7. "github.com/go-ini/ini"
  8. )
  9. const (
  10. // Static Credentials group
  11. accessKeyIDKey = `aws_access_key_id` // group required
  12. secretAccessKey = `aws_secret_access_key` // group required
  13. sessionTokenKey = `aws_session_token` // optional
  14. // Assume Role Credentials group
  15. roleArnKey = `role_arn` // group required
  16. sourceProfileKey = `source_profile` // group required
  17. externalIDKey = `external_id` // optional
  18. mfaSerialKey = `mfa_serial` // optional
  19. roleSessionNameKey = `role_session_name` // optional
  20. // Additional Config fields
  21. regionKey = `region`
  22. // DefaultSharedConfigProfile is the default profile to be used when
  23. // loading configuration from the config files if another profile name
  24. // is not provided.
  25. DefaultSharedConfigProfile = `default`
  26. )
  27. type assumeRoleConfig struct {
  28. RoleARN string
  29. SourceProfile string
  30. ExternalID string
  31. MFASerial string
  32. RoleSessionName string
  33. }
  34. // sharedConfig represents the configuration fields of the SDK config files.
  35. type sharedConfig struct {
  36. // Credentials values from the config file. Both aws_access_key_id
  37. // and aws_secret_access_key must be provided together in the same file
  38. // to be considered valid. The values will be ignored if not a complete group.
  39. // aws_session_token is an optional field that can be provided if both of the
  40. // other two fields are also provided.
  41. //
  42. // aws_access_key_id
  43. // aws_secret_access_key
  44. // aws_session_token
  45. Creds credentials.Value
  46. AssumeRole assumeRoleConfig
  47. AssumeRoleSource *sharedConfig
  48. // Region is the region the SDK should use for looking up AWS service endpoints
  49. // and signing requests.
  50. //
  51. // region
  52. Region string
  53. }
  54. type sharedConfigFile struct {
  55. Filename string
  56. IniData *ini.File
  57. }
  58. // loadSharedConfig retrieves the configuration from the list of files
  59. // using the profile provided. The order the files are listed will determine
  60. // precedence. Values in subsequent files will overwrite values defined in
  61. // earlier files.
  62. //
  63. // For example, given two files A and B. Both define credentials. If the order
  64. // of the files are A then B, B's credential values will be used instead of A's.
  65. //
  66. // See sharedConfig.setFromFile for information how the config files
  67. // will be loaded.
  68. func loadSharedConfig(profile string, filenames []string) (sharedConfig, error) {
  69. if len(profile) == 0 {
  70. profile = DefaultSharedConfigProfile
  71. }
  72. files, err := loadSharedConfigIniFiles(filenames)
  73. if err != nil {
  74. return sharedConfig{}, err
  75. }
  76. cfg := sharedConfig{}
  77. if err = cfg.setFromIniFiles(profile, files); err != nil {
  78. return sharedConfig{}, err
  79. }
  80. if len(cfg.AssumeRole.SourceProfile) > 0 {
  81. if err := cfg.setAssumeRoleSource(profile, files); err != nil {
  82. return sharedConfig{}, err
  83. }
  84. }
  85. return cfg, nil
  86. }
  87. func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
  88. files := make([]sharedConfigFile, 0, len(filenames))
  89. for _, filename := range filenames {
  90. b, err := ioutil.ReadFile(filename)
  91. if err != nil {
  92. // Skip files which can't be opened and read for whatever reason
  93. continue
  94. }
  95. f, err := ini.Load(b)
  96. if err != nil {
  97. return nil, SharedConfigLoadError{Filename: filename, Err: err}
  98. }
  99. files = append(files, sharedConfigFile{
  100. Filename: filename, IniData: f,
  101. })
  102. }
  103. return files, nil
  104. }
  105. func (cfg *sharedConfig) setAssumeRoleSource(origProfile string, files []sharedConfigFile) error {
  106. var assumeRoleSrc sharedConfig
  107. // Multiple level assume role chains are not support
  108. if cfg.AssumeRole.SourceProfile == origProfile {
  109. assumeRoleSrc = *cfg
  110. assumeRoleSrc.AssumeRole = assumeRoleConfig{}
  111. } else {
  112. err := assumeRoleSrc.setFromIniFiles(cfg.AssumeRole.SourceProfile, files)
  113. if err != nil {
  114. return err
  115. }
  116. }
  117. if len(assumeRoleSrc.Creds.AccessKeyID) == 0 {
  118. return SharedConfigAssumeRoleError{RoleARN: cfg.AssumeRole.RoleARN}
  119. }
  120. cfg.AssumeRoleSource = &assumeRoleSrc
  121. return nil
  122. }
  123. func (cfg *sharedConfig) setFromIniFiles(profile string, files []sharedConfigFile) error {
  124. // Trim files from the list that don't exist.
  125. for _, f := range files {
  126. if err := cfg.setFromIniFile(profile, f); err != nil {
  127. if _, ok := err.(SharedConfigProfileNotExistsError); ok {
  128. // Ignore proviles missings
  129. continue
  130. }
  131. return err
  132. }
  133. }
  134. return nil
  135. }
  136. // setFromFile loads the configuration from the file using
  137. // the profile provided. A sharedConfig pointer type value is used so that
  138. // multiple config file loadings can be chained.
  139. //
  140. // Only loads complete logically grouped values, and will not set fields in cfg
  141. // for incomplete grouped values in the config. Such as credentials. For example
  142. // if a config file only includes aws_access_key_id but no aws_secret_access_key
  143. // the aws_access_key_id will be ignored.
  144. func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) error {
  145. section, err := file.IniData.GetSection(profile)
  146. if err != nil {
  147. // Fallback to to alternate profile name: profile <name>
  148. section, err = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
  149. if err != nil {
  150. return SharedConfigProfileNotExistsError{Profile: profile, Err: err}
  151. }
  152. }
  153. // Shared Credentials
  154. akid := section.Key(accessKeyIDKey).String()
  155. secret := section.Key(secretAccessKey).String()
  156. if len(akid) > 0 && len(secret) > 0 {
  157. cfg.Creds = credentials.Value{
  158. AccessKeyID: akid,
  159. SecretAccessKey: secret,
  160. SessionToken: section.Key(sessionTokenKey).String(),
  161. ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename),
  162. }
  163. }
  164. // Assume Role
  165. roleArn := section.Key(roleArnKey).String()
  166. srcProfile := section.Key(sourceProfileKey).String()
  167. if len(roleArn) > 0 && len(srcProfile) > 0 {
  168. cfg.AssumeRole = assumeRoleConfig{
  169. RoleARN: roleArn,
  170. SourceProfile: srcProfile,
  171. ExternalID: section.Key(externalIDKey).String(),
  172. MFASerial: section.Key(mfaSerialKey).String(),
  173. RoleSessionName: section.Key(roleSessionNameKey).String(),
  174. }
  175. }
  176. // Region
  177. if v := section.Key(regionKey).String(); len(v) > 0 {
  178. cfg.Region = v
  179. }
  180. return nil
  181. }
  182. // SharedConfigLoadError is an error for the shared config file failed to load.
  183. type SharedConfigLoadError struct {
  184. Filename string
  185. Err error
  186. }
  187. // Code is the short id of the error.
  188. func (e SharedConfigLoadError) Code() string {
  189. return "SharedConfigLoadError"
  190. }
  191. // Message is the description of the error
  192. func (e SharedConfigLoadError) Message() string {
  193. return fmt.Sprintf("failed to load config file, %s", e.Filename)
  194. }
  195. // OrigErr is the underlying error that caused the failure.
  196. func (e SharedConfigLoadError) OrigErr() error {
  197. return e.Err
  198. }
  199. // Error satisfies the error interface.
  200. func (e SharedConfigLoadError) Error() string {
  201. return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
  202. }
  203. // SharedConfigProfileNotExistsError is an error for the shared config when
  204. // the profile was not find in the config file.
  205. type SharedConfigProfileNotExistsError struct {
  206. Profile string
  207. Err error
  208. }
  209. // Code is the short id of the error.
  210. func (e SharedConfigProfileNotExistsError) Code() string {
  211. return "SharedConfigProfileNotExistsError"
  212. }
  213. // Message is the description of the error
  214. func (e SharedConfigProfileNotExistsError) Message() string {
  215. return fmt.Sprintf("failed to get profile, %s", e.Profile)
  216. }
  217. // OrigErr is the underlying error that caused the failure.
  218. func (e SharedConfigProfileNotExistsError) OrigErr() error {
  219. return e.Err
  220. }
  221. // Error satisfies the error interface.
  222. func (e SharedConfigProfileNotExistsError) Error() string {
  223. return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
  224. }
  225. // SharedConfigAssumeRoleError is an error for the shared config when the
  226. // profile contains assume role information, but that information is invalid
  227. // or not complete.
  228. type SharedConfigAssumeRoleError struct {
  229. RoleARN string
  230. }
  231. // Code is the short id of the error.
  232. func (e SharedConfigAssumeRoleError) Code() string {
  233. return "SharedConfigAssumeRoleError"
  234. }
  235. // Message is the description of the error
  236. func (e SharedConfigAssumeRoleError) Message() string {
  237. return fmt.Sprintf("failed to load assume role for %s, source profile has no shared credentials",
  238. e.RoleARN)
  239. }
  240. // OrigErr is the underlying error that caused the failure.
  241. func (e SharedConfigAssumeRoleError) OrigErr() error {
  242. return nil
  243. }
  244. // Error satisfies the error interface.
  245. func (e SharedConfigAssumeRoleError) Error() string {
  246. return awserr.SprintError(e.Code(), e.Message(), "", nil)
  247. }