6 "github.com/aws/aws-sdk-go/aws/awserr"
7 "github.com/aws/aws-sdk-go/aws/credentials"
9 "github.com/aws/aws-sdk-go/internal/ini"
13 // Static Credentials group
14 accessKeyIDKey = `aws_access_key_id` // group required
15 secretAccessKey = `aws_secret_access_key` // group required
16 sessionTokenKey = `aws_session_token` // optional
18 // Assume Role Credentials group
19 roleArnKey = `role_arn` // group required
20 sourceProfileKey = `source_profile` // group required (or credential_source)
21 credentialSourceKey = `credential_source` // group required (or source_profile)
22 externalIDKey = `external_id` // optional
23 mfaSerialKey = `mfa_serial` // optional
24 roleSessionNameKey = `role_session_name` // optional
26 // Additional Config fields
29 // endpoint discovery group
30 enableEndpointDiscoveryKey = `endpoint_discovery_enabled` // optional
31 // External Credential Process
32 credentialProcessKey = `credential_process`
34 // DefaultSharedConfigProfile is the default profile to be used when
35 // loading configuration from the config files if another profile name
37 DefaultSharedConfigProfile = `default`
40 type assumeRoleConfig struct {
43 CredentialSource string
46 RoleSessionName string
49 // sharedConfig represents the configuration fields of the SDK config files.
50 type sharedConfig struct {
51 // Credentials values from the config file. Both aws_access_key_id
52 // and aws_secret_access_key must be provided together in the same file
53 // to be considered valid. The values will be ignored if not a complete group.
54 // aws_session_token is an optional field that can be provided if both of the
55 // other two fields are also provided.
58 // aws_secret_access_key
60 Creds credentials.Value
62 AssumeRole assumeRoleConfig
63 AssumeRoleSource *sharedConfig
65 // An external process to request credentials
66 CredentialProcess string
68 // Region is the region the SDK should use for looking up AWS service endpoints
69 // 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
87 // using 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 A's.
94 // See sharedConfig.setFromFile for information how the config files
96 func loadSharedConfig(profile string, filenames []string) (sharedConfig, error) {
97 if len(profile) == 0 {
98 profile = DefaultSharedConfigProfile
101 files, err := loadSharedConfigIniFiles(filenames)
103 return sharedConfig{}, err
106 cfg := sharedConfig{}
107 if err = cfg.setFromIniFiles(profile, files); err != nil {
108 return sharedConfig{}, err
111 if len(cfg.AssumeRole.SourceProfile) > 0 {
112 if err := cfg.setAssumeRoleSource(profile, files); err != nil {
113 return sharedConfig{}, err
120 func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
121 files := make([]sharedConfigFile, 0, len(filenames))
123 for _, filename := range filenames {
124 sections, err := ini.OpenFile(filename)
125 if aerr, ok := err.(awserr.Error); ok && aerr.Code() == ini.ErrCodeUnableToReadFile {
126 // Skip files which can't be opened and read for whatever reason
128 } else if err != nil {
129 return nil, SharedConfigLoadError{Filename: filename, Err: err}
132 files = append(files, sharedConfigFile{
133 Filename: filename, IniData: sections,
140 func (cfg *sharedConfig) setAssumeRoleSource(origProfile string, files []sharedConfigFile) error {
141 var assumeRoleSrc sharedConfig
143 if len(cfg.AssumeRole.CredentialSource) > 0 {
144 // setAssumeRoleSource is only called when source_profile is found.
145 // If both source_profile and credential_source are set, then
146 // ErrSharedConfigSourceCollision will be returned
147 return ErrSharedConfigSourceCollision
150 // Multiple level assume role chains are not support
151 if cfg.AssumeRole.SourceProfile == origProfile {
153 assumeRoleSrc.AssumeRole = assumeRoleConfig{}
155 err := assumeRoleSrc.setFromIniFiles(cfg.AssumeRole.SourceProfile, files)
161 if len(assumeRoleSrc.Creds.AccessKeyID) == 0 {
162 return SharedConfigAssumeRoleError{RoleARN: cfg.AssumeRole.RoleARN}
165 cfg.AssumeRoleSource = &assumeRoleSrc
170 func (cfg *sharedConfig) setFromIniFiles(profile string, files []sharedConfigFile) error {
171 // Trim files from the list that don't exist.
172 for _, f := range files {
173 if err := cfg.setFromIniFile(profile, f); err != nil {
174 if _, ok := err.(SharedConfigProfileNotExistsError); ok {
175 // Ignore proviles missings
185 // setFromFile loads the configuration from the file using
186 // the profile provided. A sharedConfig pointer type value is used so that
187 // multiple config file loadings can be chained.
189 // Only loads complete logically grouped values, and will not set fields in cfg
190 // for incomplete grouped values in the config. Such as credentials. For example
191 // if a config file only includes aws_access_key_id but no aws_secret_access_key
192 // the aws_access_key_id will be ignored.
193 func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) error {
194 section, ok := file.IniData.GetSection(profile)
196 // Fallback to to alternate profile name: profile <name>
197 section, ok = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
199 return SharedConfigProfileNotExistsError{Profile: profile, Err: nil}
203 // Shared Credentials
204 akid := section.String(accessKeyIDKey)
205 secret := section.String(secretAccessKey)
206 if len(akid) > 0 && len(secret) > 0 {
207 cfg.Creds = credentials.Value{
209 SecretAccessKey: secret,
210 SessionToken: section.String(sessionTokenKey),
211 ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename),
216 roleArn := section.String(roleArnKey)
217 srcProfile := section.String(sourceProfileKey)
218 credentialSource := section.String(credentialSourceKey)
219 hasSource := len(srcProfile) > 0 || len(credentialSource) > 0
220 if len(roleArn) > 0 && hasSource {
221 cfg.AssumeRole = assumeRoleConfig{
223 SourceProfile: srcProfile,
224 CredentialSource: credentialSource,
225 ExternalID: section.String(externalIDKey),
226 MFASerial: section.String(mfaSerialKey),
227 RoleSessionName: section.String(roleSessionNameKey),
231 // `credential_process`
232 if credProc := section.String(credentialProcessKey); len(credProc) > 0 {
233 cfg.CredentialProcess = credProc
237 if v := section.String(regionKey); len(v) > 0 {
241 // Endpoint discovery
242 if section.Has(enableEndpointDiscoveryKey) {
243 v := section.Bool(enableEndpointDiscoveryKey)
244 cfg.EnableEndpointDiscovery = &v
250 // SharedConfigLoadError is an error for the shared config file failed to load.
251 type SharedConfigLoadError struct {
256 // Code is the short id of the error.
257 func (e SharedConfigLoadError) Code() string {
258 return "SharedConfigLoadError"
261 // Message is the description of the error
262 func (e SharedConfigLoadError) Message() string {
263 return fmt.Sprintf("failed to load config file, %s", e.Filename)
266 // OrigErr is the underlying error that caused the failure.
267 func (e SharedConfigLoadError) OrigErr() error {
271 // Error satisfies the error interface.
272 func (e SharedConfigLoadError) Error() string {
273 return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
276 // SharedConfigProfileNotExistsError is an error for the shared config when
277 // the profile was not find in the config file.
278 type SharedConfigProfileNotExistsError struct {
283 // Code is the short id of the error.
284 func (e SharedConfigProfileNotExistsError) Code() string {
285 return "SharedConfigProfileNotExistsError"
288 // Message is the description of the error
289 func (e SharedConfigProfileNotExistsError) Message() string {
290 return fmt.Sprintf("failed to get profile, %s", e.Profile)
293 // OrigErr is the underlying error that caused the failure.
294 func (e SharedConfigProfileNotExistsError) OrigErr() error {
298 // Error satisfies the error interface.
299 func (e SharedConfigProfileNotExistsError) Error() string {
300 return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
303 // SharedConfigAssumeRoleError is an error for the shared config when the
304 // profile contains assume role information, but that information is invalid
306 type SharedConfigAssumeRoleError struct {
310 // Code is the short id of the error.
311 func (e SharedConfigAssumeRoleError) Code() string {
312 return "SharedConfigAssumeRoleError"
315 // Message is the description of the error
316 func (e SharedConfigAssumeRoleError) Message() string {
317 return fmt.Sprintf("failed to load assume role for %s, source profile has no shared credentials",
321 // OrigErr is the underlying error that caused the failure.
322 func (e SharedConfigAssumeRoleError) OrigErr() error {
326 // Error satisfies the error interface.
327 func (e SharedConfigAssumeRoleError) Error() string {
328 return awserr.SprintError(e.Code(), e.Message(), "", nil)