7 "github.com/aws/aws-sdk-go/aws/awserr"
8 "github.com/aws/aws-sdk-go/aws/credentials"
9 "github.com/go-ini/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
21 externalIDKey = `external_id` // optional
22 mfaSerialKey = `mfa_serial` // optional
23 roleSessionNameKey = `role_session_name` // optional
25 // Additional Config fields
28 // DefaultSharedConfigProfile is the default profile to be used when
29 // loading configuration from the config files if another profile name
31 DefaultSharedConfigProfile = `default`
34 type assumeRoleConfig struct {
39 RoleSessionName string
42 // sharedConfig represents the configuration fields of the SDK config files.
43 type sharedConfig struct {
44 // Credentials values from the config file. Both aws_access_key_id
45 // and aws_secret_access_key must be provided together in the same file
46 // to be considered valid. The values will be ignored if not a complete group.
47 // aws_session_token is an optional field that can be provided if both of the
48 // other two fields are also provided.
51 // aws_secret_access_key
53 Creds credentials.Value
55 AssumeRole assumeRoleConfig
56 AssumeRoleSource *sharedConfig
58 // Region is the region the SDK should use for looking up AWS service endpoints
59 // and signing requests.
65 type sharedConfigFile struct {
70 // loadSharedConfig retrieves the configuration from the list of files
71 // using the profile provided. The order the files are listed will determine
72 // precedence. Values in subsequent files will overwrite values defined in
75 // For example, given two files A and B. Both define credentials. If the order
76 // of the files are A then B, B's credential values will be used instead of A's.
78 // See sharedConfig.setFromFile for information how the config files
80 func loadSharedConfig(profile string, filenames []string) (sharedConfig, error) {
81 if len(profile) == 0 {
82 profile = DefaultSharedConfigProfile
85 files, err := loadSharedConfigIniFiles(filenames)
87 return sharedConfig{}, err
91 if err = cfg.setFromIniFiles(profile, files); err != nil {
92 return sharedConfig{}, err
95 if len(cfg.AssumeRole.SourceProfile) > 0 {
96 if err := cfg.setAssumeRoleSource(profile, files); err != nil {
97 return sharedConfig{}, err
104 func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
105 files := make([]sharedConfigFile, 0, len(filenames))
107 for _, filename := range filenames {
108 b, err := ioutil.ReadFile(filename)
110 // Skip files which can't be opened and read for whatever reason
114 f, err := ini.Load(b)
116 return nil, SharedConfigLoadError{Filename: filename}
119 files = append(files, sharedConfigFile{
120 Filename: filename, IniData: f,
127 func (cfg *sharedConfig) setAssumeRoleSource(origProfile string, files []sharedConfigFile) error {
128 var assumeRoleSrc sharedConfig
130 // Multiple level assume role chains are not support
131 if cfg.AssumeRole.SourceProfile == origProfile {
133 assumeRoleSrc.AssumeRole = assumeRoleConfig{}
135 err := assumeRoleSrc.setFromIniFiles(cfg.AssumeRole.SourceProfile, files)
141 if len(assumeRoleSrc.Creds.AccessKeyID) == 0 {
142 return SharedConfigAssumeRoleError{RoleARN: cfg.AssumeRole.RoleARN}
145 cfg.AssumeRoleSource = &assumeRoleSrc
150 func (cfg *sharedConfig) setFromIniFiles(profile string, files []sharedConfigFile) error {
151 // Trim files from the list that don't exist.
152 for _, f := range files {
153 if err := cfg.setFromIniFile(profile, f); err != nil {
154 if _, ok := err.(SharedConfigProfileNotExistsError); ok {
155 // Ignore proviles missings
165 // setFromFile loads the configuration from the file using
166 // the profile provided. A sharedConfig pointer type value is used so that
167 // multiple config file loadings can be chained.
169 // Only loads complete logically grouped values, and will not set fields in cfg
170 // for incomplete grouped values in the config. Such as credentials. For example
171 // if a config file only includes aws_access_key_id but no aws_secret_access_key
172 // the aws_access_key_id will be ignored.
173 func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) error {
174 section, err := file.IniData.GetSection(profile)
176 // Fallback to to alternate profile name: profile <name>
177 section, err = file.IniData.GetSection(fmt.Sprintf("profile %s", profile))
179 return SharedConfigProfileNotExistsError{Profile: profile, Err: err}
183 // Shared Credentials
184 akid := section.Key(accessKeyIDKey).String()
185 secret := section.Key(secretAccessKey).String()
186 if len(akid) > 0 && len(secret) > 0 {
187 cfg.Creds = credentials.Value{
189 SecretAccessKey: secret,
190 SessionToken: section.Key(sessionTokenKey).String(),
191 ProviderName: fmt.Sprintf("SharedConfigCredentials: %s", file.Filename),
196 roleArn := section.Key(roleArnKey).String()
197 srcProfile := section.Key(sourceProfileKey).String()
198 if len(roleArn) > 0 && len(srcProfile) > 0 {
199 cfg.AssumeRole = assumeRoleConfig{
201 SourceProfile: srcProfile,
202 ExternalID: section.Key(externalIDKey).String(),
203 MFASerial: section.Key(mfaSerialKey).String(),
204 RoleSessionName: section.Key(roleSessionNameKey).String(),
209 if v := section.Key(regionKey).String(); len(v) > 0 {
216 // SharedConfigLoadError is an error for the shared config file failed to load.
217 type SharedConfigLoadError struct {
222 // Code is the short id of the error.
223 func (e SharedConfigLoadError) Code() string {
224 return "SharedConfigLoadError"
227 // Message is the description of the error
228 func (e SharedConfigLoadError) Message() string {
229 return fmt.Sprintf("failed to load config file, %s", e.Filename)
232 // OrigErr is the underlying error that caused the failure.
233 func (e SharedConfigLoadError) OrigErr() error {
237 // Error satisfies the error interface.
238 func (e SharedConfigLoadError) Error() string {
239 return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
242 // SharedConfigProfileNotExistsError is an error for the shared config when
243 // the profile was not find in the config file.
244 type SharedConfigProfileNotExistsError struct {
249 // Code is the short id of the error.
250 func (e SharedConfigProfileNotExistsError) Code() string {
251 return "SharedConfigProfileNotExistsError"
254 // Message is the description of the error
255 func (e SharedConfigProfileNotExistsError) Message() string {
256 return fmt.Sprintf("failed to get profile, %s", e.Profile)
259 // OrigErr is the underlying error that caused the failure.
260 func (e SharedConfigProfileNotExistsError) OrigErr() error {
264 // Error satisfies the error interface.
265 func (e SharedConfigProfileNotExistsError) Error() string {
266 return awserr.SprintError(e.Code(), e.Message(), "", e.Err)
269 // SharedConfigAssumeRoleError is an error for the shared config when the
270 // profile contains assume role information, but that information is invalid
272 type SharedConfigAssumeRoleError struct {
276 // Code is the short id of the error.
277 func (e SharedConfigAssumeRoleError) Code() string {
278 return "SharedConfigAssumeRoleError"
281 // Message is the description of the error
282 func (e SharedConfigAssumeRoleError) Message() string {
283 return fmt.Sprintf("failed to load assume role for %s, source profile has no shared credentials",
287 // OrigErr is the underlying error that caused the failure.
288 func (e SharedConfigAssumeRoleError) OrigErr() error {
292 // Error satisfies the error interface.
293 func (e SharedConfigAssumeRoleError) Error() string {
294 return awserr.SprintError(e.Code(), e.Message(), "", nil)