1 package config
2
3 import (
4 "bytes"
5 "context"
6 "errors"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "os"
11 "path/filepath"
12 "strings"
13 "time"
14
15 "github.com/aws/aws-sdk-go-v2/aws"
16 "github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
17 "github.com/aws/aws-sdk-go-v2/internal/ini"
18 "github.com/aws/aws-sdk-go-v2/internal/shareddefaults"
19 "github.com/aws/smithy-go/logging"
20 smithyrequestcompression "github.com/aws/smithy-go/private/requestcompression"
21 )
22
23 const (
24
25
26 profilePrefix = `profile `
27
28
29
30 ssoSectionPrefix = `sso-session `
31
32
33
34 servicesPrefix = `services `
35
36
37 endpointDiscoveryDisabled = `false`
38 endpointDiscoveryEnabled = `true`
39 endpointDiscoveryAuto = `auto`
40
41
42 accessKeyIDKey = `aws_access_key_id`
43 secretAccessKey = `aws_secret_access_key`
44 sessionTokenKey = `aws_session_token`
45
46
47 roleArnKey = `role_arn`
48 sourceProfileKey = `source_profile`
49 credentialSourceKey = `credential_source`
50 externalIDKey = `external_id`
51 mfaSerialKey = `mfa_serial`
52 roleSessionNameKey = `role_session_name`
53 roleDurationSecondsKey = "duration_seconds"
54
55
56 ssoSessionNameKey = "sso_session"
57
58 ssoRegionKey = "sso_region"
59 ssoStartURLKey = "sso_start_url"
60
61 ssoAccountIDKey = "sso_account_id"
62 ssoRoleNameKey = "sso_role_name"
63
64
65 regionKey = `region`
66
67
68 enableEndpointDiscoveryKey = `endpoint_discovery_enabled`
69
70
71 credentialProcessKey = `credential_process`
72
73
74 webIdentityTokenFileKey = `web_identity_token_file`
75
76
77 s3UseARNRegionKey = "s3_use_arn_region"
78
79 ec2MetadataServiceEndpointModeKey = "ec2_metadata_service_endpoint_mode"
80
81 ec2MetadataServiceEndpointKey = "ec2_metadata_service_endpoint"
82
83 ec2MetadataV1DisabledKey = "ec2_metadata_v1_disabled"
84
85
86 useDualStackEndpoint = "use_dualstack_endpoint"
87
88
89
90
91 DefaultSharedConfigProfile = `default`
92
93
94 s3DisableMultiRegionAccessPointsKey = `s3_disable_multiregion_access_points`
95
96 useFIPSEndpointKey = "use_fips_endpoint"
97
98 defaultsModeKey = "defaults_mode"
99
100
101 retryMaxAttemptsKey = "max_attempts"
102 retryModeKey = "retry_mode"
103
104 caBundleKey = "ca_bundle"
105
106 sdkAppID = "sdk_ua_app_id"
107
108 ignoreConfiguredEndpoints = "ignore_configured_endpoint_urls"
109
110 endpointURL = "endpoint_url"
111
112 servicesSectionKey = "services"
113
114 disableRequestCompression = "disable_request_compression"
115 requestMinCompressionSizeBytes = "request_min_compression_size_bytes"
116
117 s3DisableExpressSessionAuthKey = "s3_disable_express_session_auth"
118 )
119
120
121 var defaultSharedConfigProfile = DefaultSharedConfigProfile
122
123
124
125
126
127
128
129
130 func DefaultSharedCredentialsFilename() string {
131 return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "credentials")
132 }
133
134
135
136
137
138
139
140
141 func DefaultSharedConfigFilename() string {
142 return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "config")
143 }
144
145
146
147 var DefaultSharedConfigFiles = []string{
148 DefaultSharedConfigFilename(),
149 }
150
151
152
153 var DefaultSharedCredentialsFiles = []string{
154 DefaultSharedCredentialsFilename(),
155 }
156
157
158
159 type SSOSession struct {
160 Name string
161 SSORegion string
162 SSOStartURL string
163 }
164
165 func (s *SSOSession) setFromIniSection(section ini.Section) {
166 updateString(&s.Name, section, ssoSessionNameKey)
167 updateString(&s.SSORegion, section, ssoRegionKey)
168 updateString(&s.SSOStartURL, section, ssoStartURLKey)
169 }
170
171
172
173 type Services struct {
174
175
176
177 ServiceValues map[string]map[string]string
178 }
179
180 func (s *Services) setFromIniSection(section ini.Section) {
181 if s.ServiceValues == nil {
182 s.ServiceValues = make(map[string]map[string]string)
183 }
184 for _, service := range section.List() {
185 s.ServiceValues[service] = section.Map(service)
186 }
187 }
188
189
190 type SharedConfig struct {
191 Profile string
192
193
194
195
196
197
198
199
200
201
202 Credentials aws.Credentials
203
204 CredentialSource string
205 CredentialProcess string
206 WebIdentityTokenFile string
207
208
209 SSOSessionName string
210 SSOSession *SSOSession
211
212
213 SSORegion string
214 SSOStartURL string
215
216
217 SSOAccountID string
218 SSORoleName string
219
220 RoleARN string
221 ExternalID string
222 MFASerial string
223 RoleSessionName string
224 RoleDurationSeconds *time.Duration
225
226 SourceProfileName string
227 Source *SharedConfig
228
229
230
231
232
233 Region string
234
235
236
237
238
239 EnableEndpointDiscovery aws.EndpointDiscoveryEnableState
240
241
242
243
244
245 S3UseARNRegion *bool
246
247
248
249
250
251 EC2IMDSEndpointMode imds.EndpointModeState
252
253
254
255
256
257 EC2IMDSEndpoint string
258
259
260
261
262
263 EC2IMDSv1Disabled *bool
264
265
266
267
268
269 S3DisableMultiRegionAccessPoints *bool
270
271
272
273
274
275 UseDualStackEndpoint aws.DualStackEndpointState
276
277
278
279
280
281 UseFIPSEndpoint aws.FIPSEndpointState
282
283
284
285
286 DefaultsMode aws.DefaultsMode
287
288
289
290
291
292 RetryMaxAttempts int
293
294
295
296
297 RetryMode aws.RetryMode
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313 CustomCABundle string
314
315
316 AppID string
317
318
319 IgnoreConfiguredEndpoints *bool
320
321
322
323 BaseEndpoint string
324
325
326 ServicesSectionName string
327 Services Services
328
329
330
331 DisableRequestCompression *bool
332
333
334
335
336 RequestMinCompressSizeBytes *int64
337
338
339
340
341
342
343 S3DisableExpressAuth *bool
344 }
345
346 func (c SharedConfig) getDefaultsMode(ctx context.Context) (value aws.DefaultsMode, ok bool, err error) {
347 if len(c.DefaultsMode) == 0 {
348 return "", false, nil
349 }
350
351 return c.DefaultsMode, true, nil
352 }
353
354
355
356 func (c SharedConfig) GetRetryMaxAttempts(ctx context.Context) (value int, ok bool, err error) {
357 if c.RetryMaxAttempts == 0 {
358 return 0, false, nil
359 }
360
361 return c.RetryMaxAttempts, true, nil
362 }
363
364
365 func (c SharedConfig) GetRetryMode(ctx context.Context) (value aws.RetryMode, ok bool, err error) {
366 if len(c.RetryMode) == 0 {
367 return "", false, nil
368 }
369
370 return c.RetryMode, true, nil
371 }
372
373
374
375 func (c SharedConfig) GetS3UseARNRegion(ctx context.Context) (value, ok bool, err error) {
376 if c.S3UseARNRegion == nil {
377 return false, false, nil
378 }
379
380 return *c.S3UseARNRegion, true, nil
381 }
382
383
384 func (c SharedConfig) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, ok bool, err error) {
385 if c.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset {
386 return aws.EndpointDiscoveryUnset, false, nil
387 }
388
389 return c.EnableEndpointDiscovery, true, nil
390 }
391
392
393
394 func (c SharedConfig) GetS3DisableMultiRegionAccessPoints(ctx context.Context) (value, ok bool, err error) {
395 if c.S3DisableMultiRegionAccessPoints == nil {
396 return false, false, nil
397 }
398
399 return *c.S3DisableMultiRegionAccessPoints, true, nil
400 }
401
402
403 func (c SharedConfig) getRegion(ctx context.Context) (string, bool, error) {
404 if len(c.Region) == 0 {
405 return "", false, nil
406 }
407 return c.Region, true, nil
408 }
409
410
411 func (c SharedConfig) getCredentialsProvider() (aws.Credentials, bool, error) {
412 return c.Credentials, true, nil
413 }
414
415
416 func (c SharedConfig) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) {
417 if c.EC2IMDSEndpointMode == imds.EndpointModeStateUnset {
418 return imds.EndpointModeStateUnset, false, nil
419 }
420
421 return c.EC2IMDSEndpointMode, true, nil
422 }
423
424
425 func (c SharedConfig) GetEC2IMDSEndpoint() (string, bool, error) {
426 if len(c.EC2IMDSEndpoint) == 0 {
427 return "", false, nil
428 }
429
430 return c.EC2IMDSEndpoint, true, nil
431 }
432
433
434
435 func (c SharedConfig) GetEC2IMDSV1FallbackDisabled() (bool, bool) {
436 if c.EC2IMDSv1Disabled == nil {
437 return false, false
438 }
439
440 return *c.EC2IMDSv1Disabled, true
441 }
442
443
444
445 func (c SharedConfig) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) {
446 if c.UseDualStackEndpoint == aws.DualStackEndpointStateUnset {
447 return aws.DualStackEndpointStateUnset, false, nil
448 }
449
450 return c.UseDualStackEndpoint, true, nil
451 }
452
453
454
455 func (c SharedConfig) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) {
456 if c.UseFIPSEndpoint == aws.FIPSEndpointStateUnset {
457 return aws.FIPSEndpointStateUnset, false, nil
458 }
459
460 return c.UseFIPSEndpoint, true, nil
461 }
462
463
464
465 func (c SharedConfig) GetS3DisableExpressAuth() (value, ok bool) {
466 if c.S3DisableExpressAuth == nil {
467 return false, false
468 }
469
470 return *c.S3DisableExpressAuth, true
471 }
472
473
474 func (c SharedConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) {
475 if len(c.CustomCABundle) == 0 {
476 return nil, false, nil
477 }
478
479 b, err := ioutil.ReadFile(c.CustomCABundle)
480 if err != nil {
481 return nil, false, err
482 }
483 return bytes.NewReader(b), true, nil
484 }
485
486
487 func (c SharedConfig) getAppID(context.Context) (string, bool, error) {
488 return c.AppID, len(c.AppID) > 0, nil
489 }
490
491
492
493 func (c SharedConfig) GetIgnoreConfiguredEndpoints(context.Context) (bool, bool, error) {
494 if c.IgnoreConfiguredEndpoints == nil {
495 return false, false, nil
496 }
497
498 return *c.IgnoreConfiguredEndpoints, true, nil
499 }
500
501 func (c SharedConfig) getBaseEndpoint(context.Context) (string, bool, error) {
502 return c.BaseEndpoint, len(c.BaseEndpoint) > 0, nil
503 }
504
505
506
507 func (c SharedConfig) GetServiceBaseEndpoint(ctx context.Context, sdkID string) (string, bool, error) {
508 if service, ok := c.Services.ServiceValues[normalizeShared(sdkID)]; ok {
509 if endpt, ok := service[endpointURL]; ok {
510 return endpt, true, nil
511 }
512 }
513 return "", false, nil
514 }
515
516 func normalizeShared(sdkID string) string {
517 lower := strings.ToLower(sdkID)
518 return strings.ReplaceAll(lower, " ", "_")
519 }
520
521 func (c SharedConfig) getServicesObject(context.Context) (map[string]map[string]string, bool, error) {
522 return c.Services.ServiceValues, c.Services.ServiceValues != nil, nil
523 }
524
525
526
527
528 func loadSharedConfigIgnoreNotExist(ctx context.Context, configs configs) (Config, error) {
529 cfg, err := loadSharedConfig(ctx, configs)
530 if err != nil {
531 if _, ok := err.(SharedConfigProfileNotExistError); ok {
532 return SharedConfig{}, nil
533 }
534 return nil, err
535 }
536
537 return cfg, nil
538 }
539
540
541
542
543
544
545
546
547
548
549
550
551
552 func loadSharedConfig(ctx context.Context, configs configs) (Config, error) {
553 var profile string
554 var configFiles []string
555 var credentialsFiles []string
556 var ok bool
557 var err error
558
559 profile, ok, err = getSharedConfigProfile(ctx, configs)
560 if err != nil {
561 return nil, err
562 }
563 if !ok {
564 profile = defaultSharedConfigProfile
565 }
566
567 configFiles, ok, err = getSharedConfigFiles(ctx, configs)
568 if err != nil {
569 return nil, err
570 }
571
572 credentialsFiles, ok, err = getSharedCredentialsFiles(ctx, configs)
573 if err != nil {
574 return nil, err
575 }
576
577
578 var logger logging.Logger
579 logWarnings, found, err := getLogConfigurationWarnings(ctx, configs)
580 if err != nil {
581 return SharedConfig{}, err
582 }
583 if found && logWarnings {
584 logger, found, err = getLogger(ctx, configs)
585 if err != nil {
586 return SharedConfig{}, err
587 }
588 if !found {
589 logger = logging.NewStandardLogger(os.Stderr)
590 }
591 }
592
593 return LoadSharedConfigProfile(ctx, profile,
594 func(o *LoadSharedConfigOptions) {
595 o.Logger = logger
596 o.ConfigFiles = configFiles
597 o.CredentialsFiles = credentialsFiles
598 },
599 )
600 }
601
602
603 type LoadSharedConfigOptions struct {
604
605
606 CredentialsFiles []string
607
608
609 ConfigFiles []string
610
611
612 Logger logging.Logger
613 }
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629 func LoadSharedConfigProfile(ctx context.Context, profile string, optFns ...func(*LoadSharedConfigOptions)) (SharedConfig, error) {
630 var option LoadSharedConfigOptions
631 for _, fn := range optFns {
632 fn(&option)
633 }
634
635 if option.ConfigFiles == nil {
636 option.ConfigFiles = DefaultSharedConfigFiles
637 }
638
639 if option.CredentialsFiles == nil {
640 option.CredentialsFiles = DefaultSharedCredentialsFiles
641 }
642
643
644 configSections, err := loadIniFiles(option.ConfigFiles)
645 if err != nil {
646 return SharedConfig{}, err
647 }
648
649
650 err = processConfigSections(ctx, &configSections, option.Logger)
651 if err != nil {
652 return SharedConfig{}, err
653 }
654
655
656 credentialsSections, err := loadIniFiles(option.CredentialsFiles)
657 if err != nil {
658 return SharedConfig{}, err
659 }
660
661
662 err = processCredentialsSections(ctx, &credentialsSections, option.Logger)
663 if err != nil {
664 return SharedConfig{}, err
665 }
666
667 err = mergeSections(&configSections, credentialsSections)
668 if err != nil {
669 return SharedConfig{}, err
670 }
671
672 cfg := SharedConfig{}
673 profiles := map[string]struct{}{}
674
675 if err = cfg.setFromIniSections(profiles, profile, configSections, option.Logger); err != nil {
676 return SharedConfig{}, err
677 }
678
679 return cfg, nil
680 }
681
682 func processConfigSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error {
683 skipSections := map[string]struct{}{}
684
685 for _, section := range sections.List() {
686 if _, ok := skipSections[section]; ok {
687 continue
688 }
689
690
691 switch {
692 case strings.HasPrefix(section, profilePrefix):
693
694
695
696 newName, err := renameProfileSection(section, sections, logger)
697 if err != nil {
698 return fmt.Errorf("failed to rename profile section, %w", err)
699 }
700 skipSections[newName] = struct{}{}
701
702 case strings.HasPrefix(section, ssoSectionPrefix):
703 case strings.HasPrefix(section, servicesPrefix):
704 case strings.EqualFold(section, "default"):
705 default:
706
707 sections.DeleteSection(section)
708
709 if logger != nil {
710 logger.Logf(logging.Debug, "A profile defined with name `%v` is ignored. "+
711 "For use within a shared configuration file, "+
712 "a non-default profile must have `profile ` "+
713 "prefixed to the profile name.",
714 section,
715 )
716 }
717 }
718 }
719 return nil
720 }
721
722 func renameProfileSection(section string, sections *ini.Sections, logger logging.Logger) (string, error) {
723 v, ok := sections.GetSection(section)
724 if !ok {
725 return "", fmt.Errorf("error processing profiles within the shared configuration files")
726 }
727
728
729 sections.DeleteSection(section)
730
731
732 section = strings.TrimPrefix(section, profilePrefix)
733 if sections.HasSection(section) {
734 oldSection, _ := sections.GetSection(section)
735 v.Logs = append(v.Logs,
736 fmt.Sprintf("A non-default profile not prefixed with `profile ` found in %s, "+
737 "overriding non-default profile from %s",
738 v.SourceFile, oldSection.SourceFile))
739 sections.DeleteSection(section)
740 }
741
742
743 v.Name = section
744 sections.SetSection(section, v)
745
746 return section, nil
747 }
748
749 func processCredentialsSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error {
750 for _, section := range sections.List() {
751
752 if strings.HasPrefix(section, profilePrefix) {
753
754 sections.DeleteSection(section)
755
756 if logger != nil {
757 logger.Logf(logging.Debug,
758 "The profile defined with name `%v` is ignored. A profile with the `profile ` prefix is invalid "+
759 "for the shared credentials file.\n",
760 section,
761 )
762 }
763 }
764 }
765 return nil
766 }
767
768 func loadIniFiles(filenames []string) (ini.Sections, error) {
769 mergedSections := ini.NewSections()
770
771 for _, filename := range filenames {
772 sections, err := ini.OpenFile(filename)
773 var v *ini.UnableToReadFile
774 if ok := errors.As(err, &v); ok {
775
776
777 continue
778 } else if err != nil {
779 return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err}
780 }
781
782
783 err = mergeSections(&mergedSections, sections)
784 if err != nil {
785 return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err}
786 }
787 }
788
789 return mergedSections, nil
790 }
791
792
793 func mergeSections(dst *ini.Sections, src ini.Sections) error {
794 for _, sectionName := range src.List() {
795 srcSection, _ := src.GetSection(sectionName)
796
797 if (!srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey)) ||
798 (srcSection.Has(accessKeyIDKey) && !srcSection.Has(secretAccessKey)) {
799 srcSection.Errors = append(srcSection.Errors,
800 fmt.Errorf("partial credentials found for profile %v", sectionName))
801 }
802
803 if !dst.HasSection(sectionName) {
804 dst.SetSection(sectionName, srcSection)
805 continue
806 }
807
808
809 dstSection, _ := dst.GetSection(sectionName)
810
811
812 dstSection.Errors = srcSection.Errors
813
814
815 if srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey) {
816 accessKey := srcSection.String(accessKeyIDKey)
817 secretKey := srcSection.String(secretAccessKey)
818
819 if dstSection.Has(accessKeyIDKey) {
820 dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, accessKeyIDKey,
821 dstSection.SourceFile[accessKeyIDKey], srcSection.SourceFile[accessKeyIDKey]))
822 }
823
824
825 v, err := ini.NewStringValue(accessKey)
826 if err != nil {
827 return fmt.Errorf("error merging access key, %w", err)
828 }
829 dstSection.UpdateValue(accessKeyIDKey, v)
830
831
832 v, err = ini.NewStringValue(secretKey)
833 if err != nil {
834 return fmt.Errorf("error merging secret key, %w", err)
835 }
836 dstSection.UpdateValue(secretAccessKey, v)
837
838
839 if err = mergeStringKey(&srcSection, &dstSection, sectionName, sessionTokenKey); err != nil {
840 return err
841 }
842
843
844 dstSection.UpdateSourceFile(accessKeyIDKey, srcSection.SourceFile[accessKeyIDKey])
845 dstSection.UpdateSourceFile(secretAccessKey, srcSection.SourceFile[secretAccessKey])
846 }
847
848 stringKeys := []string{
849 roleArnKey,
850 sourceProfileKey,
851 credentialSourceKey,
852 externalIDKey,
853 mfaSerialKey,
854 roleSessionNameKey,
855 regionKey,
856 enableEndpointDiscoveryKey,
857 credentialProcessKey,
858 webIdentityTokenFileKey,
859 s3UseARNRegionKey,
860 s3DisableMultiRegionAccessPointsKey,
861 ec2MetadataServiceEndpointModeKey,
862 ec2MetadataServiceEndpointKey,
863 ec2MetadataV1DisabledKey,
864 useDualStackEndpoint,
865 useFIPSEndpointKey,
866 defaultsModeKey,
867 retryModeKey,
868 caBundleKey,
869 roleDurationSecondsKey,
870 retryMaxAttemptsKey,
871
872 ssoSessionNameKey,
873 ssoAccountIDKey,
874 ssoRegionKey,
875 ssoRoleNameKey,
876 ssoStartURLKey,
877 }
878 for i := range stringKeys {
879 if err := mergeStringKey(&srcSection, &dstSection, sectionName, stringKeys[i]); err != nil {
880 return err
881 }
882 }
883
884
885 *dst = dst.SetSection(sectionName, dstSection)
886 }
887
888 return nil
889 }
890
891 func mergeStringKey(srcSection *ini.Section, dstSection *ini.Section, sectionName, key string) error {
892 if srcSection.Has(key) {
893 srcValue := srcSection.String(key)
894 val, err := ini.NewStringValue(srcValue)
895 if err != nil {
896 return fmt.Errorf("error merging %s, %w", key, err)
897 }
898
899 if dstSection.Has(key) {
900 dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, key,
901 dstSection.SourceFile[key], srcSection.SourceFile[key]))
902 }
903
904 dstSection.UpdateValue(key, val)
905 dstSection.UpdateSourceFile(key, srcSection.SourceFile[key])
906 }
907 return nil
908 }
909
910 func newMergeKeyLogMessage(sectionName, key, dstSourceFile, srcSourceFile string) string {
911 return fmt.Sprintf("For profile: %v, overriding %v value, defined in %v "+
912 "with a %v value found in a duplicate profile defined at file %v. \n",
913 sectionName, key, dstSourceFile, key, srcSourceFile)
914 }
915
916
917
918 func (c *SharedConfig) setFromIniSections(profiles map[string]struct{}, profile string,
919 sections ini.Sections, logger logging.Logger) error {
920 c.Profile = profile
921
922 section, ok := sections.GetSection(profile)
923 if !ok {
924 return SharedConfigProfileNotExistError{
925 Profile: profile,
926 }
927 }
928
929
930 if section.Logs != nil && logger != nil {
931 for _, log := range section.Logs {
932 logger.Logf(logging.Debug, log)
933 }
934 }
935
936
937 err := c.setFromIniSection(profile, section)
938 if err != nil {
939 return fmt.Errorf("error fetching config from profile, %v, %w", profile, err)
940 }
941
942 if _, ok := profiles[profile]; ok {
943
944
945
946
947 c.clearAssumeRoleOptions()
948 } else {
949
950
951 if err := c.validateCredentialsConfig(profile); err != nil {
952 return err
953 }
954 }
955
956
957 if len(profiles) != 0 && c.Credentials.HasKeys() {
958 return nil
959 }
960
961 profiles[profile] = struct{}{}
962
963
964 if err := c.validateCredentialType(); err != nil {
965 return err
966 }
967
968
969 if len(c.SourceProfileName) != 0 {
970
971
972 c.clearCredentialOptions()
973
974 srcCfg := &SharedConfig{}
975 err := srcCfg.setFromIniSections(profiles, c.SourceProfileName, sections, logger)
976 if err != nil {
977
978 if _, ok := err.(SharedConfigProfileNotExistError); ok {
979 err = SharedConfigAssumeRoleError{
980 RoleARN: c.RoleARN,
981 Profile: c.SourceProfileName,
982 Err: err,
983 }
984 }
985 return err
986 }
987
988 if !srcCfg.hasCredentials() {
989 return SharedConfigAssumeRoleError{
990 RoleARN: c.RoleARN,
991 Profile: c.SourceProfileName,
992 }
993 }
994
995 c.Source = srcCfg
996 }
997
998
999
1000
1001
1002 if c.hasSSOTokenProviderConfiguration() {
1003 section, ok := sections.GetSection(ssoSectionPrefix + strings.TrimSpace(c.SSOSessionName))
1004 if !ok {
1005 return fmt.Errorf("failed to find SSO session section, %v", c.SSOSessionName)
1006 }
1007 var ssoSession SSOSession
1008 ssoSession.setFromIniSection(section)
1009 ssoSession.Name = c.SSOSessionName
1010 c.SSOSession = &ssoSession
1011 }
1012
1013 if len(c.ServicesSectionName) > 0 {
1014 if section, ok := sections.GetSection(servicesPrefix + c.ServicesSectionName); ok {
1015 var svcs Services
1016 svcs.setFromIniSection(section)
1017 c.Services = svcs
1018 }
1019 }
1020
1021 return nil
1022 }
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032 func (c *SharedConfig) setFromIniSection(profile string, section ini.Section) error {
1033 if len(section.Name) == 0 {
1034 sources := make([]string, 0)
1035 for _, v := range section.SourceFile {
1036 sources = append(sources, v)
1037 }
1038
1039 return fmt.Errorf("parsing error : could not find profile section name after processing files: %v", sources)
1040 }
1041
1042 if len(section.Errors) != 0 {
1043 var errStatement string
1044 for i, e := range section.Errors {
1045 errStatement = fmt.Sprintf("%d, %v\n", i+1, e.Error())
1046 }
1047 return fmt.Errorf("Error using profile: \n %v", errStatement)
1048 }
1049
1050
1051 updateString(&c.RoleARN, section, roleArnKey)
1052 updateString(&c.ExternalID, section, externalIDKey)
1053 updateString(&c.MFASerial, section, mfaSerialKey)
1054 updateString(&c.RoleSessionName, section, roleSessionNameKey)
1055 updateString(&c.SourceProfileName, section, sourceProfileKey)
1056 updateString(&c.CredentialSource, section, credentialSourceKey)
1057 updateString(&c.Region, section, regionKey)
1058
1059
1060
1061 updateString(&c.SSOSessionName, section, ssoSessionNameKey)
1062
1063
1064 updateString(&c.SSORegion, section, ssoRegionKey)
1065 updateString(&c.SSOStartURL, section, ssoStartURLKey)
1066
1067
1068 updateString(&c.SSOAccountID, section, ssoAccountIDKey)
1069 updateString(&c.SSORoleName, section, ssoRoleNameKey)
1070
1071
1072
1073
1074
1075 if section.Has(roleDurationSecondsKey) {
1076 if v, ok := section.Int(roleDurationSecondsKey); ok {
1077 c.RoleDurationSeconds = aws.Duration(time.Duration(v) * time.Second)
1078 } else {
1079 c.RoleDurationSeconds = aws.Duration(time.Duration(0))
1080 }
1081 }
1082
1083 updateString(&c.CredentialProcess, section, credentialProcessKey)
1084 updateString(&c.WebIdentityTokenFile, section, webIdentityTokenFileKey)
1085
1086 updateEndpointDiscoveryType(&c.EnableEndpointDiscovery, section, enableEndpointDiscoveryKey)
1087 updateBoolPtr(&c.S3UseARNRegion, section, s3UseARNRegionKey)
1088 updateBoolPtr(&c.S3DisableMultiRegionAccessPoints, section, s3DisableMultiRegionAccessPointsKey)
1089 updateBoolPtr(&c.S3DisableExpressAuth, section, s3DisableExpressSessionAuthKey)
1090
1091 if err := updateEC2MetadataServiceEndpointMode(&c.EC2IMDSEndpointMode, section, ec2MetadataServiceEndpointModeKey); err != nil {
1092 return fmt.Errorf("failed to load %s from shared config, %v", ec2MetadataServiceEndpointModeKey, err)
1093 }
1094 updateString(&c.EC2IMDSEndpoint, section, ec2MetadataServiceEndpointKey)
1095 updateBoolPtr(&c.EC2IMDSv1Disabled, section, ec2MetadataV1DisabledKey)
1096
1097 updateUseDualStackEndpoint(&c.UseDualStackEndpoint, section, useDualStackEndpoint)
1098 updateUseFIPSEndpoint(&c.UseFIPSEndpoint, section, useFIPSEndpointKey)
1099
1100 if err := updateDefaultsMode(&c.DefaultsMode, section, defaultsModeKey); err != nil {
1101 return fmt.Errorf("failed to load %s from shared config, %w", defaultsModeKey, err)
1102 }
1103
1104 if err := updateInt(&c.RetryMaxAttempts, section, retryMaxAttemptsKey); err != nil {
1105 return fmt.Errorf("failed to load %s from shared config, %w", retryMaxAttemptsKey, err)
1106 }
1107 if err := updateRetryMode(&c.RetryMode, section, retryModeKey); err != nil {
1108 return fmt.Errorf("failed to load %s from shared config, %w", retryModeKey, err)
1109 }
1110
1111 updateString(&c.CustomCABundle, section, caBundleKey)
1112
1113
1114 updateString(&c.AppID, section, sdkAppID)
1115
1116 updateBoolPtr(&c.IgnoreConfiguredEndpoints, section, ignoreConfiguredEndpoints)
1117
1118 updateString(&c.BaseEndpoint, section, endpointURL)
1119
1120 if err := updateDisableRequestCompression(&c.DisableRequestCompression, section, disableRequestCompression); err != nil {
1121 return fmt.Errorf("failed to load %s from shared config, %w", disableRequestCompression, err)
1122 }
1123 if err := updateRequestMinCompressSizeBytes(&c.RequestMinCompressSizeBytes, section, requestMinCompressionSizeBytes); err != nil {
1124 return fmt.Errorf("failed to load %s from shared config, %w", requestMinCompressionSizeBytes, err)
1125 }
1126
1127
1128 creds := aws.Credentials{
1129 AccessKeyID: section.String(accessKeyIDKey),
1130 SecretAccessKey: section.String(secretAccessKey),
1131 SessionToken: section.String(sessionTokenKey),
1132 Source: fmt.Sprintf("SharedConfigCredentials: %s", section.SourceFile[accessKeyIDKey]),
1133 }
1134
1135 if creds.HasKeys() {
1136 c.Credentials = creds
1137 }
1138
1139 updateString(&c.ServicesSectionName, section, servicesSectionKey)
1140
1141 return nil
1142 }
1143
1144 func updateRequestMinCompressSizeBytes(bytes **int64, sec ini.Section, key string) error {
1145 if !sec.Has(key) {
1146 return nil
1147 }
1148
1149 v, ok := sec.Int(key)
1150 if !ok {
1151 return fmt.Errorf("invalid value for min request compression size bytes %s, need int64", sec.String(key))
1152 }
1153 if v < 0 || v > smithyrequestcompression.MaxRequestMinCompressSizeBytes {
1154 return fmt.Errorf("invalid range for min request compression size bytes %d, must be within 0 and 10485760 inclusively", v)
1155 }
1156 *bytes = new(int64)
1157 **bytes = v
1158 return nil
1159 }
1160
1161 func updateDisableRequestCompression(disable **bool, sec ini.Section, key string) error {
1162 if !sec.Has(key) {
1163 return nil
1164 }
1165
1166 v := sec.String(key)
1167 switch {
1168 case v == "true":
1169 *disable = new(bool)
1170 **disable = true
1171 case v == "false":
1172 *disable = new(bool)
1173 **disable = false
1174 default:
1175 return fmt.Errorf("invalid value for shared config profile field, %s=%s, need true or false", key, v)
1176 }
1177 return nil
1178 }
1179
1180 func (c SharedConfig) getRequestMinCompressSizeBytes(ctx context.Context) (int64, bool, error) {
1181 if c.RequestMinCompressSizeBytes == nil {
1182 return 0, false, nil
1183 }
1184 return *c.RequestMinCompressSizeBytes, true, nil
1185 }
1186
1187 func (c SharedConfig) getDisableRequestCompression(ctx context.Context) (bool, bool, error) {
1188 if c.DisableRequestCompression == nil {
1189 return false, false, nil
1190 }
1191 return *c.DisableRequestCompression, true, nil
1192 }
1193
1194 func updateDefaultsMode(mode *aws.DefaultsMode, section ini.Section, key string) error {
1195 if !section.Has(key) {
1196 return nil
1197 }
1198 value := section.String(key)
1199 if ok := mode.SetFromString(value); !ok {
1200 return fmt.Errorf("invalid value: %s", value)
1201 }
1202 return nil
1203 }
1204
1205 func updateRetryMode(mode *aws.RetryMode, section ini.Section, key string) (err error) {
1206 if !section.Has(key) {
1207 return nil
1208 }
1209 value := section.String(key)
1210 if *mode, err = aws.ParseRetryMode(value); err != nil {
1211 return err
1212 }
1213 return nil
1214 }
1215
1216 func updateEC2MetadataServiceEndpointMode(endpointMode *imds.EndpointModeState, section ini.Section, key string) error {
1217 if !section.Has(key) {
1218 return nil
1219 }
1220 value := section.String(key)
1221 return endpointMode.SetFromString(value)
1222 }
1223
1224 func (c *SharedConfig) validateCredentialsConfig(profile string) error {
1225 if err := c.validateCredentialsRequireARN(profile); err != nil {
1226 return err
1227 }
1228
1229 return nil
1230 }
1231
1232 func (c *SharedConfig) validateCredentialsRequireARN(profile string) error {
1233 var credSource string
1234
1235 switch {
1236 case len(c.SourceProfileName) != 0:
1237 credSource = sourceProfileKey
1238 case len(c.CredentialSource) != 0:
1239 credSource = credentialSourceKey
1240 case len(c.WebIdentityTokenFile) != 0:
1241 credSource = webIdentityTokenFileKey
1242 }
1243
1244 if len(credSource) != 0 && len(c.RoleARN) == 0 {
1245 return CredentialRequiresARNError{
1246 Type: credSource,
1247 Profile: profile,
1248 }
1249 }
1250
1251 return nil
1252 }
1253
1254 func (c *SharedConfig) validateCredentialType() error {
1255
1256 if !oneOrNone(
1257 len(c.SourceProfileName) != 0,
1258 len(c.CredentialSource) != 0,
1259 len(c.CredentialProcess) != 0,
1260 len(c.WebIdentityTokenFile) != 0,
1261 ) {
1262 return fmt.Errorf("only one credential type may be specified per profile: source profile, credential source, credential process, web identity token")
1263 }
1264
1265 return nil
1266 }
1267
1268 func (c *SharedConfig) validateSSOConfiguration() error {
1269 if c.hasSSOTokenProviderConfiguration() {
1270 err := c.validateSSOTokenProviderConfiguration()
1271 if err != nil {
1272 return err
1273 }
1274 return nil
1275 }
1276
1277 if c.hasLegacySSOConfiguration() {
1278 err := c.validateLegacySSOConfiguration()
1279 if err != nil {
1280 return err
1281 }
1282 }
1283 return nil
1284 }
1285
1286 func (c *SharedConfig) validateSSOTokenProviderConfiguration() error {
1287 var missing []string
1288
1289 if len(c.SSOSessionName) == 0 {
1290 missing = append(missing, ssoSessionNameKey)
1291 }
1292
1293 if c.SSOSession == nil {
1294 missing = append(missing, ssoSectionPrefix)
1295 } else {
1296 if len(c.SSOSession.SSORegion) == 0 {
1297 missing = append(missing, ssoRegionKey)
1298 }
1299
1300 if len(c.SSOSession.SSOStartURL) == 0 {
1301 missing = append(missing, ssoStartURLKey)
1302 }
1303 }
1304
1305 if len(missing) > 0 {
1306 return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s",
1307 c.Profile, strings.Join(missing, ", "))
1308 }
1309
1310 if len(c.SSORegion) > 0 && c.SSORegion != c.SSOSession.SSORegion {
1311 return fmt.Errorf("%s in profile %q must match %s in %s", ssoRegionKey, c.Profile, ssoRegionKey, ssoSectionPrefix)
1312 }
1313
1314 if len(c.SSOStartURL) > 0 && c.SSOStartURL != c.SSOSession.SSOStartURL {
1315 return fmt.Errorf("%s in profile %q must match %s in %s", ssoStartURLKey, c.Profile, ssoStartURLKey, ssoSectionPrefix)
1316 }
1317
1318 return nil
1319 }
1320
1321 func (c *SharedConfig) validateLegacySSOConfiguration() error {
1322 var missing []string
1323
1324 if len(c.SSORegion) == 0 {
1325 missing = append(missing, ssoRegionKey)
1326 }
1327
1328 if len(c.SSOStartURL) == 0 {
1329 missing = append(missing, ssoStartURLKey)
1330 }
1331
1332 if len(c.SSOAccountID) == 0 {
1333 missing = append(missing, ssoAccountIDKey)
1334 }
1335
1336 if len(c.SSORoleName) == 0 {
1337 missing = append(missing, ssoRoleNameKey)
1338 }
1339
1340 if len(missing) > 0 {
1341 return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s",
1342 c.Profile, strings.Join(missing, ", "))
1343 }
1344 return nil
1345 }
1346
1347 func (c *SharedConfig) hasCredentials() bool {
1348 switch {
1349 case len(c.SourceProfileName) != 0:
1350 case len(c.CredentialSource) != 0:
1351 case len(c.CredentialProcess) != 0:
1352 case len(c.WebIdentityTokenFile) != 0:
1353 case c.hasSSOConfiguration():
1354 case c.Credentials.HasKeys():
1355 default:
1356 return false
1357 }
1358
1359 return true
1360 }
1361
1362 func (c *SharedConfig) hasSSOConfiguration() bool {
1363 return c.hasSSOTokenProviderConfiguration() || c.hasLegacySSOConfiguration()
1364 }
1365
1366 func (c *SharedConfig) hasSSOTokenProviderConfiguration() bool {
1367 return len(c.SSOSessionName) > 0
1368 }
1369
1370 func (c *SharedConfig) hasLegacySSOConfiguration() bool {
1371 return len(c.SSORegion) > 0 || len(c.SSOAccountID) > 0 || len(c.SSOStartURL) > 0 || len(c.SSORoleName) > 0
1372 }
1373
1374 func (c *SharedConfig) clearAssumeRoleOptions() {
1375 c.RoleARN = ""
1376 c.ExternalID = ""
1377 c.MFASerial = ""
1378 c.RoleSessionName = ""
1379 c.SourceProfileName = ""
1380 }
1381
1382 func (c *SharedConfig) clearCredentialOptions() {
1383 c.CredentialSource = ""
1384 c.CredentialProcess = ""
1385 c.WebIdentityTokenFile = ""
1386 c.Credentials = aws.Credentials{}
1387 c.SSOAccountID = ""
1388 c.SSORegion = ""
1389 c.SSORoleName = ""
1390 c.SSOStartURL = ""
1391 }
1392
1393
1394 type SharedConfigLoadError struct {
1395 Filename string
1396 Err error
1397 }
1398
1399
1400 func (e SharedConfigLoadError) Unwrap() error {
1401 return e.Err
1402 }
1403
1404 func (e SharedConfigLoadError) Error() string {
1405 return fmt.Sprintf("failed to load shared config file, %s, %v", e.Filename, e.Err)
1406 }
1407
1408
1409
1410 type SharedConfigProfileNotExistError struct {
1411 Filename []string
1412 Profile string
1413 Err error
1414 }
1415
1416
1417 func (e SharedConfigProfileNotExistError) Unwrap() error {
1418 return e.Err
1419 }
1420
1421 func (e SharedConfigProfileNotExistError) Error() string {
1422 return fmt.Sprintf("failed to get shared config profile, %s", e.Profile)
1423 }
1424
1425
1426
1427
1428 type SharedConfigAssumeRoleError struct {
1429 Profile string
1430 RoleARN string
1431 Err error
1432 }
1433
1434
1435 func (e SharedConfigAssumeRoleError) Unwrap() error {
1436 return e.Err
1437 }
1438
1439 func (e SharedConfigAssumeRoleError) Error() string {
1440 return fmt.Sprintf("failed to load assume role %s, of profile %s, %v",
1441 e.RoleARN, e.Profile, e.Err)
1442 }
1443
1444
1445
1446 type CredentialRequiresARNError struct {
1447
1448 Type string
1449
1450
1451 Profile string
1452 }
1453
1454
1455 func (e CredentialRequiresARNError) Error() string {
1456 return fmt.Sprintf(
1457 "credential type %s requires role_arn, profile %s",
1458 e.Type, e.Profile,
1459 )
1460 }
1461
1462 func oneOrNone(bs ...bool) bool {
1463 var count int
1464
1465 for _, b := range bs {
1466 if b {
1467 count++
1468 if count > 1 {
1469 return false
1470 }
1471 }
1472 }
1473
1474 return true
1475 }
1476
1477
1478
1479 func updateString(dst *string, section ini.Section, key string) {
1480 if !section.Has(key) {
1481 return
1482 }
1483 *dst = section.String(key)
1484 }
1485
1486
1487
1488
1489
1490
1491 func updateInt(dst *int, section ini.Section, key string) error {
1492 if !section.Has(key) {
1493 return nil
1494 }
1495
1496 v, ok := section.Int(key)
1497 if !ok {
1498 return fmt.Errorf("invalid value %s=%s, expect integer", key, section.String(key))
1499 }
1500
1501 *dst = int(v)
1502 return nil
1503 }
1504
1505
1506
1507 func updateBool(dst *bool, section ini.Section, key string) {
1508 if !section.Has(key) {
1509 return
1510 }
1511
1512
1513 v, _ := section.Bool(key)
1514 *dst = v
1515 }
1516
1517
1518
1519 func updateBoolPtr(dst **bool, section ini.Section, key string) {
1520 if !section.Has(key) {
1521 return
1522 }
1523
1524
1525 v, _ := section.Bool(key)
1526 *dst = new(bool)
1527 **dst = v
1528 }
1529
1530
1531
1532 func updateEndpointDiscoveryType(dst *aws.EndpointDiscoveryEnableState, section ini.Section, key string) {
1533 if !section.Has(key) {
1534 return
1535 }
1536
1537 value := section.String(key)
1538 if len(value) == 0 {
1539 return
1540 }
1541
1542 switch {
1543 case strings.EqualFold(value, endpointDiscoveryDisabled):
1544 *dst = aws.EndpointDiscoveryDisabled
1545 case strings.EqualFold(value, endpointDiscoveryEnabled):
1546 *dst = aws.EndpointDiscoveryEnabled
1547 case strings.EqualFold(value, endpointDiscoveryAuto):
1548 *dst = aws.EndpointDiscoveryAuto
1549 }
1550 }
1551
1552
1553
1554 func updateUseDualStackEndpoint(dst *aws.DualStackEndpointState, section ini.Section, key string) {
1555 if !section.Has(key) {
1556 return
1557 }
1558
1559
1560 if v, _ := section.Bool(key); v {
1561 *dst = aws.DualStackEndpointStateEnabled
1562 } else {
1563 *dst = aws.DualStackEndpointStateDisabled
1564 }
1565
1566 return
1567 }
1568
1569
1570
1571 func updateUseFIPSEndpoint(dst *aws.FIPSEndpointState, section ini.Section, key string) {
1572 if !section.Has(key) {
1573 return
1574 }
1575
1576
1577 if v, _ := section.Bool(key); v {
1578 *dst = aws.FIPSEndpointStateEnabled
1579 } else {
1580 *dst = aws.FIPSEndpointStateDisabled
1581 }
1582
1583 return
1584 }
1585
View as plain text