code.dwrz.net

Go monorepo.
Log | Files | Refs

shared_config.go (40771B)


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