1package config
  2
  3import (
  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// CredentialsSourceName provides a name of the provider when config is
 19// loaded from environment.
 20const CredentialsSourceName = "EnvConfigCredentials"
 21
 22// Environment variables that will be read for configuration values.
 23const (
 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	awsAccountIDEnv             = "AWS_ACCOUNT_ID"
 85	awsAccountIDEndpointModeEnv = "AWS_ACCOUNT_ID_ENDPOINT_MODE"
 86)
 87
 88var (
 89	credAccessEnvKeys = []string{
 90		awsAccessKeyIDEnvVar,
 91		awsAccessKeyEnvVar,
 92	}
 93	credSecretEnvKeys = []string{
 94		awsSecretAccessKeyEnvVar,
 95		awsSecretKeyEnvVar,
 96	}
 97	regionEnvKeys = []string{
 98		awsRegionEnvVar,
 99		awsDefaultRegionEnvVar,
100	}
101	profileEnvKeys = []string{
102		awsProfileEnvVar,
103		awsDefaultProfileEnvVar,
104	}
105)
106
107// EnvConfig is a collection of environment values the SDK will read
108// setup config from. All environment values are optional. But some values
109// such as credentials require multiple values to be complete or the values
110// will be ignored.
111type EnvConfig struct {
112	// Environment configuration values. If set both Access Key ID and Secret Access
113	// Key must be provided. Session Token and optionally also be provided, but is
114	// not required.
115	//
116	//	# Access Key ID
117	//	AWS_ACCESS_KEY_ID=AKID
118	//	AWS_ACCESS_KEY=AKID # only read if AWS_ACCESS_KEY_ID is not set.
119	//
120	//	# Secret Access Key
121	//	AWS_SECRET_ACCESS_KEY=SECRET
122	//	AWS_SECRET_KEY=SECRET # only read if AWS_SECRET_ACCESS_KEY is not set.
123	//
124	//	# Session Token
125	//	AWS_SESSION_TOKEN=TOKEN
126	Credentials aws.Credentials
127
128	// ContainerCredentialsEndpoint value is the HTTP enabled endpoint to retrieve credentials
129	// using the endpointcreds.Provider
130	ContainerCredentialsEndpoint string
131
132	// ContainerCredentialsRelativePath is the relative URI path that will be used when attempting to retrieve
133	// credentials from the container endpoint.
134	ContainerCredentialsRelativePath string
135
136	// ContainerAuthorizationToken is the authorization token that will be included in the HTTP Authorization
137	// header when attempting to retrieve credentials from the container credentials endpoint.
138	ContainerAuthorizationToken string
139
140	// Region value will instruct the SDK where to make service API requests to. If is
141	// not provided in the environment the region must be provided before a service
142	// client request is made.
143	//
144	//	AWS_REGION=us-west-2
145	//	AWS_DEFAULT_REGION=us-west-2
146	Region string
147
148	// Profile name the SDK should load use when loading shared configuration from the
149	// shared configuration files. If not provided "default" will be used as the
150	// profile name.
151	//
152	//	AWS_PROFILE=my_profile
153	//	AWS_DEFAULT_PROFILE=my_profile
154	SharedConfigProfile string
155
156	// Shared credentials file path can be set to instruct the SDK to use an alternate
157	// file for the shared credentials. If not set the file will be loaded from
158	// $HOME/.aws/credentials on Linux/Unix based systems, and
159	// %USERPROFILE%\.aws\credentials on Windows.
160	//
161	//	AWS_SHARED_CREDENTIALS_FILE=$HOME/my_shared_credentials
162	SharedCredentialsFile string
163
164	// Shared config file path can be set to instruct the SDK to use an alternate
165	// file for the shared config. If not set the file will be loaded from
166	// $HOME/.aws/config on Linux/Unix based systems, and
167	// %USERPROFILE%\.aws\config on Windows.
168	//
169	//	AWS_CONFIG_FILE=$HOME/my_shared_config
170	SharedConfigFile string
171
172	// Sets the path to a custom Credentials Authority (CA) Bundle PEM file
173	// that the SDK will use instead of the system's root CA bundle.
174	// Only use this if you want to configure the SDK to use a custom set
175	// of CAs.
176	//
177	// Enabling this option will attempt to merge the Transport
178	// into the SDK's HTTP client. If the client's Transport is
179	// not a http.Transport an error will be returned. If the
180	// Transport's TLS config is set this option will cause the
181	// SDK to overwrite the Transport's TLS config's  RootCAs value.
182	//
183	// Setting a custom HTTPClient in the aws.Config options will override this setting.
184	// To use this option and custom HTTP client, the HTTP client needs to be provided
185	// when creating the config. Not the service client.
186	//
187	//  AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
188	CustomCABundle string
189
190	// Enables endpoint discovery via environment variables.
191	//
192	//	AWS_ENABLE_ENDPOINT_DISCOVERY=true
193	EnableEndpointDiscovery aws.EndpointDiscoveryEnableState
194
195	// Specifies the WebIdentity token the SDK should use to assume a role
196	// with.
197	//
198	//  AWS_WEB_IDENTITY_TOKEN_FILE=file_path
199	WebIdentityTokenFilePath string
200
201	// Specifies the IAM role arn to use when assuming an role.
202	//
203	//  AWS_ROLE_ARN=role_arn
204	RoleARN string
205
206	// Specifies the IAM role session name to use when assuming a role.
207	//
208	//  AWS_ROLE_SESSION_NAME=session_name
209	RoleSessionName string
210
211	// Specifies if the S3 service should allow ARNs to direct the region
212	// the client's requests are sent to.
213	//
214	// AWS_S3_USE_ARN_REGION=true
215	S3UseARNRegion *bool
216
217	// Specifies if the EC2 IMDS service client is enabled.
218	//
219	// AWS_EC2_METADATA_DISABLED=true
220	EC2IMDSClientEnableState imds.ClientEnableState
221
222	// Specifies if EC2 IMDSv1 fallback is disabled.
223	//
224	// AWS_EC2_METADATA_V1_DISABLED=true
225	EC2IMDSv1Disabled *bool
226
227	// Specifies the EC2 Instance Metadata Service default endpoint selection mode (IPv4 or IPv6)
228	//
229	// AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE=IPv6
230	EC2IMDSEndpointMode imds.EndpointModeState
231
232	// Specifies the EC2 Instance Metadata Service endpoint to use. If specified it overrides EC2IMDSEndpointMode.
233	//
234	// AWS_EC2_METADATA_SERVICE_ENDPOINT=http://fd00:ec2::254
235	EC2IMDSEndpoint string
236
237	// Specifies if the S3 service should disable multi-region access points
238	// support.
239	//
240	// AWS_S3_DISABLE_MULTIREGION_ACCESS_POINTS=true
241	S3DisableMultiRegionAccessPoints *bool
242
243	// Specifies that SDK clients must resolve a dual-stack endpoint for
244	// services.
245	//
246	// AWS_USE_DUALSTACK_ENDPOINT=true
247	UseDualStackEndpoint aws.DualStackEndpointState
248
249	// Specifies that SDK clients must resolve a FIPS endpoint for
250	// services.
251	//
252	// AWS_USE_FIPS_ENDPOINT=true
253	UseFIPSEndpoint aws.FIPSEndpointState
254
255	// Specifies the SDK Defaults Mode used by services.
256	//
257	// AWS_DEFAULTS_MODE=standard
258	DefaultsMode aws.DefaultsMode
259
260	// Specifies the maximum number attempts an API client will call an
261	// operation that fails with a retryable error.
262	//
263	// AWS_MAX_ATTEMPTS=3
264	RetryMaxAttempts int
265
266	// Specifies the retry model the API client will be created with.
267	//
268	// aws_retry_mode=standard
269	RetryMode aws.RetryMode
270
271	// aws sdk app ID that can be added to user agent header string
272	AppID string
273
274	// Flag used to disable configured endpoints.
275	IgnoreConfiguredEndpoints *bool
276
277	// Value to contain configured endpoints to be propagated to
278	// corresponding endpoint resolution field.
279	BaseEndpoint string
280
281	// determine if request compression is allowed, default to false
282	// retrieved from env var AWS_DISABLE_REQUEST_COMPRESSION
283	DisableRequestCompression *bool
284
285	// inclusive threshold request body size to trigger compression,
286	// default to 10240 and must be within 0 and 10485760 bytes inclusive
287	// retrieved from env var AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES
288	RequestMinCompressSizeBytes *int64
289
290	// Whether S3Express auth is disabled.
291	//
292	// This will NOT prevent requests from being made to S3Express buckets, it
293	// will only bypass the modified endpoint routing and signing behaviors
294	// associated with the feature.
295	S3DisableExpressAuth *bool
296
297	// Indicates whether account ID will be required/ignored in endpoint2.0 routing
298	AccountIDEndpointMode aws.AccountIDEndpointMode
299}
300
301// loadEnvConfig reads configuration values from the OS's environment variables.
302// Returning the a Config typed EnvConfig to satisfy the ConfigLoader func type.
303func loadEnvConfig(ctx context.Context, cfgs configs) (Config, error) {
304	return NewEnvConfig()
305}
306
307// NewEnvConfig retrieves the SDK's environment configuration.
308// See `EnvConfig` for the values that will be retrieved.
309func NewEnvConfig() (EnvConfig, error) {
310	var cfg EnvConfig
311
312	creds := aws.Credentials{
313		Source: CredentialsSourceName,
314	}
315	setStringFromEnvVal(&creds.AccessKeyID, credAccessEnvKeys)
316	setStringFromEnvVal(&creds.SecretAccessKey, credSecretEnvKeys)
317	if creds.HasKeys() {
318		creds.AccountID = os.Getenv(awsAccountIDEnv)
319		creds.SessionToken = os.Getenv(awsSessionTokenEnvVar)
320		cfg.Credentials = creds
321	}
322
323	cfg.ContainerCredentialsEndpoint = os.Getenv(awsContainerCredentialsEndpointEnvVar)
324	cfg.ContainerCredentialsRelativePath = os.Getenv(awsContainerCredentialsRelativePathEnvVar)
325	cfg.ContainerAuthorizationToken = os.Getenv(awsContainerPProviderAuthorizationEnvVar)
326
327	setStringFromEnvVal(&cfg.Region, regionEnvKeys)
328	setStringFromEnvVal(&cfg.SharedConfigProfile, profileEnvKeys)
329
330	cfg.SharedCredentialsFile = os.Getenv(awsSharedCredentialsFileEnvVar)
331	cfg.SharedConfigFile = os.Getenv(awsConfigFileEnvVar)
332
333	cfg.CustomCABundle = os.Getenv(awsCustomCABundleEnvVar)
334
335	cfg.WebIdentityTokenFilePath = os.Getenv(awsWebIdentityTokenFilePathEnvVar)
336
337	cfg.RoleARN = os.Getenv(awsRoleARNEnvVar)
338	cfg.RoleSessionName = os.Getenv(awsRoleSessionNameEnvVar)
339
340	cfg.AppID = os.Getenv(awsSdkAppID)
341
342	if err := setBoolPtrFromEnvVal(&cfg.DisableRequestCompression, []string{awsDisableRequestCompression}); err != nil {
343		return cfg, err
344	}
345	if err := setInt64PtrFromEnvVal(&cfg.RequestMinCompressSizeBytes, []string{awsRequestMinCompressionSizeBytes}, smithyrequestcompression.MaxRequestMinCompressSizeBytes); err != nil {
346		return cfg, err
347	}
348
349	if err := setEndpointDiscoveryTypeFromEnvVal(&cfg.EnableEndpointDiscovery, []string{awsEnableEndpointDiscoveryEnvVar}); err != nil {
350		return cfg, err
351	}
352
353	if err := setBoolPtrFromEnvVal(&cfg.S3UseARNRegion, []string{awsS3UseARNRegionEnvVar}); err != nil {
354		return cfg, err
355	}
356
357	setEC2IMDSClientEnableState(&cfg.EC2IMDSClientEnableState, []string{awsEc2MetadataDisabled})
358	if err := setEC2IMDSEndpointMode(&cfg.EC2IMDSEndpointMode, []string{awsEc2MetadataServiceEndpointModeEnvVar}); err != nil {
359		return cfg, err
360	}
361	cfg.EC2IMDSEndpoint = os.Getenv(awsEc2MetadataServiceEndpointEnvVar)
362	if err := setBoolPtrFromEnvVal(&cfg.EC2IMDSv1Disabled, []string{awsEc2MetadataV1DisabledEnvVar}); err != nil {
363		return cfg, err
364	}
365
366	if err := setBoolPtrFromEnvVal(&cfg.S3DisableMultiRegionAccessPoints, []string{awsS3DisableMultiRegionAccessPointEnvVar}); err != nil {
367		return cfg, err
368	}
369
370	if err := setUseDualStackEndpointFromEnvVal(&cfg.UseDualStackEndpoint, []string{awsUseDualStackEndpoint}); err != nil {
371		return cfg, err
372	}
373
374	if err := setUseFIPSEndpointFromEnvVal(&cfg.UseFIPSEndpoint, []string{awsUseFIPSEndpoint}); err != nil {
375		return cfg, err
376	}
377
378	if err := setDefaultsModeFromEnvVal(&cfg.DefaultsMode, []string{awsDefaultMode}); err != nil {
379		return cfg, err
380	}
381
382	if err := setIntFromEnvVal(&cfg.RetryMaxAttempts, []string{awsRetryMaxAttempts}); err != nil {
383		return cfg, err
384	}
385	if err := setRetryModeFromEnvVal(&cfg.RetryMode, []string{awsRetryMode}); err != nil {
386		return cfg, err
387	}
388
389	setStringFromEnvVal(&cfg.BaseEndpoint, []string{awsEndpointURL})
390
391	if err := setBoolPtrFromEnvVal(&cfg.IgnoreConfiguredEndpoints, []string{awsIgnoreConfiguredEndpoints}); err != nil {
392		return cfg, err
393	}
394
395	if err := setBoolPtrFromEnvVal(&cfg.S3DisableExpressAuth, []string{awsS3DisableExpressSessionAuthEnv}); err != nil {
396		return cfg, err
397	}
398
399	if err := setAIDEndPointModeFromEnvVal(&cfg.AccountIDEndpointMode, []string{awsAccountIDEndpointModeEnv}); err != nil {
400		return cfg, err
401	}
402
403	return cfg, nil
404}
405
406func (c EnvConfig) getDefaultsMode(ctx context.Context) (aws.DefaultsMode, bool, error) {
407	if len(c.DefaultsMode) == 0 {
408		return "", false, nil
409	}
410	return c.DefaultsMode, true, nil
411}
412
413func (c EnvConfig) getAppID(context.Context) (string, bool, error) {
414	return c.AppID, len(c.AppID) > 0, nil
415}
416
417func (c EnvConfig) getDisableRequestCompression(context.Context) (bool, bool, error) {
418	if c.DisableRequestCompression == nil {
419		return false, false, nil
420	}
421	return *c.DisableRequestCompression, true, nil
422}
423
424func (c EnvConfig) getRequestMinCompressSizeBytes(context.Context) (int64, bool, error) {
425	if c.RequestMinCompressSizeBytes == nil {
426		return 0, false, nil
427	}
428	return *c.RequestMinCompressSizeBytes, true, nil
429}
430
431func (c EnvConfig) getAccountIDEndpointMode(context.Context) (aws.AccountIDEndpointMode, bool, error) {
432	return c.AccountIDEndpointMode, len(c.AccountIDEndpointMode) > 0, nil
433}
434
435// GetRetryMaxAttempts returns the value of AWS_MAX_ATTEMPTS if was specified,
436// and not 0.
437func (c EnvConfig) GetRetryMaxAttempts(ctx context.Context) (int, bool, error) {
438	if c.RetryMaxAttempts == 0 {
439		return 0, false, nil
440	}
441	return c.RetryMaxAttempts, true, nil
442}
443
444// GetRetryMode returns the RetryMode of AWS_RETRY_MODE if was specified, and a
445// valid value.
446func (c EnvConfig) GetRetryMode(ctx context.Context) (aws.RetryMode, bool, error) {
447	if len(c.RetryMode) == 0 {
448		return "", false, nil
449	}
450	return c.RetryMode, true, nil
451}
452
453func setEC2IMDSClientEnableState(state *imds.ClientEnableState, keys []string) {
454	for _, k := range keys {
455		value := os.Getenv(k)
456		if len(value) == 0 {
457			continue
458		}
459		switch {
460		case strings.EqualFold(value, "true"):
461			*state = imds.ClientDisabled
462		case strings.EqualFold(value, "false"):
463			*state = imds.ClientEnabled
464		default:
465			continue
466		}
467		break
468	}
469}
470
471func setDefaultsModeFromEnvVal(mode *aws.DefaultsMode, keys []string) error {
472	for _, k := range keys {
473		if value := os.Getenv(k); len(value) > 0 {
474			if ok := mode.SetFromString(value); !ok {
475				return fmt.Errorf("invalid %s value: %s", k, value)
476			}
477			break
478		}
479	}
480	return nil
481}
482
483func setRetryModeFromEnvVal(mode *aws.RetryMode, keys []string) (err error) {
484	for _, k := range keys {
485		if value := os.Getenv(k); len(value) > 0 {
486			*mode, err = aws.ParseRetryMode(value)
487			if err != nil {
488				return fmt.Errorf("invalid %s value, %w", k, err)
489			}
490			break
491		}
492	}
493	return nil
494}
495
496func setEC2IMDSEndpointMode(mode *imds.EndpointModeState, keys []string) error {
497	for _, k := range keys {
498		value := os.Getenv(k)
499		if len(value) == 0 {
500			continue
501		}
502		if err := mode.SetFromString(value); err != nil {
503			return fmt.Errorf("invalid value for environment variable, %s=%s, %v", k, value, err)
504		}
505	}
506	return nil
507}
508
509func setAIDEndPointModeFromEnvVal(m *aws.AccountIDEndpointMode, keys []string) error {
510	for _, k := range keys {
511		value := os.Getenv(k)
512		if len(value) == 0 {
513			continue
514		}
515
516		switch value {
517		case "preferred":
518			*m = aws.AccountIDEndpointModePreferred
519		case "required":
520			*m = aws.AccountIDEndpointModeRequired
521		case "disabled":
522			*m = aws.AccountIDEndpointModeDisabled
523		default:
524			return fmt.Errorf("invalid value for environment variable, %s=%s, must be preferred/required/disabled", k, value)
525		}
526		break
527	}
528	return nil
529}
530
531// GetRegion returns the AWS Region if set in the environment. Returns an empty
532// string if not set.
533func (c EnvConfig) getRegion(ctx context.Context) (string, bool, error) {
534	if len(c.Region) == 0 {
535		return "", false, nil
536	}
537	return c.Region, true, nil
538}
539
540// GetSharedConfigProfile returns the shared config profile if set in the
541// environment. Returns an empty string if not set.
542func (c EnvConfig) getSharedConfigProfile(ctx context.Context) (string, bool, error) {
543	if len(c.SharedConfigProfile) == 0 {
544		return "", false, nil
545	}
546
547	return c.SharedConfigProfile, true, nil
548}
549
550// getSharedConfigFiles returns a slice of filenames set in the environment.
551//
552// Will return the filenames in the order of:
553// * Shared Config
554func (c EnvConfig) getSharedConfigFiles(context.Context) ([]string, bool, error) {
555	var files []string
556	if v := c.SharedConfigFile; len(v) > 0 {
557		files = append(files, v)
558	}
559
560	if len(files) == 0 {
561		return nil, false, nil
562	}
563	return files, true, nil
564}
565
566// getSharedCredentialsFiles returns a slice of filenames set in the environment.
567//
568// Will return the filenames in the order of:
569// * Shared Credentials
570func (c EnvConfig) getSharedCredentialsFiles(context.Context) ([]string, bool, error) {
571	var files []string
572	if v := c.SharedCredentialsFile; len(v) > 0 {
573		files = append(files, v)
574	}
575	if len(files) == 0 {
576		return nil, false, nil
577	}
578	return files, true, nil
579}
580
581// GetCustomCABundle returns the custom CA bundle's PEM bytes if the file was
582func (c EnvConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) {
583	if len(c.CustomCABundle) == 0 {
584		return nil, false, nil
585	}
586
587	b, err := ioutil.ReadFile(c.CustomCABundle)
588	if err != nil {
589		return nil, false, err
590	}
591	return bytes.NewReader(b), true, nil
592}
593
594// GetIgnoreConfiguredEndpoints is used in knowing when to disable configured
595// endpoints feature.
596func (c EnvConfig) GetIgnoreConfiguredEndpoints(context.Context) (bool, bool, error) {
597	if c.IgnoreConfiguredEndpoints == nil {
598		return false, false, nil
599	}
600
601	return *c.IgnoreConfiguredEndpoints, true, nil
602}
603
604func (c EnvConfig) getBaseEndpoint(context.Context) (string, bool, error) {
605	return c.BaseEndpoint, len(c.BaseEndpoint) > 0, nil
606}
607
608// GetServiceBaseEndpoint is used to retrieve a normalized SDK ID for use
609// with configured endpoints.
610func (c EnvConfig) GetServiceBaseEndpoint(ctx context.Context, sdkID string) (string, bool, error) {
611	if endpt := os.Getenv(fmt.Sprintf("%s_%s", awsEndpointURL, normalizeEnv(sdkID))); endpt != "" {
612		return endpt, true, nil
613	}
614	return "", false, nil
615}
616
617func normalizeEnv(sdkID string) string {
618	upper := strings.ToUpper(sdkID)
619	return strings.ReplaceAll(upper, " ", "_")
620}
621
622// GetS3UseARNRegion returns whether to allow ARNs to direct the region
623// the S3 client's requests are sent to.
624func (c EnvConfig) GetS3UseARNRegion(ctx context.Context) (value, ok bool, err error) {
625	if c.S3UseARNRegion == nil {
626		return false, false, nil
627	}
628
629	return *c.S3UseARNRegion, true, nil
630}
631
632// GetS3DisableMultiRegionAccessPoints returns whether to disable multi-region access point
633// support for the S3 client.
634func (c EnvConfig) GetS3DisableMultiRegionAccessPoints(ctx context.Context) (value, ok bool, err error) {
635	if c.S3DisableMultiRegionAccessPoints == nil {
636		return false, false, nil
637	}
638
639	return *c.S3DisableMultiRegionAccessPoints, true, nil
640}
641
642// GetUseDualStackEndpoint returns whether the service's dual-stack endpoint should be
643// used for requests.
644func (c EnvConfig) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) {
645	if c.UseDualStackEndpoint == aws.DualStackEndpointStateUnset {
646		return aws.DualStackEndpointStateUnset, false, nil
647	}
648
649	return c.UseDualStackEndpoint, true, nil
650}
651
652// GetUseFIPSEndpoint returns whether the service's FIPS endpoint should be
653// used for requests.
654func (c EnvConfig) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) {
655	if c.UseFIPSEndpoint == aws.FIPSEndpointStateUnset {
656		return aws.FIPSEndpointStateUnset, false, nil
657	}
658
659	return c.UseFIPSEndpoint, true, nil
660}
661
662func setStringFromEnvVal(dst *string, keys []string) {
663	for _, k := range keys {
664		if v := os.Getenv(k); len(v) > 0 {
665			*dst = v
666			break
667		}
668	}
669}
670
671func setIntFromEnvVal(dst *int, keys []string) error {
672	for _, k := range keys {
673		if v := os.Getenv(k); len(v) > 0 {
674			i, err := strconv.ParseInt(v, 10, 64)
675			if err != nil {
676				return fmt.Errorf("invalid value %s=%s, %w", k, v, err)
677			}
678			*dst = int(i)
679			break
680		}
681	}
682
683	return nil
684}
685
686func setBoolPtrFromEnvVal(dst **bool, keys []string) error {
687	for _, k := range keys {
688		value := os.Getenv(k)
689		if len(value) == 0 {
690			continue
691		}
692
693		if *dst == nil {
694			*dst = new(bool)
695		}
696
697		switch {
698		case strings.EqualFold(value, "false"):
699			**dst = false
700		case strings.EqualFold(value, "true"):
701			**dst = true
702		default:
703			return fmt.Errorf(
704				"invalid value for environment variable, %s=%s, need true or false",
705				k, value)
706		}
707		break
708	}
709
710	return nil
711}
712
713func setInt64PtrFromEnvVal(dst **int64, keys []string, max int64) error {
714	for _, k := range keys {
715		value := os.Getenv(k)
716		if len(value) == 0 {
717			continue
718		}
719
720		v, err := strconv.ParseInt(value, 10, 64)
721		if err != nil {
722			return fmt.Errorf("invalid value for env var, %s=%s, need int64", k, value)
723		} else if v < 0 || v > max {
724			return fmt.Errorf("invalid range for env var min request compression size bytes %q, must be within 0 and 10485760 inclusively", v)
725		}
726		if *dst == nil {
727			*dst = new(int64)
728		}
729
730		**dst = v
731		break
732	}
733
734	return nil
735}
736
737func setEndpointDiscoveryTypeFromEnvVal(dst *aws.EndpointDiscoveryEnableState, keys []string) error {
738	for _, k := range keys {
739		value := os.Getenv(k)
740		if len(value) == 0 {
741			continue // skip if empty
742		}
743
744		switch {
745		case strings.EqualFold(value, endpointDiscoveryDisabled):
746			*dst = aws.EndpointDiscoveryDisabled
747		case strings.EqualFold(value, endpointDiscoveryEnabled):
748			*dst = aws.EndpointDiscoveryEnabled
749		case strings.EqualFold(value, endpointDiscoveryAuto):
750			*dst = aws.EndpointDiscoveryAuto
751		default:
752			return fmt.Errorf(
753				"invalid value for environment variable, %s=%s, need true, false or auto",
754				k, value)
755		}
756	}
757	return nil
758}
759
760func setUseDualStackEndpointFromEnvVal(dst *aws.DualStackEndpointState, keys []string) error {
761	for _, k := range keys {
762		value := os.Getenv(k)
763		if len(value) == 0 {
764			continue // skip if empty
765		}
766
767		switch {
768		case strings.EqualFold(value, "true"):
769			*dst = aws.DualStackEndpointStateEnabled
770		case strings.EqualFold(value, "false"):
771			*dst = aws.DualStackEndpointStateDisabled
772		default:
773			return fmt.Errorf(
774				"invalid value for environment variable, %s=%s, need true, false",
775				k, value)
776		}
777	}
778	return nil
779}
780
781func setUseFIPSEndpointFromEnvVal(dst *aws.FIPSEndpointState, keys []string) error {
782	for _, k := range keys {
783		value := os.Getenv(k)
784		if len(value) == 0 {
785			continue // skip if empty
786		}
787
788		switch {
789		case strings.EqualFold(value, "true"):
790			*dst = aws.FIPSEndpointStateEnabled
791		case strings.EqualFold(value, "false"):
792			*dst = aws.FIPSEndpointStateDisabled
793		default:
794			return fmt.Errorf(
795				"invalid value for environment variable, %s=%s, need true, false",
796				k, value)
797		}
798	}
799	return nil
800}
801
802// GetEnableEndpointDiscovery returns resolved value for EnableEndpointDiscovery env variable setting.
803func (c EnvConfig) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, found bool, err error) {
804	if c.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset {
805		return aws.EndpointDiscoveryUnset, false, nil
806	}
807
808	return c.EnableEndpointDiscovery, true, nil
809}
810
811// GetEC2IMDSClientEnableState implements a EC2IMDSClientEnableState options resolver interface.
812func (c EnvConfig) GetEC2IMDSClientEnableState() (imds.ClientEnableState, bool, error) {
813	if c.EC2IMDSClientEnableState == imds.ClientDefaultEnableState {
814		return imds.ClientDefaultEnableState, false, nil
815	}
816
817	return c.EC2IMDSClientEnableState, true, nil
818}
819
820// GetEC2IMDSEndpointMode implements a EC2IMDSEndpointMode option resolver interface.
821func (c EnvConfig) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) {
822	if c.EC2IMDSEndpointMode == imds.EndpointModeStateUnset {
823		return imds.EndpointModeStateUnset, false, nil
824	}
825
826	return c.EC2IMDSEndpointMode, true, nil
827}
828
829// GetEC2IMDSEndpoint implements a EC2IMDSEndpoint option resolver interface.
830func (c EnvConfig) GetEC2IMDSEndpoint() (string, bool, error) {
831	if len(c.EC2IMDSEndpoint) == 0 {
832		return "", false, nil
833	}
834
835	return c.EC2IMDSEndpoint, true, nil
836}
837
838// GetEC2IMDSV1FallbackDisabled implements an EC2IMDSV1FallbackDisabled option
839// resolver interface.
840func (c EnvConfig) GetEC2IMDSV1FallbackDisabled() (bool, bool) {
841	if c.EC2IMDSv1Disabled == nil {
842		return false, false
843	}
844
845	return *c.EC2IMDSv1Disabled, true
846}
847
848// GetS3DisableExpressAuth returns the configured value for
849// [EnvConfig.S3DisableExpressAuth].
850func (c EnvConfig) GetS3DisableExpressAuth() (value, ok bool) {
851	if c.S3DisableExpressAuth == nil {
852		return false, false
853	}
854
855	return *c.S3DisableExpressAuth, true
856}