6 "github.com/aws/aws-sdk-go/aws/awserr"
7 "github.com/aws/aws-sdk-go/aws/credentials"
8 "github.com/aws/aws-sdk-go/internal/ini"
12 // Static Credentials group
13 accessKeyIDKey = `aws_access_key_id` // group required
14 secretAccessKey = `aws_secret_access_key` // group required
15 sessionTokenKey = `aws_session_token` // optional
17 // Assume Role Credentials group
18 roleArnKey = `role_arn` // group required
19 sourceProfileKey = `source_profile` // group required (or credential_source)
20 credentialSourceKey = `credential_source` // group required (or source_profile)
21 externalIDKey = `external_id` // optional
22 mfaSerialKey = `mfa_serial` // optional
23 roleSessionNameKey = `role_session_name` // optional
25 // Additional Config fields
28 // endpoint discovery group
29 enableEndpointDiscoveryKey = `endpoint_discovery_enabled` // optional
31 // External Credential Process
32 credentialProcessKey = `credential_process` // optional
34 // Web Identity Token File
35 webIdentityTokenFileKey = `web_identity_token_file` // optional
37 // DefaultSharedConfigProfile is the default profile to be used when
38 // loading configuration from the config files if another profile name
40 DefaultSharedConfigProfile = `default`
43 // sharedConfig represents the configuration fields of the SDK config files.
44 type sharedConfig struct {
45 // Credentials values from the config file. Both aws_access_key_id and
46 // aws_secret_access_key must be provided together in the same file to be
47 // considered valid. The values will be ignored if not a complete group.
48 // aws_session_token is an optional field that can be provided if both of
49 // the other two fields are also provided.
52 // aws_secret_access_key
54 Creds credentials.Value
56 CredentialSource string
57 CredentialProcess string
58 WebIdentityTokenFile string
61 RoleSessionName string
65 SourceProfileName string
66 SourceProfile *sharedConfig
68 // Region is the region the SDK should use for looking up AWS service
69 // endpoints and signing requests.
74 // EnableEndpointDiscovery can be enabled in the shared config by setting
75 // endpoint_discovery_enabled to true
77 // endpoint_discovery_enabled = true
78 EnableEndpointDiscovery *bool
81 type sharedConfigFile struct {
86 // loadSharedConfig retrieves the configuration from the list of files using
87 // the profile provided. The order the files are listed will determine
88 // precedence. Values in subsequent files will overwrite values defined in
91 // For example, given two files A and B. Both define credentials. If the order
92 // of the files are A then B, B's credential values will be used instead of
95 // See sharedConfig.setFromFile for information how the config files
97 func loadSharedConfig(profile string, filenames []string, exOpts bool) (sharedConfig, error) {
98 if len(profile) == 0 {
99 profile = DefaultSharedConfigProfile
102 files, err := loadSharedConfigIniFiles(filenames)
104 return sharedConfig{}, err
107 cfg := sharedConfig{}
108 profiles := map[string]struct{}{}
109 if err = cfg.setFromIniFiles(profiles, profile, files, exOpts); err != nil {
110 return sharedConfig{}, err
116 func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
117 files := make([]sharedConfigFile, 0, len(filenames))
119 for _, filename := range filenames {
120 sections, err := ini.OpenFile(filename)
121 if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ini.ErrCodeUnableToReadFile {
122 // Skip files which can't be opened and read for whatever reason
124 } else if err != nil {
125 return nil, SharedConfigLoadError{Filename: filename, Err: err}
128 files = append(files, sharedConfigFile{
129 Filename: filename, IniData: sections,
136 func (cfg *sharedConfig) setFromIniFiles(profiles map[string]struct{}, profile string, files []sharedConfigFile, exOpts bool) error {
137 // Trim files from the list that don't exist.
139 var profileNotFoundErr error
140 for _, f := range files {
141 if err := cfg.setFromIniFile(profile, f, exOpts); err != nil {
142 if _, ok := err.(SharedConfigProfileNotExistsError); ok {
143 // Ignore profiles not defined in individual files.
144 profileNotFoundErr = err
151 if skippedFiles == len(files) {
152 // If all files were skipped because the profile is not found, return
153 // the original profile not found error.
154 return profileNotFoundErr
157 if _, ok := profiles[profile]; ok {
158 // if this is the second instance of the profile the Assume Role
159 // options must be cleared because they are only valid for the
160 // first reference of a profile. The self linked instance of the
161 // profile only have credential provider options.
162 cfg.clearAssumeRoleOptions()
164 // First time a profile has been seen, It must either be a assume role
165 // or credentials. Assert if the credential type requires a role ARN,
166 // the ARN is also set.
167 if err := cfg.validateCredentialsRequireARN(profile); err != nil {
171 profiles[profile] = struct{}{}
173 if err := cfg.validateCredentialType(); err != nil {
177 // Link source profiles for assume roles
178 if len(cfg.SourceProfileName) != 0 {
179 // Linked profile via source_profile ignore credential provider
180 // options, the source profile must provide the credentials.
181 cfg.clearCredentialOptions()
183 srcCfg := &sharedConfig{}
184 err := srcCfg.setFromIniFiles(profiles, cfg.SourceProfileName, files, exOpts)
186 // SourceProfile that doesn't exist is an error in configuration.
187 if _, ok := err.(SharedConfigProfileNotExistsError); ok {
188 err = SharedConfigAssumeRoleError{
189 RoleARN: cfg.RoleARN,
190 SourceProfile: cfg.SourceProfileName,
196 if !srcCfg.hasCredentials() {
197 return SharedConfigAssumeRoleError{
198 RoleARN: cfg.RoleARN,
199 SourceProfile: cfg.SourceProfileName,
203 cfg.SourceProfile = srcCfg
209 // setFromFile loads the configuration from the file using the profile
210 // provided. A sharedConfig pointer type value is used so that multiple config
211 // file loadings can be chained.
213 // Only loads complete logically grouped values, and will not set fields in cfg
214 // for incomplete grouped values in the config. Such as credentials. For
215 // example if a config file only includes aws_access_key_id but no
216 // aws_secret_access_key the aws_access_key_id will be ignored.
217 func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile, exOpts bool) error {
218 section, ok := file.IniData.GetSection(profile)
220 // Fallback to to alternate profile name: profile <name>
221 section, ok = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
223 return SharedConfigProfileNotExistsError{Profile: profile, Err: nil}
228 // Assume Role Parameters
229 updateString(&cfg.RoleARN, section, roleArnKey)
230 updateString(&cfg.ExternalID, section, externalIDKey)
231 updateString(&cfg.MFASerial, section, mfaSerialKey)
232 updateString(&cfg.RoleSessionName, section, roleSessionNameKey)
233 updateString(&cfg.SourceProfileName, section, sourceProfileKey)
234 updateString(&cfg.CredentialSource, section, credentialSourceKey)
236 updateString(&cfg.Region, section, regionKey)
239 updateString(&cfg.CredentialProcess, section, credentialProcessKey)
240 updateString(&cfg.WebIdentityTokenFile, section, webIdentityTokenFileKey)
242 // Shared Credentials
243 creds := credentials.Value{
244 AccessKeyID: section.String(accessKeyIDKey),
245 SecretAccessKey: section.String(secretAccessKey),
246 SessionToken: section.String(sessionTokenKey),
247 ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename),
253 // Endpoint discovery
254 if section.Has(enableEndpointDiscoveryKey) {
255 v := section.Bool(enableEndpointDiscoveryKey)
256 cfg.EnableEndpointDiscovery = &v
262 func (cfg *sharedConfig) validateCredentialsRequireARN(profile string) error {
263 var credSource string
266 case len(cfg.SourceProfileName) != 0:
267 credSource = sourceProfileKey
268 case len(cfg.CredentialSource) != 0:
269 credSource = credentialSourceKey
270 case len(cfg.WebIdentityTokenFile) != 0:
271 credSource = webIdentityTokenFileKey
274 if len(credSource) != 0 && len(cfg.RoleARN) == 0 {
275 return CredentialRequiresARNError{
284 func (cfg *sharedConfig) validateCredentialType() error {
285 // Only one or no credential type can be defined.
287 len(cfg.SourceProfileName) != 0,
288 len(cfg.CredentialSource) != 0,
289 len(cfg.CredentialProcess) != 0,
290 len(cfg.WebIdentityTokenFile) != 0,
292 return ErrSharedConfigSourceCollision
298 func (cfg *sharedConfig) hasCredentials() bool {
300 case len(cfg.SourceProfileName) != 0:
301 case len(cfg.CredentialSource) != 0:
302 case len(cfg.CredentialProcess) != 0:
303 case len(cfg.WebIdentityTokenFile) != 0:
304 case cfg.Creds.HasKeys():
312 func (cfg *sharedConfig) clearCredentialOptions() {
313 cfg.CredentialSource = ""
314 cfg.CredentialProcess = ""
315 cfg.WebIdentityTokenFile = ""
316 cfg.Creds = credentials.Value{}
319 func (cfg *sharedConfig) clearAssumeRoleOptions() {
323 cfg.RoleSessionName = ""
324 cfg.SourceProfileName = ""
327 func oneOrNone(bs ...bool) bool {
330 for _, b := range bs {
342 // updateString will only update the dst with the value in the section key, key
343 // is present in the section.
344 func updateString(dst *string, section ini.Section, key string) {
345 if !section.Has(key) {
348 *dst = section.String(key)
351 // SharedConfigLoadError is an error for the shared config file failed to load.
352 type SharedConfigLoadError struct {
357 // Code is the short id of the error.
358 func (e SharedConfigLoadError) Code() string {
359 return "SharedConfigLoadError"
362 // Message is the description of the error
363 func (e SharedConfigLoadError) Message() string {
364 return fmt.Sprintf("failed to load config file, %s", e.Filename)
367 // OrigErr is the underlying error that caused the failure.
368 func (e SharedConfigLoadError) OrigErr() error {
372 // Error satisfies the error interface.
373 func (e SharedConfigLoadError) Error() string {
374 return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
377 // SharedConfigProfileNotExistsError is an error for the shared config when
378 // the profile was not find in the config file.
379 type SharedConfigProfileNotExistsError struct {
384 // Code is the short id of the error.
385 func (e SharedConfigProfileNotExistsError) Code() string {
386 return "SharedConfigProfileNotExistsError"
389 // Message is the description of the error
390 func (e SharedConfigProfileNotExistsError) Message() string {
391 return fmt.Sprintf("failed to get profile, %s", e.Profile)
394 // OrigErr is the underlying error that caused the failure.
395 func (e SharedConfigProfileNotExistsError) OrigErr() error {
399 // Error satisfies the error interface.
400 func (e SharedConfigProfileNotExistsError) Error() string {
401 return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
404 // SharedConfigAssumeRoleError is an error for the shared config when the
405 // profile contains assume role information, but that information is invalid
407 type SharedConfigAssumeRoleError struct {
412 // Code is the short id of the error.
413 func (e SharedConfigAssumeRoleError) Code() string {
414 return "SharedConfigAssumeRoleError"
417 // Message is the description of the error
418 func (e SharedConfigAssumeRoleError) Message() string {
420 "failed to load assume role for %s, source profile %s has no shared credentials",
421 e.RoleARN, e.SourceProfile,
425 // OrigErr is the underlying error that caused the failure.
426 func (e SharedConfigAssumeRoleError) OrigErr() error {
430 // Error satisfies the error interface.
431 func (e SharedConfigAssumeRoleError) Error() string {
432 return awserr.SprintError(e.Code(), e.Message(), "", nil)
435 // CredentialRequiresARNError provides the error for shared config credentials
436 // that are incorrectly configured in the shared config or credentials file.
437 type CredentialRequiresARNError struct {
438 // type of credentials that were configured.
441 // Profile name the credentials were in.
445 // Code is the short id of the error.
446 func (e CredentialRequiresARNError) Code() string {
447 return "CredentialRequiresARNError"
450 // Message is the description of the error
451 func (e CredentialRequiresARNError) Message() string {
453 "credential type %s requires role_arn, profile %s",
458 // OrigErr is the underlying error that caused the failure.
459 func (e CredentialRequiresARNError) OrigErr() error {
463 // Error satisfies the error interface.
464 func (e CredentialRequiresARNError) Error() string {
465 return awserr.SprintError(e.Code(), e.Message(), "", nil)