1 package config
2
3 import (
4 "bytes"
5 "context"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "os"
10 "strconv"
11 "strings"
12
13 "github.com/aws/aws-sdk-go-v2/aws"
14 "github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
15 smithyrequestcompression "github.com/aws/smithy-go/private/requestcompression"
16 )
17
18
19
20 const CredentialsSourceName = "EnvConfigCredentials"
21
22
23 const (
24 awsAccessKeyIDEnvVar = "AWS_ACCESS_KEY_ID"
25 awsAccessKeyEnvVar = "AWS_ACCESS_KEY"
26
27 awsSecretAccessKeyEnvVar = "AWS_SECRET_ACCESS_KEY"
28 awsSecretKeyEnvVar = "AWS_SECRET_KEY"
29
30 awsSessionTokenEnvVar = "AWS_SESSION_TOKEN"
31
32 awsContainerCredentialsEndpointEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
33 awsContainerCredentialsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
34 awsContainerPProviderAuthorizationEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
35
36 awsRegionEnvVar = "AWS_REGION"
37 awsDefaultRegionEnvVar = "AWS_DEFAULT_REGION"
38
39 awsProfileEnvVar = "AWS_PROFILE"
40 awsDefaultProfileEnvVar = "AWS_DEFAULT_PROFILE"
41
42 awsSharedCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE"
43
44 awsConfigFileEnvVar = "AWS_CONFIG_FILE"
45
46 awsCustomCABundleEnvVar = "AWS_CA_BUNDLE"
47
48 awsWebIdentityTokenFilePathEnvVar = "AWS_WEB_IDENTITY_TOKEN_FILE"
49
50 awsRoleARNEnvVar = "AWS_ROLE_ARN"
51 awsRoleSessionNameEnvVar = "AWS_ROLE_SESSION_NAME"
52
53 awsEnableEndpointDiscoveryEnvVar = "AWS_ENABLE_ENDPOINT_DISCOVERY"
54
55 awsS3UseARNRegionEnvVar = "AWS_S3_USE_ARN_REGION"
56
57 awsEc2MetadataServiceEndpointModeEnvVar = "AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE"
58
59 awsEc2MetadataServiceEndpointEnvVar = "AWS_EC2_METADATA_SERVICE_ENDPOINT"
60
61 awsEc2MetadataDisabled = "AWS_EC2_METADATA_DISABLED"
62 awsEc2MetadataV1DisabledEnvVar = "AWS_EC2_METADATA_V1_DISABLED"
63
64 awsS3DisableMultiRegionAccessPointEnvVar = "AWS_S3_DISABLE_MULTIREGION_ACCESS_POINTS"
65
66 awsUseDualStackEndpoint = "AWS_USE_DUALSTACK_ENDPOINT"
67
68 awsUseFIPSEndpoint = "AWS_USE_FIPS_ENDPOINT"
69
70 awsDefaultMode = "AWS_DEFAULTS_MODE"
71
72 awsRetryMaxAttempts = "AWS_MAX_ATTEMPTS"
73 awsRetryMode = "AWS_RETRY_MODE"
74 awsSdkAppID = "AWS_SDK_UA_APP_ID"
75
76 awsIgnoreConfiguredEndpoints = "AWS_IGNORE_CONFIGURED_ENDPOINT_URLS"
77 awsEndpointURL = "AWS_ENDPOINT_URL"
78
79 awsDisableRequestCompression = "AWS_DISABLE_REQUEST_COMPRESSION"
80 awsRequestMinCompressionSizeBytes = "AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES"
81
82 awsS3DisableExpressSessionAuthEnv = "AWS_S3_DISABLE_EXPRESS_SESSION_AUTH"
83 )
84
85 var (
86 credAccessEnvKeys = []string{
87 awsAccessKeyIDEnvVar,
88 awsAccessKeyEnvVar,
89 }
90 credSecretEnvKeys = []string{
91 awsSecretAccessKeyEnvVar,
92 awsSecretKeyEnvVar,
93 }
94 regionEnvKeys = []string{
95 awsRegionEnvVar,
96 awsDefaultRegionEnvVar,
97 }
98 profileEnvKeys = []string{
99 awsProfileEnvVar,
100 awsDefaultProfileEnvVar,
101 }
102 )
103
104
105
106
107
108 type EnvConfig struct {
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 Credentials aws.Credentials
124
125
126
127 ContainerCredentialsEndpoint string
128
129
130
131 ContainerCredentialsRelativePath string
132
133
134
135 ContainerAuthorizationToken string
136
137
138
139
140
141
142
143 Region string
144
145
146
147
148
149
150
151 SharedConfigProfile string
152
153
154
155
156
157
158
159 SharedCredentialsFile string
160
161
162
163
164
165
166
167 SharedConfigFile string
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 CustomCABundle string
186
187
188
189
190 EnableEndpointDiscovery aws.EndpointDiscoveryEnableState
191
192
193
194
195
196 WebIdentityTokenFilePath string
197
198
199
200
201 RoleARN string
202
203
204
205
206 RoleSessionName string
207
208
209
210
211
212 S3UseARNRegion *bool
213
214
215
216
217 EC2IMDSClientEnableState imds.ClientEnableState
218
219
220
221
222 EC2IMDSv1Disabled *bool
223
224
225
226
227 EC2IMDSEndpointMode imds.EndpointModeState
228
229
230
231
232 EC2IMDSEndpoint string
233
234
235
236
237
238 S3DisableMultiRegionAccessPoints *bool
239
240
241
242
243
244 UseDualStackEndpoint aws.DualStackEndpointState
245
246
247
248
249
250 UseFIPSEndpoint aws.FIPSEndpointState
251
252
253
254
255 DefaultsMode aws.DefaultsMode
256
257
258
259
260
261 RetryMaxAttempts int
262
263
264
265
266 RetryMode aws.RetryMode
267
268
269 AppID string
270
271
272 IgnoreConfiguredEndpoints *bool
273
274
275
276 BaseEndpoint string
277
278
279
280 DisableRequestCompression *bool
281
282
283
284
285 RequestMinCompressSizeBytes *int64
286
287
288
289
290
291
292 S3DisableExpressAuth *bool
293 }
294
295
296
297 func loadEnvConfig(ctx context.Context, cfgs configs) (Config, error) {
298 return NewEnvConfig()
299 }
300
301
302
303 func NewEnvConfig() (EnvConfig, error) {
304 var cfg EnvConfig
305
306 creds := aws.Credentials{
307 Source: CredentialsSourceName,
308 }
309 setStringFromEnvVal(&creds.AccessKeyID, credAccessEnvKeys)
310 setStringFromEnvVal(&creds.SecretAccessKey, credSecretEnvKeys)
311 if creds.HasKeys() {
312 creds.SessionToken = os.Getenv(awsSessionTokenEnvVar)
313 cfg.Credentials = creds
314 }
315
316 cfg.ContainerCredentialsEndpoint = os.Getenv(awsContainerCredentialsEndpointEnvVar)
317 cfg.ContainerCredentialsRelativePath = os.Getenv(awsContainerCredentialsRelativePathEnvVar)
318 cfg.ContainerAuthorizationToken = os.Getenv(awsContainerPProviderAuthorizationEnvVar)
319
320 setStringFromEnvVal(&cfg.Region, regionEnvKeys)
321 setStringFromEnvVal(&cfg.SharedConfigProfile, profileEnvKeys)
322
323 cfg.SharedCredentialsFile = os.Getenv(awsSharedCredentialsFileEnvVar)
324 cfg.SharedConfigFile = os.Getenv(awsConfigFileEnvVar)
325
326 cfg.CustomCABundle = os.Getenv(awsCustomCABundleEnvVar)
327
328 cfg.WebIdentityTokenFilePath = os.Getenv(awsWebIdentityTokenFilePathEnvVar)
329
330 cfg.RoleARN = os.Getenv(awsRoleARNEnvVar)
331 cfg.RoleSessionName = os.Getenv(awsRoleSessionNameEnvVar)
332
333 cfg.AppID = os.Getenv(awsSdkAppID)
334
335 if err := setBoolPtrFromEnvVal(&cfg.DisableRequestCompression, []string{awsDisableRequestCompression}); err != nil {
336 return cfg, err
337 }
338 if err := setInt64PtrFromEnvVal(&cfg.RequestMinCompressSizeBytes, []string{awsRequestMinCompressionSizeBytes}, smithyrequestcompression.MaxRequestMinCompressSizeBytes); err != nil {
339 return cfg, err
340 }
341
342 if err := setEndpointDiscoveryTypeFromEnvVal(&cfg.EnableEndpointDiscovery, []string{awsEnableEndpointDiscoveryEnvVar}); err != nil {
343 return cfg, err
344 }
345
346 if err := setBoolPtrFromEnvVal(&cfg.S3UseARNRegion, []string{awsS3UseARNRegionEnvVar}); err != nil {
347 return cfg, err
348 }
349
350 setEC2IMDSClientEnableState(&cfg.EC2IMDSClientEnableState, []string{awsEc2MetadataDisabled})
351 if err := setEC2IMDSEndpointMode(&cfg.EC2IMDSEndpointMode, []string{awsEc2MetadataServiceEndpointModeEnvVar}); err != nil {
352 return cfg, err
353 }
354 cfg.EC2IMDSEndpoint = os.Getenv(awsEc2MetadataServiceEndpointEnvVar)
355 if err := setBoolPtrFromEnvVal(&cfg.EC2IMDSv1Disabled, []string{awsEc2MetadataV1DisabledEnvVar}); err != nil {
356 return cfg, err
357 }
358
359 if err := setBoolPtrFromEnvVal(&cfg.S3DisableMultiRegionAccessPoints, []string{awsS3DisableMultiRegionAccessPointEnvVar}); err != nil {
360 return cfg, err
361 }
362
363 if err := setUseDualStackEndpointFromEnvVal(&cfg.UseDualStackEndpoint, []string{awsUseDualStackEndpoint}); err != nil {
364 return cfg, err
365 }
366
367 if err := setUseFIPSEndpointFromEnvVal(&cfg.UseFIPSEndpoint, []string{awsUseFIPSEndpoint}); err != nil {
368 return cfg, err
369 }
370
371 if err := setDefaultsModeFromEnvVal(&cfg.DefaultsMode, []string{awsDefaultMode}); err != nil {
372 return cfg, err
373 }
374
375 if err := setIntFromEnvVal(&cfg.RetryMaxAttempts, []string{awsRetryMaxAttempts}); err != nil {
376 return cfg, err
377 }
378 if err := setRetryModeFromEnvVal(&cfg.RetryMode, []string{awsRetryMode}); err != nil {
379 return cfg, err
380 }
381
382 setStringFromEnvVal(&cfg.BaseEndpoint, []string{awsEndpointURL})
383
384 if err := setBoolPtrFromEnvVal(&cfg.IgnoreConfiguredEndpoints, []string{awsIgnoreConfiguredEndpoints}); err != nil {
385 return cfg, err
386 }
387
388 if err := setBoolPtrFromEnvVal(&cfg.S3DisableExpressAuth, []string{awsS3DisableExpressSessionAuthEnv}); err != nil {
389 return cfg, err
390 }
391
392 return cfg, nil
393 }
394
395 func (c EnvConfig) getDefaultsMode(ctx context.Context) (aws.DefaultsMode, bool, error) {
396 if len(c.DefaultsMode) == 0 {
397 return "", false, nil
398 }
399 return c.DefaultsMode, true, nil
400 }
401
402 func (c EnvConfig) getAppID(context.Context) (string, bool, error) {
403 return c.AppID, len(c.AppID) > 0, nil
404 }
405
406 func (c EnvConfig) getDisableRequestCompression(context.Context) (bool, bool, error) {
407 if c.DisableRequestCompression == nil {
408 return false, false, nil
409 }
410 return *c.DisableRequestCompression, true, nil
411 }
412
413 func (c EnvConfig) getRequestMinCompressSizeBytes(context.Context) (int64, bool, error) {
414 if c.RequestMinCompressSizeBytes == nil {
415 return 0, false, nil
416 }
417 return *c.RequestMinCompressSizeBytes, true, nil
418 }
419
420
421
422 func (c EnvConfig) GetRetryMaxAttempts(ctx context.Context) (int, bool, error) {
423 if c.RetryMaxAttempts == 0 {
424 return 0, false, nil
425 }
426 return c.RetryMaxAttempts, true, nil
427 }
428
429
430
431 func (c EnvConfig) GetRetryMode(ctx context.Context) (aws.RetryMode, bool, error) {
432 if len(c.RetryMode) == 0 {
433 return "", false, nil
434 }
435 return c.RetryMode, true, nil
436 }
437
438 func setEC2IMDSClientEnableState(state *imds.ClientEnableState, keys []string) {
439 for _, k := range keys {
440 value := os.Getenv(k)
441 if len(value) == 0 {
442 continue
443 }
444 switch {
445 case strings.EqualFold(value, "true"):
446 *state = imds.ClientDisabled
447 case strings.EqualFold(value, "false"):
448 *state = imds.ClientEnabled
449 default:
450 continue
451 }
452 break
453 }
454 }
455
456 func setDefaultsModeFromEnvVal(mode *aws.DefaultsMode, keys []string) error {
457 for _, k := range keys {
458 if value := os.Getenv(k); len(value) > 0 {
459 if ok := mode.SetFromString(value); !ok {
460 return fmt.Errorf("invalid %s value: %s", k, value)
461 }
462 break
463 }
464 }
465 return nil
466 }
467
468 func setRetryModeFromEnvVal(mode *aws.RetryMode, keys []string) (err error) {
469 for _, k := range keys {
470 if value := os.Getenv(k); len(value) > 0 {
471 *mode, err = aws.ParseRetryMode(value)
472 if err != nil {
473 return fmt.Errorf("invalid %s value, %w", k, err)
474 }
475 break
476 }
477 }
478 return nil
479 }
480
481 func setEC2IMDSEndpointMode(mode *imds.EndpointModeState, keys []string) error {
482 for _, k := range keys {
483 value := os.Getenv(k)
484 if len(value) == 0 {
485 continue
486 }
487 if err := mode.SetFromString(value); err != nil {
488 return fmt.Errorf("invalid value for environment variable, %s=%s, %v", k, value, err)
489 }
490 }
491 return nil
492 }
493
494
495
496 func (c EnvConfig) getRegion(ctx context.Context) (string, bool, error) {
497 if len(c.Region) == 0 {
498 return "", false, nil
499 }
500 return c.Region, true, nil
501 }
502
503
504
505 func (c EnvConfig) getSharedConfigProfile(ctx context.Context) (string, bool, error) {
506 if len(c.SharedConfigProfile) == 0 {
507 return "", false, nil
508 }
509
510 return c.SharedConfigProfile, true, nil
511 }
512
513
514
515
516
517 func (c EnvConfig) getSharedConfigFiles(context.Context) ([]string, bool, error) {
518 var files []string
519 if v := c.SharedConfigFile; len(v) > 0 {
520 files = append(files, v)
521 }
522
523 if len(files) == 0 {
524 return nil, false, nil
525 }
526 return files, true, nil
527 }
528
529
530
531
532
533 func (c EnvConfig) getSharedCredentialsFiles(context.Context) ([]string, bool, error) {
534 var files []string
535 if v := c.SharedCredentialsFile; len(v) > 0 {
536 files = append(files, v)
537 }
538 if len(files) == 0 {
539 return nil, false, nil
540 }
541 return files, true, nil
542 }
543
544
545 func (c EnvConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) {
546 if len(c.CustomCABundle) == 0 {
547 return nil, false, nil
548 }
549
550 b, err := ioutil.ReadFile(c.CustomCABundle)
551 if err != nil {
552 return nil, false, err
553 }
554 return bytes.NewReader(b), true, nil
555 }
556
557
558
559 func (c EnvConfig) GetIgnoreConfiguredEndpoints(context.Context) (bool, bool, error) {
560 if c.IgnoreConfiguredEndpoints == nil {
561 return false, false, nil
562 }
563
564 return *c.IgnoreConfiguredEndpoints, true, nil
565 }
566
567 func (c EnvConfig) getBaseEndpoint(context.Context) (string, bool, error) {
568 return c.BaseEndpoint, len(c.BaseEndpoint) > 0, nil
569 }
570
571
572
573 func (c EnvConfig) GetServiceBaseEndpoint(ctx context.Context, sdkID string) (string, bool, error) {
574 if endpt := os.Getenv(fmt.Sprintf("%s_%s", awsEndpointURL, normalizeEnv(sdkID))); endpt != "" {
575 return endpt, true, nil
576 }
577 return "", false, nil
578 }
579
580 func normalizeEnv(sdkID string) string {
581 upper := strings.ToUpper(sdkID)
582 return strings.ReplaceAll(upper, " ", "_")
583 }
584
585
586
587 func (c EnvConfig) GetS3UseARNRegion(ctx context.Context) (value, ok bool, err error) {
588 if c.S3UseARNRegion == nil {
589 return false, false, nil
590 }
591
592 return *c.S3UseARNRegion, true, nil
593 }
594
595
596
597 func (c EnvConfig) GetS3DisableMultiRegionAccessPoints(ctx context.Context) (value, ok bool, err error) {
598 if c.S3DisableMultiRegionAccessPoints == nil {
599 return false, false, nil
600 }
601
602 return *c.S3DisableMultiRegionAccessPoints, true, nil
603 }
604
605
606
607 func (c EnvConfig) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) {
608 if c.UseDualStackEndpoint == aws.DualStackEndpointStateUnset {
609 return aws.DualStackEndpointStateUnset, false, nil
610 }
611
612 return c.UseDualStackEndpoint, true, nil
613 }
614
615
616
617 func (c EnvConfig) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) {
618 if c.UseFIPSEndpoint == aws.FIPSEndpointStateUnset {
619 return aws.FIPSEndpointStateUnset, false, nil
620 }
621
622 return c.UseFIPSEndpoint, true, nil
623 }
624
625 func setStringFromEnvVal(dst *string, keys []string) {
626 for _, k := range keys {
627 if v := os.Getenv(k); len(v) > 0 {
628 *dst = v
629 break
630 }
631 }
632 }
633
634 func setIntFromEnvVal(dst *int, keys []string) error {
635 for _, k := range keys {
636 if v := os.Getenv(k); len(v) > 0 {
637 i, err := strconv.ParseInt(v, 10, 64)
638 if err != nil {
639 return fmt.Errorf("invalid value %s=%s, %w", k, v, err)
640 }
641 *dst = int(i)
642 break
643 }
644 }
645
646 return nil
647 }
648
649 func setBoolPtrFromEnvVal(dst **bool, keys []string) error {
650 for _, k := range keys {
651 value := os.Getenv(k)
652 if len(value) == 0 {
653 continue
654 }
655
656 if *dst == nil {
657 *dst = new(bool)
658 }
659
660 switch {
661 case strings.EqualFold(value, "false"):
662 **dst = false
663 case strings.EqualFold(value, "true"):
664 **dst = true
665 default:
666 return fmt.Errorf(
667 "invalid value for environment variable, %s=%s, need true or false",
668 k, value)
669 }
670 break
671 }
672
673 return nil
674 }
675
676 func setInt64PtrFromEnvVal(dst **int64, keys []string, max int64) error {
677 for _, k := range keys {
678 value := os.Getenv(k)
679 if len(value) == 0 {
680 continue
681 }
682
683 v, err := strconv.ParseInt(value, 10, 64)
684 if err != nil {
685 return fmt.Errorf("invalid value for env var, %s=%s, need int64", k, value)
686 } else if v < 0 || v > max {
687 return fmt.Errorf("invalid range for env var min request compression size bytes %q, must be within 0 and 10485760 inclusively", v)
688 }
689 if *dst == nil {
690 *dst = new(int64)
691 }
692
693 **dst = v
694 break
695 }
696
697 return nil
698 }
699
700 func setEndpointDiscoveryTypeFromEnvVal(dst *aws.EndpointDiscoveryEnableState, keys []string) error {
701 for _, k := range keys {
702 value := os.Getenv(k)
703 if len(value) == 0 {
704 continue
705 }
706
707 switch {
708 case strings.EqualFold(value, endpointDiscoveryDisabled):
709 *dst = aws.EndpointDiscoveryDisabled
710 case strings.EqualFold(value, endpointDiscoveryEnabled):
711 *dst = aws.EndpointDiscoveryEnabled
712 case strings.EqualFold(value, endpointDiscoveryAuto):
713 *dst = aws.EndpointDiscoveryAuto
714 default:
715 return fmt.Errorf(
716 "invalid value for environment variable, %s=%s, need true, false or auto",
717 k, value)
718 }
719 }
720 return nil
721 }
722
723 func setUseDualStackEndpointFromEnvVal(dst *aws.DualStackEndpointState, keys []string) error {
724 for _, k := range keys {
725 value := os.Getenv(k)
726 if len(value) == 0 {
727 continue
728 }
729
730 switch {
731 case strings.EqualFold(value, "true"):
732 *dst = aws.DualStackEndpointStateEnabled
733 case strings.EqualFold(value, "false"):
734 *dst = aws.DualStackEndpointStateDisabled
735 default:
736 return fmt.Errorf(
737 "invalid value for environment variable, %s=%s, need true, false",
738 k, value)
739 }
740 }
741 return nil
742 }
743
744 func setUseFIPSEndpointFromEnvVal(dst *aws.FIPSEndpointState, keys []string) error {
745 for _, k := range keys {
746 value := os.Getenv(k)
747 if len(value) == 0 {
748 continue
749 }
750
751 switch {
752 case strings.EqualFold(value, "true"):
753 *dst = aws.FIPSEndpointStateEnabled
754 case strings.EqualFold(value, "false"):
755 *dst = aws.FIPSEndpointStateDisabled
756 default:
757 return fmt.Errorf(
758 "invalid value for environment variable, %s=%s, need true, false",
759 k, value)
760 }
761 }
762 return nil
763 }
764
765
766 func (c EnvConfig) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, found bool, err error) {
767 if c.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset {
768 return aws.EndpointDiscoveryUnset, false, nil
769 }
770
771 return c.EnableEndpointDiscovery, true, nil
772 }
773
774
775 func (c EnvConfig) GetEC2IMDSClientEnableState() (imds.ClientEnableState, bool, error) {
776 if c.EC2IMDSClientEnableState == imds.ClientDefaultEnableState {
777 return imds.ClientDefaultEnableState, false, nil
778 }
779
780 return c.EC2IMDSClientEnableState, true, nil
781 }
782
783
784 func (c EnvConfig) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) {
785 if c.EC2IMDSEndpointMode == imds.EndpointModeStateUnset {
786 return imds.EndpointModeStateUnset, false, nil
787 }
788
789 return c.EC2IMDSEndpointMode, true, nil
790 }
791
792
793 func (c EnvConfig) GetEC2IMDSEndpoint() (string, bool, error) {
794 if len(c.EC2IMDSEndpoint) == 0 {
795 return "", false, nil
796 }
797
798 return c.EC2IMDSEndpoint, true, nil
799 }
800
801
802
803 func (c EnvConfig) GetEC2IMDSV1FallbackDisabled() (bool, bool) {
804 if c.EC2IMDSv1Disabled == nil {
805 return false, false
806 }
807
808 return *c.EC2IMDSv1Disabled, true
809 }
810
811
812
813 func (c EnvConfig) GetS3DisableExpressAuth() (value, ok bool) {
814 if c.S3DisableExpressAuth == nil {
815 return false, false
816 }
817
818 return *c.S3DisableExpressAuth, true
819 }
820
View as plain text