shared_config.go 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384
  1. package config
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "time"
  13. "github.com/aws/aws-sdk-go-v2/aws"
  14. "github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
  15. "github.com/aws/aws-sdk-go-v2/internal/ini"
  16. "github.com/aws/aws-sdk-go-v2/internal/shareddefaults"
  17. "github.com/aws/smithy-go/logging"
  18. )
  19. const (
  20. // Prefix to use for filtering profiles. The profile prefix should only
  21. // exist in the shared config file, not the credentials file.
  22. profilePrefix = `profile `
  23. // Prefix to be used for SSO sections. These are supposed to only exist in
  24. // the shared config file, not the credentials file.
  25. ssoSectionPrefix = `sso-session `
  26. // string equivalent for boolean
  27. endpointDiscoveryDisabled = `false`
  28. endpointDiscoveryEnabled = `true`
  29. endpointDiscoveryAuto = `auto`
  30. // Static Credentials group
  31. accessKeyIDKey = `aws_access_key_id` // group required
  32. secretAccessKey = `aws_secret_access_key` // group required
  33. sessionTokenKey = `aws_session_token` // optional
  34. // Assume Role Credentials group
  35. roleArnKey = `role_arn` // group required
  36. sourceProfileKey = `source_profile` // group required
  37. credentialSourceKey = `credential_source` // group required (or source_profile)
  38. externalIDKey = `external_id` // optional
  39. mfaSerialKey = `mfa_serial` // optional
  40. roleSessionNameKey = `role_session_name` // optional
  41. roleDurationSecondsKey = "duration_seconds" // optional
  42. // AWS Single Sign-On (AWS SSO) group
  43. ssoSessionNameKey = "sso_session"
  44. ssoRegionKey = "sso_region"
  45. ssoStartURLKey = "sso_start_url"
  46. ssoAccountIDKey = "sso_account_id"
  47. ssoRoleNameKey = "sso_role_name"
  48. // Additional Config fields
  49. regionKey = `region`
  50. // endpoint discovery group
  51. enableEndpointDiscoveryKey = `endpoint_discovery_enabled` // optional
  52. // External Credential process
  53. credentialProcessKey = `credential_process` // optional
  54. // Web Identity Token File
  55. webIdentityTokenFileKey = `web_identity_token_file` // optional
  56. // S3 ARN Region Usage
  57. s3UseARNRegionKey = "s3_use_arn_region"
  58. ec2MetadataServiceEndpointModeKey = "ec2_metadata_service_endpoint_mode"
  59. ec2MetadataServiceEndpointKey = "ec2_metadata_service_endpoint"
  60. // Use DualStack Endpoint Resolution
  61. useDualStackEndpoint = "use_dualstack_endpoint"
  62. // DefaultSharedConfigProfile is the default profile to be used when
  63. // loading configuration from the config files if another profile name
  64. // is not provided.
  65. DefaultSharedConfigProfile = `default`
  66. // S3 Disable Multi-Region AccessPoints
  67. s3DisableMultiRegionAccessPointsKey = `s3_disable_multiregion_access_points`
  68. useFIPSEndpointKey = "use_fips_endpoint"
  69. defaultsModeKey = "defaults_mode"
  70. // Retry options
  71. retryMaxAttemptsKey = "max_attempts"
  72. retryModeKey = "retry_mode"
  73. caBundleKey = "ca_bundle"
  74. )
  75. // defaultSharedConfigProfile allows for swapping the default profile for testing
  76. var defaultSharedConfigProfile = DefaultSharedConfigProfile
  77. // DefaultSharedCredentialsFilename returns the SDK's default file path
  78. // for the shared credentials file.
  79. //
  80. // Builds the shared config file path based on the OS's platform.
  81. //
  82. // - Linux/Unix: $HOME/.aws/credentials
  83. // - Windows: %USERPROFILE%\.aws\credentials
  84. func DefaultSharedCredentialsFilename() string {
  85. return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "credentials")
  86. }
  87. // DefaultSharedConfigFilename returns the SDK's default file path for
  88. // the shared config file.
  89. //
  90. // Builds the shared config file path based on the OS's platform.
  91. //
  92. // - Linux/Unix: $HOME/.aws/config
  93. // - Windows: %USERPROFILE%\.aws\config
  94. func DefaultSharedConfigFilename() string {
  95. return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "config")
  96. }
  97. // DefaultSharedConfigFiles is a slice of the default shared config files that
  98. // the will be used in order to load the SharedConfig.
  99. var DefaultSharedConfigFiles = []string{
  100. DefaultSharedConfigFilename(),
  101. }
  102. // DefaultSharedCredentialsFiles is a slice of the default shared credentials
  103. // files that the will be used in order to load the SharedConfig.
  104. var DefaultSharedCredentialsFiles = []string{
  105. DefaultSharedCredentialsFilename(),
  106. }
  107. // SSOSession provides the shared configuration parameters of the sso-session
  108. // section.
  109. type SSOSession struct {
  110. Name string
  111. SSORegion string
  112. SSOStartURL string
  113. }
  114. func (s *SSOSession) setFromIniSection(section ini.Section) {
  115. updateString(&s.Name, section, ssoSessionNameKey)
  116. updateString(&s.SSORegion, section, ssoRegionKey)
  117. updateString(&s.SSOStartURL, section, ssoStartURLKey)
  118. }
  119. // SharedConfig represents the configuration fields of the SDK config files.
  120. type SharedConfig struct {
  121. Profile string
  122. // Credentials values from the config file. Both aws_access_key_id
  123. // and aws_secret_access_key must be provided together in the same file
  124. // to be considered valid. The values will be ignored if not a complete group.
  125. // aws_session_token is an optional field that can be provided if both of the
  126. // other two fields are also provided.
  127. //
  128. // aws_access_key_id
  129. // aws_secret_access_key
  130. // aws_session_token
  131. Credentials aws.Credentials
  132. CredentialSource string
  133. CredentialProcess string
  134. WebIdentityTokenFile string
  135. // SSO session options
  136. SSOSessionName string
  137. SSOSession *SSOSession
  138. // Legacy SSO session options
  139. SSORegion string
  140. SSOStartURL string
  141. // SSO fields not used
  142. SSOAccountID string
  143. SSORoleName string
  144. RoleARN string
  145. ExternalID string
  146. MFASerial string
  147. RoleSessionName string
  148. RoleDurationSeconds *time.Duration
  149. SourceProfileName string
  150. Source *SharedConfig
  151. // Region is the region the SDK should use for looking up AWS service endpoints
  152. // and signing requests.
  153. //
  154. // region = us-west-2
  155. Region string
  156. // EnableEndpointDiscovery can be enabled or disabled in the shared config
  157. // by setting endpoint_discovery_enabled to true, or false respectively.
  158. //
  159. // endpoint_discovery_enabled = true
  160. EnableEndpointDiscovery aws.EndpointDiscoveryEnableState
  161. // Specifies if the S3 service should allow ARNs to direct the region
  162. // the client's requests are sent to.
  163. //
  164. // s3_use_arn_region=true
  165. S3UseARNRegion *bool
  166. // Specifies the EC2 Instance Metadata Service default endpoint selection
  167. // mode (IPv4 or IPv6)
  168. //
  169. // ec2_metadata_service_endpoint_mode=IPv6
  170. EC2IMDSEndpointMode imds.EndpointModeState
  171. // Specifies the EC2 Instance Metadata Service endpoint to use. If
  172. // specified it overrides EC2IMDSEndpointMode.
  173. //
  174. // ec2_metadata_service_endpoint=http://fd00:ec2::254
  175. EC2IMDSEndpoint string
  176. // Specifies if the S3 service should disable support for Multi-Region
  177. // access-points
  178. //
  179. // s3_disable_multiregion_access_points=true
  180. S3DisableMultiRegionAccessPoints *bool
  181. // Specifies that SDK clients must resolve a dual-stack endpoint for
  182. // services.
  183. //
  184. // use_dualstack_endpoint=true
  185. UseDualStackEndpoint aws.DualStackEndpointState
  186. // Specifies that SDK clients must resolve a FIPS endpoint for
  187. // services.
  188. //
  189. // use_fips_endpoint=true
  190. UseFIPSEndpoint aws.FIPSEndpointState
  191. // Specifies which defaults mode should be used by services.
  192. //
  193. // defaults_mode=standard
  194. DefaultsMode aws.DefaultsMode
  195. // Specifies the maximum number attempts an API client will call an
  196. // operation that fails with a retryable error.
  197. //
  198. // max_attempts=3
  199. RetryMaxAttempts int
  200. // Specifies the retry model the API client will be created with.
  201. //
  202. // retry_mode=standard
  203. RetryMode aws.RetryMode
  204. // Sets the path to a custom Credentials Authority (CA) Bundle PEM file
  205. // that the SDK will use instead of the system's root CA bundle. Only use
  206. // this if you want to configure the SDK to use a custom set of CAs.
  207. //
  208. // Enabling this option will attempt to merge the Transport into the SDK's
  209. // HTTP client. If the client's Transport is not a http.Transport an error
  210. // will be returned. If the Transport's TLS config is set this option will
  211. // cause the SDK to overwrite the Transport's TLS config's RootCAs value.
  212. //
  213. // Setting a custom HTTPClient in the aws.Config options will override this
  214. // setting. To use this option and custom HTTP client, the HTTP client
  215. // needs to be provided when creating the config. Not the service client.
  216. //
  217. // ca_bundle=$HOME/my_custom_ca_bundle
  218. CustomCABundle string
  219. }
  220. func (c SharedConfig) getDefaultsMode(ctx context.Context) (value aws.DefaultsMode, ok bool, err error) {
  221. if len(c.DefaultsMode) == 0 {
  222. return "", false, nil
  223. }
  224. return c.DefaultsMode, true, nil
  225. }
  226. // GetRetryMaxAttempts returns the maximum number of attempts an API client
  227. // created Retryer should attempt an operation call before failing.
  228. func (c SharedConfig) GetRetryMaxAttempts(ctx context.Context) (value int, ok bool, err error) {
  229. if c.RetryMaxAttempts == 0 {
  230. return 0, false, nil
  231. }
  232. return c.RetryMaxAttempts, true, nil
  233. }
  234. // GetRetryMode returns the model the API client should create its Retryer in.
  235. func (c SharedConfig) GetRetryMode(ctx context.Context) (value aws.RetryMode, ok bool, err error) {
  236. if len(c.RetryMode) == 0 {
  237. return "", false, nil
  238. }
  239. return c.RetryMode, true, nil
  240. }
  241. // GetS3UseARNRegion returns if the S3 service should allow ARNs to direct the region
  242. // the client's requests are sent to.
  243. func (c SharedConfig) GetS3UseARNRegion(ctx context.Context) (value, ok bool, err error) {
  244. if c.S3UseARNRegion == nil {
  245. return false, false, nil
  246. }
  247. return *c.S3UseARNRegion, true, nil
  248. }
  249. // GetEnableEndpointDiscovery returns if the enable_endpoint_discovery is set.
  250. func (c SharedConfig) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, ok bool, err error) {
  251. if c.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset {
  252. return aws.EndpointDiscoveryUnset, false, nil
  253. }
  254. return c.EnableEndpointDiscovery, true, nil
  255. }
  256. // GetS3DisableMultiRegionAccessPoints returns if the S3 service should disable support for Multi-Region
  257. // access-points.
  258. func (c SharedConfig) GetS3DisableMultiRegionAccessPoints(ctx context.Context) (value, ok bool, err error) {
  259. if c.S3DisableMultiRegionAccessPoints == nil {
  260. return false, false, nil
  261. }
  262. return *c.S3DisableMultiRegionAccessPoints, true, nil
  263. }
  264. // GetRegion returns the region for the profile if a region is set.
  265. func (c SharedConfig) getRegion(ctx context.Context) (string, bool, error) {
  266. if len(c.Region) == 0 {
  267. return "", false, nil
  268. }
  269. return c.Region, true, nil
  270. }
  271. // GetCredentialsProvider returns the credentials for a profile if they were set.
  272. func (c SharedConfig) getCredentialsProvider() (aws.Credentials, bool, error) {
  273. return c.Credentials, true, nil
  274. }
  275. // GetEC2IMDSEndpointMode implements a EC2IMDSEndpointMode option resolver interface.
  276. func (c SharedConfig) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) {
  277. if c.EC2IMDSEndpointMode == imds.EndpointModeStateUnset {
  278. return imds.EndpointModeStateUnset, false, nil
  279. }
  280. return c.EC2IMDSEndpointMode, true, nil
  281. }
  282. // GetEC2IMDSEndpoint implements a EC2IMDSEndpoint option resolver interface.
  283. func (c SharedConfig) GetEC2IMDSEndpoint() (string, bool, error) {
  284. if len(c.EC2IMDSEndpoint) == 0 {
  285. return "", false, nil
  286. }
  287. return c.EC2IMDSEndpoint, true, nil
  288. }
  289. // GetUseDualStackEndpoint returns whether the service's dual-stack endpoint should be
  290. // used for requests.
  291. func (c SharedConfig) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) {
  292. if c.UseDualStackEndpoint == aws.DualStackEndpointStateUnset {
  293. return aws.DualStackEndpointStateUnset, false, nil
  294. }
  295. return c.UseDualStackEndpoint, true, nil
  296. }
  297. // GetUseFIPSEndpoint returns whether the service's FIPS endpoint should be
  298. // used for requests.
  299. func (c SharedConfig) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) {
  300. if c.UseFIPSEndpoint == aws.FIPSEndpointStateUnset {
  301. return aws.FIPSEndpointStateUnset, false, nil
  302. }
  303. return c.UseFIPSEndpoint, true, nil
  304. }
  305. // GetCustomCABundle returns the custom CA bundle's PEM bytes if the file was
  306. func (c SharedConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) {
  307. if len(c.CustomCABundle) == 0 {
  308. return nil, false, nil
  309. }
  310. b, err := ioutil.ReadFile(c.CustomCABundle)
  311. if err != nil {
  312. return nil, false, err
  313. }
  314. return bytes.NewReader(b), true, nil
  315. }
  316. // loadSharedConfigIgnoreNotExist is an alias for loadSharedConfig with the
  317. // addition of ignoring when none of the files exist or when the profile
  318. // is not found in any of the files.
  319. func loadSharedConfigIgnoreNotExist(ctx context.Context, configs configs) (Config, error) {
  320. cfg, err := loadSharedConfig(ctx, configs)
  321. if err != nil {
  322. if _, ok := err.(SharedConfigProfileNotExistError); ok {
  323. return SharedConfig{}, nil
  324. }
  325. return nil, err
  326. }
  327. return cfg, nil
  328. }
  329. // loadSharedConfig uses the configs passed in to load the SharedConfig from file
  330. // The file names and profile name are sourced from the configs.
  331. //
  332. // If profile name is not provided DefaultSharedConfigProfile (default) will
  333. // be used.
  334. //
  335. // If shared config filenames are not provided DefaultSharedConfigFiles will
  336. // be used.
  337. //
  338. // Config providers used:
  339. // * sharedConfigProfileProvider
  340. // * sharedConfigFilesProvider
  341. func loadSharedConfig(ctx context.Context, configs configs) (Config, error) {
  342. var profile string
  343. var configFiles []string
  344. var credentialsFiles []string
  345. var ok bool
  346. var err error
  347. profile, ok, err = getSharedConfigProfile(ctx, configs)
  348. if err != nil {
  349. return nil, err
  350. }
  351. if !ok {
  352. profile = defaultSharedConfigProfile
  353. }
  354. configFiles, ok, err = getSharedConfigFiles(ctx, configs)
  355. if err != nil {
  356. return nil, err
  357. }
  358. credentialsFiles, ok, err = getSharedCredentialsFiles(ctx, configs)
  359. if err != nil {
  360. return nil, err
  361. }
  362. // setup logger if log configuration warning is seti
  363. var logger logging.Logger
  364. logWarnings, found, err := getLogConfigurationWarnings(ctx, configs)
  365. if err != nil {
  366. return SharedConfig{}, err
  367. }
  368. if found && logWarnings {
  369. logger, found, err = getLogger(ctx, configs)
  370. if err != nil {
  371. return SharedConfig{}, err
  372. }
  373. if !found {
  374. logger = logging.NewStandardLogger(os.Stderr)
  375. }
  376. }
  377. return LoadSharedConfigProfile(ctx, profile,
  378. func(o *LoadSharedConfigOptions) {
  379. o.Logger = logger
  380. o.ConfigFiles = configFiles
  381. o.CredentialsFiles = credentialsFiles
  382. },
  383. )
  384. }
  385. // LoadSharedConfigOptions struct contains optional values that can be used to load the config.
  386. type LoadSharedConfigOptions struct {
  387. // CredentialsFiles are the shared credentials files
  388. CredentialsFiles []string
  389. // ConfigFiles are the shared config files
  390. ConfigFiles []string
  391. // Logger is the logger used to log shared config behavior
  392. Logger logging.Logger
  393. }
  394. // LoadSharedConfigProfile retrieves the configuration from the list of files
  395. // using the profile provided. The order the files are listed will determine
  396. // precedence. Values in subsequent files will overwrite values defined in
  397. // earlier files.
  398. //
  399. // For example, given two files A and B. Both define credentials. If the order
  400. // of the files are A then B, B's credential values will be used instead of A's.
  401. //
  402. // If config files are not set, SDK will default to using a file at location `.aws/config` if present.
  403. // If credentials files are not set, SDK will default to using a file at location `.aws/credentials` if present.
  404. // No default files are set, if files set to an empty slice.
  405. //
  406. // You can read more about shared config and credentials file location at
  407. // https://docs.aws.amazon.com/credref/latest/refdocs/file-location.html#file-location
  408. func LoadSharedConfigProfile(ctx context.Context, profile string, optFns ...func(*LoadSharedConfigOptions)) (SharedConfig, error) {
  409. var option LoadSharedConfigOptions
  410. for _, fn := range optFns {
  411. fn(&option)
  412. }
  413. if option.ConfigFiles == nil {
  414. option.ConfigFiles = DefaultSharedConfigFiles
  415. }
  416. if option.CredentialsFiles == nil {
  417. option.CredentialsFiles = DefaultSharedCredentialsFiles
  418. }
  419. // load shared configuration sections from shared configuration INI options
  420. configSections, err := loadIniFiles(option.ConfigFiles)
  421. if err != nil {
  422. return SharedConfig{}, err
  423. }
  424. // check for profile prefix and drop duplicates or invalid profiles
  425. err = processConfigSections(ctx, &configSections, option.Logger)
  426. if err != nil {
  427. return SharedConfig{}, err
  428. }
  429. // load shared credentials sections from shared credentials INI options
  430. credentialsSections, err := loadIniFiles(option.CredentialsFiles)
  431. if err != nil {
  432. return SharedConfig{}, err
  433. }
  434. // check for profile prefix and drop duplicates or invalid profiles
  435. err = processCredentialsSections(ctx, &credentialsSections, option.Logger)
  436. if err != nil {
  437. return SharedConfig{}, err
  438. }
  439. err = mergeSections(&configSections, credentialsSections)
  440. if err != nil {
  441. return SharedConfig{}, err
  442. }
  443. cfg := SharedConfig{}
  444. profiles := map[string]struct{}{}
  445. if err = cfg.setFromIniSections(profiles, profile, configSections, option.Logger); err != nil {
  446. return SharedConfig{}, err
  447. }
  448. return cfg, nil
  449. }
  450. func processConfigSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error {
  451. skipSections := map[string]struct{}{}
  452. for _, section := range sections.List() {
  453. if _, ok := skipSections[section]; ok {
  454. continue
  455. }
  456. // drop sections from config file that do not have expected prefixes.
  457. switch {
  458. case strings.HasPrefix(section, profilePrefix):
  459. // Rename sections to remove "profile " prefixing to match with
  460. // credentials file. If default is already present, it will be
  461. // dropped.
  462. newName, err := renameProfileSection(section, sections, logger)
  463. if err != nil {
  464. return fmt.Errorf("failed to rename profile section, %w", err)
  465. }
  466. skipSections[newName] = struct{}{}
  467. case strings.HasPrefix(section, ssoSectionPrefix):
  468. case strings.EqualFold(section, "default"):
  469. default:
  470. // drop this section, as invalid profile name
  471. sections.DeleteSection(section)
  472. if logger != nil {
  473. logger.Logf(logging.Debug, "A profile defined with name `%v` is ignored. "+
  474. "For use within a shared configuration file, "+
  475. "a non-default profile must have `profile ` "+
  476. "prefixed to the profile name.",
  477. section,
  478. )
  479. }
  480. }
  481. }
  482. return nil
  483. }
  484. func renameProfileSection(section string, sections *ini.Sections, logger logging.Logger) (string, error) {
  485. v, ok := sections.GetSection(section)
  486. if !ok {
  487. return "", fmt.Errorf("error processing profiles within the shared configuration files")
  488. }
  489. // delete section with profile as prefix
  490. sections.DeleteSection(section)
  491. // set the value to non-prefixed name in sections.
  492. section = strings.TrimPrefix(section, profilePrefix)
  493. if sections.HasSection(section) {
  494. oldSection, _ := sections.GetSection(section)
  495. v.Logs = append(v.Logs,
  496. fmt.Sprintf("A non-default profile not prefixed with `profile ` found in %s, "+
  497. "overriding non-default profile from %s",
  498. v.SourceFile, oldSection.SourceFile))
  499. sections.DeleteSection(section)
  500. }
  501. // assign non-prefixed name to section
  502. v.Name = section
  503. sections.SetSection(section, v)
  504. return section, nil
  505. }
  506. func processCredentialsSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error {
  507. for _, section := range sections.List() {
  508. // drop profiles with prefix for credential files
  509. if strings.HasPrefix(section, profilePrefix) {
  510. // drop this section, as invalid profile name
  511. sections.DeleteSection(section)
  512. if logger != nil {
  513. logger.Logf(logging.Debug,
  514. "The profile defined with name `%v` is ignored. A profile with the `profile ` prefix is invalid "+
  515. "for the shared credentials file.\n",
  516. section,
  517. )
  518. }
  519. }
  520. }
  521. return nil
  522. }
  523. func loadIniFiles(filenames []string) (ini.Sections, error) {
  524. mergedSections := ini.NewSections()
  525. for _, filename := range filenames {
  526. sections, err := ini.OpenFile(filename)
  527. var v *ini.UnableToReadFile
  528. if ok := errors.As(err, &v); ok {
  529. // Skip files which can't be opened and read for whatever reason.
  530. // We treat such files as empty, and do not fall back to other locations.
  531. continue
  532. } else if err != nil {
  533. return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err}
  534. }
  535. // mergeSections into mergedSections
  536. err = mergeSections(&mergedSections, sections)
  537. if err != nil {
  538. return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err}
  539. }
  540. }
  541. return mergedSections, nil
  542. }
  543. // mergeSections merges source section properties into destination section properties
  544. func mergeSections(dst *ini.Sections, src ini.Sections) error {
  545. for _, sectionName := range src.List() {
  546. srcSection, _ := src.GetSection(sectionName)
  547. if (!srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey)) ||
  548. (srcSection.Has(accessKeyIDKey) && !srcSection.Has(secretAccessKey)) {
  549. srcSection.Errors = append(srcSection.Errors,
  550. fmt.Errorf("partial credentials found for profile %v", sectionName))
  551. }
  552. if !dst.HasSection(sectionName) {
  553. dst.SetSection(sectionName, srcSection)
  554. continue
  555. }
  556. // merge with destination srcSection
  557. dstSection, _ := dst.GetSection(sectionName)
  558. // errors should be overriden if any
  559. dstSection.Errors = srcSection.Errors
  560. // Access key id update
  561. if srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey) {
  562. accessKey := srcSection.String(accessKeyIDKey)
  563. secretKey := srcSection.String(secretAccessKey)
  564. if dstSection.Has(accessKeyIDKey) {
  565. dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, accessKeyIDKey,
  566. dstSection.SourceFile[accessKeyIDKey], srcSection.SourceFile[accessKeyIDKey]))
  567. }
  568. // update access key
  569. v, err := ini.NewStringValue(accessKey)
  570. if err != nil {
  571. return fmt.Errorf("error merging access key, %w", err)
  572. }
  573. dstSection.UpdateValue(accessKeyIDKey, v)
  574. // update secret key
  575. v, err = ini.NewStringValue(secretKey)
  576. if err != nil {
  577. return fmt.Errorf("error merging secret key, %w", err)
  578. }
  579. dstSection.UpdateValue(secretAccessKey, v)
  580. // update session token
  581. if err = mergeStringKey(&srcSection, &dstSection, sectionName, sessionTokenKey); err != nil {
  582. return err
  583. }
  584. // update source file to reflect where the static creds came from
  585. dstSection.UpdateSourceFile(accessKeyIDKey, srcSection.SourceFile[accessKeyIDKey])
  586. dstSection.UpdateSourceFile(secretAccessKey, srcSection.SourceFile[secretAccessKey])
  587. }
  588. stringKeys := []string{
  589. roleArnKey,
  590. sourceProfileKey,
  591. credentialSourceKey,
  592. externalIDKey,
  593. mfaSerialKey,
  594. roleSessionNameKey,
  595. regionKey,
  596. enableEndpointDiscoveryKey,
  597. credentialProcessKey,
  598. webIdentityTokenFileKey,
  599. s3UseARNRegionKey,
  600. s3DisableMultiRegionAccessPointsKey,
  601. ec2MetadataServiceEndpointModeKey,
  602. ec2MetadataServiceEndpointKey,
  603. useDualStackEndpoint,
  604. useFIPSEndpointKey,
  605. defaultsModeKey,
  606. retryModeKey,
  607. caBundleKey,
  608. ssoSessionNameKey,
  609. ssoAccountIDKey,
  610. ssoRegionKey,
  611. ssoRoleNameKey,
  612. ssoStartURLKey,
  613. }
  614. for i := range stringKeys {
  615. if err := mergeStringKey(&srcSection, &dstSection, sectionName, stringKeys[i]); err != nil {
  616. return err
  617. }
  618. }
  619. intKeys := []string{
  620. roleDurationSecondsKey,
  621. retryMaxAttemptsKey,
  622. }
  623. for i := range intKeys {
  624. if err := mergeIntKey(&srcSection, &dstSection, sectionName, intKeys[i]); err != nil {
  625. return err
  626. }
  627. }
  628. // set srcSection on dst srcSection
  629. *dst = dst.SetSection(sectionName, dstSection)
  630. }
  631. return nil
  632. }
  633. func mergeStringKey(srcSection *ini.Section, dstSection *ini.Section, sectionName, key string) error {
  634. if srcSection.Has(key) {
  635. srcValue := srcSection.String(key)
  636. val, err := ini.NewStringValue(srcValue)
  637. if err != nil {
  638. return fmt.Errorf("error merging %s, %w", key, err)
  639. }
  640. if dstSection.Has(key) {
  641. dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, key,
  642. dstSection.SourceFile[key], srcSection.SourceFile[key]))
  643. }
  644. dstSection.UpdateValue(key, val)
  645. dstSection.UpdateSourceFile(key, srcSection.SourceFile[key])
  646. }
  647. return nil
  648. }
  649. func mergeIntKey(srcSection *ini.Section, dstSection *ini.Section, sectionName, key string) error {
  650. if srcSection.Has(key) {
  651. srcValue := srcSection.Int(key)
  652. v, err := ini.NewIntValue(srcValue)
  653. if err != nil {
  654. return fmt.Errorf("error merging %s, %w", key, err)
  655. }
  656. if dstSection.Has(key) {
  657. dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, key,
  658. dstSection.SourceFile[key], srcSection.SourceFile[key]))
  659. }
  660. dstSection.UpdateValue(key, v)
  661. dstSection.UpdateSourceFile(key, srcSection.SourceFile[key])
  662. }
  663. return nil
  664. }
  665. func newMergeKeyLogMessage(sectionName, key, dstSourceFile, srcSourceFile string) string {
  666. return fmt.Sprintf("For profile: %v, overriding %v value, defined in %v "+
  667. "with a %v value found in a duplicate profile defined at file %v. \n",
  668. sectionName, key, dstSourceFile, key, srcSourceFile)
  669. }
  670. // Returns an error if all of the files fail to load. If at least one file is
  671. // successfully loaded and contains the profile, no error will be returned.
  672. func (c *SharedConfig) setFromIniSections(profiles map[string]struct{}, profile string,
  673. sections ini.Sections, logger logging.Logger) error {
  674. c.Profile = profile
  675. section, ok := sections.GetSection(profile)
  676. if !ok {
  677. return SharedConfigProfileNotExistError{
  678. Profile: profile,
  679. }
  680. }
  681. // if logs are appended to the section, log them
  682. if section.Logs != nil && logger != nil {
  683. for _, log := range section.Logs {
  684. logger.Logf(logging.Debug, log)
  685. }
  686. }
  687. // set config from the provided INI section
  688. err := c.setFromIniSection(profile, section)
  689. if err != nil {
  690. return fmt.Errorf("error fetching config from profile, %v, %w", profile, err)
  691. }
  692. if _, ok := profiles[profile]; ok {
  693. // if this is the second instance of the profile the Assume Role
  694. // options must be cleared because they are only valid for the
  695. // first reference of a profile. The self linked instance of the
  696. // profile only have credential provider options.
  697. c.clearAssumeRoleOptions()
  698. } else {
  699. // First time a profile has been seen. Assert if the credential type
  700. // requires a role ARN, the ARN is also set
  701. if err := c.validateCredentialsConfig(profile); err != nil {
  702. return err
  703. }
  704. }
  705. // if not top level profile and has credentials, return with credentials.
  706. if len(profiles) != 0 && c.Credentials.HasKeys() {
  707. return nil
  708. }
  709. profiles[profile] = struct{}{}
  710. // validate no colliding credentials type are present
  711. if err := c.validateCredentialType(); err != nil {
  712. return err
  713. }
  714. // Link source profiles for assume roles
  715. if len(c.SourceProfileName) != 0 {
  716. // Linked profile via source_profile ignore credential provider
  717. // options, the source profile must provide the credentials.
  718. c.clearCredentialOptions()
  719. srcCfg := &SharedConfig{}
  720. err := srcCfg.setFromIniSections(profiles, c.SourceProfileName, sections, logger)
  721. if err != nil {
  722. // SourceProfileName that doesn't exist is an error in configuration.
  723. if _, ok := err.(SharedConfigProfileNotExistError); ok {
  724. err = SharedConfigAssumeRoleError{
  725. RoleARN: c.RoleARN,
  726. Profile: c.SourceProfileName,
  727. Err: err,
  728. }
  729. }
  730. return err
  731. }
  732. if !srcCfg.hasCredentials() {
  733. return SharedConfigAssumeRoleError{
  734. RoleARN: c.RoleARN,
  735. Profile: c.SourceProfileName,
  736. }
  737. }
  738. c.Source = srcCfg
  739. }
  740. // If the profile contains an SSO session parameter, the session MUST exist
  741. // as a section in the config file. Load the SSO session using the name
  742. // provided. If the session section is not found or incomplete an error
  743. // will be returned.
  744. if c.hasSSOTokenProviderConfiguration() {
  745. section, ok := sections.GetSection(ssoSectionPrefix + strings.TrimSpace(c.SSOSessionName))
  746. if !ok {
  747. return fmt.Errorf("failed to find SSO session section, %v", c.SSOSessionName)
  748. }
  749. var ssoSession SSOSession
  750. ssoSession.setFromIniSection(section)
  751. ssoSession.Name = c.SSOSessionName
  752. c.SSOSession = &ssoSession
  753. }
  754. return nil
  755. }
  756. // setFromIniSection loads the configuration from the profile section defined in
  757. // the provided INI file. A SharedConfig pointer type value is used so that
  758. // multiple config file loadings can be chained.
  759. //
  760. // Only loads complete logically grouped values, and will not set fields in cfg
  761. // for incomplete grouped values in the config. Such as credentials. For example
  762. // if a config file only includes aws_access_key_id but no aws_secret_access_key
  763. // the aws_access_key_id will be ignored.
  764. func (c *SharedConfig) setFromIniSection(profile string, section ini.Section) error {
  765. if len(section.Name) == 0 {
  766. sources := make([]string, 0)
  767. for _, v := range section.SourceFile {
  768. sources = append(sources, v)
  769. }
  770. return fmt.Errorf("parsing error : could not find profile section name after processing files: %v", sources)
  771. }
  772. if len(section.Errors) != 0 {
  773. var errStatement string
  774. for i, e := range section.Errors {
  775. errStatement = fmt.Sprintf("%d, %v\n", i+1, e.Error())
  776. }
  777. return fmt.Errorf("Error using profile: \n %v", errStatement)
  778. }
  779. // Assume Role
  780. updateString(&c.RoleARN, section, roleArnKey)
  781. updateString(&c.ExternalID, section, externalIDKey)
  782. updateString(&c.MFASerial, section, mfaSerialKey)
  783. updateString(&c.RoleSessionName, section, roleSessionNameKey)
  784. updateString(&c.SourceProfileName, section, sourceProfileKey)
  785. updateString(&c.CredentialSource, section, credentialSourceKey)
  786. updateString(&c.Region, section, regionKey)
  787. // AWS Single Sign-On (AWS SSO)
  788. // SSO session options
  789. updateString(&c.SSOSessionName, section, ssoSessionNameKey)
  790. // Legacy SSO session options
  791. updateString(&c.SSORegion, section, ssoRegionKey)
  792. updateString(&c.SSOStartURL, section, ssoStartURLKey)
  793. // SSO fields not used
  794. updateString(&c.SSOAccountID, section, ssoAccountIDKey)
  795. updateString(&c.SSORoleName, section, ssoRoleNameKey)
  796. if section.Has(roleDurationSecondsKey) {
  797. d := time.Duration(section.Int(roleDurationSecondsKey)) * time.Second
  798. c.RoleDurationSeconds = &d
  799. }
  800. updateString(&c.CredentialProcess, section, credentialProcessKey)
  801. updateString(&c.WebIdentityTokenFile, section, webIdentityTokenFileKey)
  802. updateEndpointDiscoveryType(&c.EnableEndpointDiscovery, section, enableEndpointDiscoveryKey)
  803. updateBoolPtr(&c.S3UseARNRegion, section, s3UseARNRegionKey)
  804. updateBoolPtr(&c.S3DisableMultiRegionAccessPoints, section, s3DisableMultiRegionAccessPointsKey)
  805. if err := updateEC2MetadataServiceEndpointMode(&c.EC2IMDSEndpointMode, section, ec2MetadataServiceEndpointModeKey); err != nil {
  806. return fmt.Errorf("failed to load %s from shared config, %v", ec2MetadataServiceEndpointModeKey, err)
  807. }
  808. updateString(&c.EC2IMDSEndpoint, section, ec2MetadataServiceEndpointKey)
  809. updateUseDualStackEndpoint(&c.UseDualStackEndpoint, section, useDualStackEndpoint)
  810. updateUseFIPSEndpoint(&c.UseFIPSEndpoint, section, useFIPSEndpointKey)
  811. if err := updateDefaultsMode(&c.DefaultsMode, section, defaultsModeKey); err != nil {
  812. return fmt.Errorf("failed to load %s from shared config, %w", defaultsModeKey, err)
  813. }
  814. if err := updateInt(&c.RetryMaxAttempts, section, retryMaxAttemptsKey); err != nil {
  815. return fmt.Errorf("failed to load %s from shared config, %w", retryMaxAttemptsKey, err)
  816. }
  817. if err := updateRetryMode(&c.RetryMode, section, retryModeKey); err != nil {
  818. return fmt.Errorf("failed to load %s from shared config, %w", retryModeKey, err)
  819. }
  820. updateString(&c.CustomCABundle, section, caBundleKey)
  821. // Shared Credentials
  822. creds := aws.Credentials{
  823. AccessKeyID: section.String(accessKeyIDKey),
  824. SecretAccessKey: section.String(secretAccessKey),
  825. SessionToken: section.String(sessionTokenKey),
  826. Source: fmt.Sprintf("SharedConfigCredentials: %s", section.SourceFile[accessKeyIDKey]),
  827. }
  828. if creds.HasKeys() {
  829. c.Credentials = creds
  830. }
  831. return nil
  832. }
  833. func updateDefaultsMode(mode *aws.DefaultsMode, section ini.Section, key string) error {
  834. if !section.Has(key) {
  835. return nil
  836. }
  837. value := section.String(key)
  838. if ok := mode.SetFromString(value); !ok {
  839. return fmt.Errorf("invalid value: %s", value)
  840. }
  841. return nil
  842. }
  843. func updateRetryMode(mode *aws.RetryMode, section ini.Section, key string) (err error) {
  844. if !section.Has(key) {
  845. return nil
  846. }
  847. value := section.String(key)
  848. if *mode, err = aws.ParseRetryMode(value); err != nil {
  849. return err
  850. }
  851. return nil
  852. }
  853. func updateEC2MetadataServiceEndpointMode(endpointMode *imds.EndpointModeState, section ini.Section, key string) error {
  854. if !section.Has(key) {
  855. return nil
  856. }
  857. value := section.String(key)
  858. return endpointMode.SetFromString(value)
  859. }
  860. func (c *SharedConfig) validateCredentialsConfig(profile string) error {
  861. if err := c.validateCredentialsRequireARN(profile); err != nil {
  862. return err
  863. }
  864. return nil
  865. }
  866. func (c *SharedConfig) validateCredentialsRequireARN(profile string) error {
  867. var credSource string
  868. switch {
  869. case len(c.SourceProfileName) != 0:
  870. credSource = sourceProfileKey
  871. case len(c.CredentialSource) != 0:
  872. credSource = credentialSourceKey
  873. case len(c.WebIdentityTokenFile) != 0:
  874. credSource = webIdentityTokenFileKey
  875. }
  876. if len(credSource) != 0 && len(c.RoleARN) == 0 {
  877. return CredentialRequiresARNError{
  878. Type: credSource,
  879. Profile: profile,
  880. }
  881. }
  882. return nil
  883. }
  884. func (c *SharedConfig) validateCredentialType() error {
  885. // Only one or no credential type can be defined.
  886. if !oneOrNone(
  887. len(c.SourceProfileName) != 0,
  888. len(c.CredentialSource) != 0,
  889. len(c.CredentialProcess) != 0,
  890. len(c.WebIdentityTokenFile) != 0,
  891. ) {
  892. return fmt.Errorf("only one credential type may be specified per profile: source profile, credential source, credential process, web identity token")
  893. }
  894. return nil
  895. }
  896. func (c *SharedConfig) validateSSOConfiguration() error {
  897. if c.hasSSOTokenProviderConfiguration() {
  898. err := c.validateSSOTokenProviderConfiguration()
  899. if err != nil {
  900. return err
  901. }
  902. return nil
  903. }
  904. if c.hasLegacySSOConfiguration() {
  905. err := c.validateLegacySSOConfiguration()
  906. if err != nil {
  907. return err
  908. }
  909. }
  910. return nil
  911. }
  912. func (c *SharedConfig) validateSSOTokenProviderConfiguration() error {
  913. var missing []string
  914. if len(c.SSOSessionName) == 0 {
  915. missing = append(missing, ssoSessionNameKey)
  916. }
  917. if c.SSOSession == nil {
  918. missing = append(missing, ssoSectionPrefix)
  919. } else {
  920. if len(c.SSOSession.SSORegion) == 0 {
  921. missing = append(missing, ssoRegionKey)
  922. }
  923. if len(c.SSOSession.SSOStartURL) == 0 {
  924. missing = append(missing, ssoStartURLKey)
  925. }
  926. }
  927. if len(missing) > 0 {
  928. return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s",
  929. c.Profile, strings.Join(missing, ", "))
  930. }
  931. if len(c.SSORegion) > 0 && c.SSORegion != c.SSOSession.SSORegion {
  932. return fmt.Errorf("%s in profile %q must match %s in %s", ssoRegionKey, c.Profile, ssoRegionKey, ssoSectionPrefix)
  933. }
  934. if len(c.SSOStartURL) > 0 && c.SSOStartURL != c.SSOSession.SSOStartURL {
  935. return fmt.Errorf("%s in profile %q must match %s in %s", ssoStartURLKey, c.Profile, ssoStartURLKey, ssoSectionPrefix)
  936. }
  937. return nil
  938. }
  939. func (c *SharedConfig) validateLegacySSOConfiguration() error {
  940. var missing []string
  941. if len(c.SSORegion) == 0 {
  942. missing = append(missing, ssoRegionKey)
  943. }
  944. if len(c.SSOStartURL) == 0 {
  945. missing = append(missing, ssoStartURLKey)
  946. }
  947. if len(c.SSOAccountID) == 0 {
  948. missing = append(missing, ssoAccountIDKey)
  949. }
  950. if len(c.SSORoleName) == 0 {
  951. missing = append(missing, ssoRoleNameKey)
  952. }
  953. if len(missing) > 0 {
  954. return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s",
  955. c.Profile, strings.Join(missing, ", "))
  956. }
  957. return nil
  958. }
  959. func (c *SharedConfig) hasCredentials() bool {
  960. switch {
  961. case len(c.SourceProfileName) != 0:
  962. case len(c.CredentialSource) != 0:
  963. case len(c.CredentialProcess) != 0:
  964. case len(c.WebIdentityTokenFile) != 0:
  965. case c.hasSSOConfiguration():
  966. case c.Credentials.HasKeys():
  967. default:
  968. return false
  969. }
  970. return true
  971. }
  972. func (c *SharedConfig) hasSSOConfiguration() bool {
  973. return c.hasSSOTokenProviderConfiguration() || c.hasLegacySSOConfiguration()
  974. }
  975. func (c *SharedConfig) hasSSOTokenProviderConfiguration() bool {
  976. return len(c.SSOSessionName) > 0
  977. }
  978. func (c *SharedConfig) hasLegacySSOConfiguration() bool {
  979. return len(c.SSORegion) > 0 || len(c.SSOAccountID) > 0 || len(c.SSOStartURL) > 0 || len(c.SSORoleName) > 0
  980. }
  981. func (c *SharedConfig) clearAssumeRoleOptions() {
  982. c.RoleARN = ""
  983. c.ExternalID = ""
  984. c.MFASerial = ""
  985. c.RoleSessionName = ""
  986. c.SourceProfileName = ""
  987. }
  988. func (c *SharedConfig) clearCredentialOptions() {
  989. c.CredentialSource = ""
  990. c.CredentialProcess = ""
  991. c.WebIdentityTokenFile = ""
  992. c.Credentials = aws.Credentials{}
  993. c.SSOAccountID = ""
  994. c.SSORegion = ""
  995. c.SSORoleName = ""
  996. c.SSOStartURL = ""
  997. }
  998. // SharedConfigLoadError is an error for the shared config file failed to load.
  999. type SharedConfigLoadError struct {
  1000. Filename string
  1001. Err error
  1002. }
  1003. // Unwrap returns the underlying error that caused the failure.
  1004. func (e SharedConfigLoadError) Unwrap() error {
  1005. return e.Err
  1006. }
  1007. func (e SharedConfigLoadError) Error() string {
  1008. return fmt.Sprintf("failed to load shared config file, %s, %v", e.Filename, e.Err)
  1009. }
  1010. // SharedConfigProfileNotExistError is an error for the shared config when
  1011. // the profile was not find in the config file.
  1012. type SharedConfigProfileNotExistError struct {
  1013. Filename []string
  1014. Profile string
  1015. Err error
  1016. }
  1017. // Unwrap returns the underlying error that caused the failure.
  1018. func (e SharedConfigProfileNotExistError) Unwrap() error {
  1019. return e.Err
  1020. }
  1021. func (e SharedConfigProfileNotExistError) Error() string {
  1022. return fmt.Sprintf("failed to get shared config profile, %s", e.Profile)
  1023. }
  1024. // SharedConfigAssumeRoleError is an error for the shared config when the
  1025. // profile contains assume role information, but that information is invalid
  1026. // or not complete.
  1027. type SharedConfigAssumeRoleError struct {
  1028. Profile string
  1029. RoleARN string
  1030. Err error
  1031. }
  1032. // Unwrap returns the underlying error that caused the failure.
  1033. func (e SharedConfigAssumeRoleError) Unwrap() error {
  1034. return e.Err
  1035. }
  1036. func (e SharedConfigAssumeRoleError) Error() string {
  1037. return fmt.Sprintf("failed to load assume role %s, of profile %s, %v",
  1038. e.RoleARN, e.Profile, e.Err)
  1039. }
  1040. // CredentialRequiresARNError provides the error for shared config credentials
  1041. // that are incorrectly configured in the shared config or credentials file.
  1042. type CredentialRequiresARNError struct {
  1043. // type of credentials that were configured.
  1044. Type string
  1045. // Profile name the credentials were in.
  1046. Profile string
  1047. }
  1048. // Error satisfies the error interface.
  1049. func (e CredentialRequiresARNError) Error() string {
  1050. return fmt.Sprintf(
  1051. "credential type %s requires role_arn, profile %s",
  1052. e.Type, e.Profile,
  1053. )
  1054. }
  1055. func oneOrNone(bs ...bool) bool {
  1056. var count int
  1057. for _, b := range bs {
  1058. if b {
  1059. count++
  1060. if count > 1 {
  1061. return false
  1062. }
  1063. }
  1064. }
  1065. return true
  1066. }
  1067. // updateString will only update the dst with the value in the section key, key
  1068. // is present in the section.
  1069. func updateString(dst *string, section ini.Section, key string) {
  1070. if !section.Has(key) {
  1071. return
  1072. }
  1073. *dst = section.String(key)
  1074. }
  1075. // updateInt will only update the dst with the value in the section key, key
  1076. // is present in the section.
  1077. //
  1078. // Down casts the INI integer value from a int64 to an int, which could be
  1079. // different bit size depending on platform.
  1080. func updateInt(dst *int, section ini.Section, key string) error {
  1081. if !section.Has(key) {
  1082. return nil
  1083. }
  1084. if vt, _ := section.ValueType(key); vt != ini.IntegerType {
  1085. return fmt.Errorf("invalid value %s=%s, expect integer",
  1086. key, section.String(key))
  1087. }
  1088. *dst = int(section.Int(key))
  1089. return nil
  1090. }
  1091. // updateBool will only update the dst with the value in the section key, key
  1092. // is present in the section.
  1093. func updateBool(dst *bool, section ini.Section, key string) {
  1094. if !section.Has(key) {
  1095. return
  1096. }
  1097. *dst = section.Bool(key)
  1098. }
  1099. // updateBoolPtr will only update the dst with the value in the section key,
  1100. // key is present in the section.
  1101. func updateBoolPtr(dst **bool, section ini.Section, key string) {
  1102. if !section.Has(key) {
  1103. return
  1104. }
  1105. *dst = new(bool)
  1106. **dst = section.Bool(key)
  1107. }
  1108. // updateEndpointDiscoveryType will only update the dst with the value in the section, if
  1109. // a valid key and corresponding EndpointDiscoveryType is found.
  1110. func updateEndpointDiscoveryType(dst *aws.EndpointDiscoveryEnableState, section ini.Section, key string) {
  1111. if !section.Has(key) {
  1112. return
  1113. }
  1114. value := section.String(key)
  1115. if len(value) == 0 {
  1116. return
  1117. }
  1118. switch {
  1119. case strings.EqualFold(value, endpointDiscoveryDisabled):
  1120. *dst = aws.EndpointDiscoveryDisabled
  1121. case strings.EqualFold(value, endpointDiscoveryEnabled):
  1122. *dst = aws.EndpointDiscoveryEnabled
  1123. case strings.EqualFold(value, endpointDiscoveryAuto):
  1124. *dst = aws.EndpointDiscoveryAuto
  1125. }
  1126. }
  1127. // updateEndpointDiscoveryType will only update the dst with the value in the section, if
  1128. // a valid key and corresponding EndpointDiscoveryType is found.
  1129. func updateUseDualStackEndpoint(dst *aws.DualStackEndpointState, section ini.Section, key string) {
  1130. if !section.Has(key) {
  1131. return
  1132. }
  1133. if section.Bool(key) {
  1134. *dst = aws.DualStackEndpointStateEnabled
  1135. } else {
  1136. *dst = aws.DualStackEndpointStateDisabled
  1137. }
  1138. return
  1139. }
  1140. // updateEndpointDiscoveryType will only update the dst with the value in the section, if
  1141. // a valid key and corresponding EndpointDiscoveryType is found.
  1142. func updateUseFIPSEndpoint(dst *aws.FIPSEndpointState, section ini.Section, key string) {
  1143. if !section.Has(key) {
  1144. return
  1145. }
  1146. if section.Bool(key) {
  1147. *dst = aws.FIPSEndpointStateEnabled
  1148. } else {
  1149. *dst = aws.FIPSEndpointStateDisabled
  1150. }
  1151. return
  1152. }