Skip to content

api: Add NewWithCredentials()#646

Merged
harshavardhana merged 1 commit into
minio:masterfrom
harshavardhana:credentials
May 12, 2017
Merged

api: Add NewWithCredentials()#646
harshavardhana merged 1 commit into
minio:masterfrom
harshavardhana:credentials

Conversation

@harshavardhana
Copy link
Copy Markdown
Member

@harshavardhana harshavardhana commented Apr 13, 2017

This PR adds a new API

  • NewWithCredentials()

Internally NewWithCredentials is now used with
all APIs such as New(), NewV4(), NewV2() and NewWithRegion.

Also brings a new package called credentials to manage
various credentials type, currently the credentials
package supports

  • Reading file from .aws/credentials, .mc/config.json
  • Reading env variables for AWS*, MINIO*
  • Fetching from IAM roles assigned to an EC2 instance.
  • Static credentials which is the current default behavior.

Example code using IAM.

        iam := credentials.NewIAM("")
        s3Client, err := minio.NewWithCredentials("s3.amazonaws.com", iam, true, "")
        if err != nil {
                log.Fatalln(err)
        }

        buckets, err := s3Client.ListBuckets()
        if err != nil {
                log.Fatalln(err)
        }
        for _, bucket := range buckets {
                log.Println(bucket)
        }

Fixes #643

@harshavardhana harshavardhana force-pushed the credentials branch 3 times, most recently from 61a6d51 to 7357588 Compare April 13, 2017 08:54
@harshavardhana harshavardhana changed the title [WIP] api: Add NewWithCredentials() api: Add NewWithCredentials() Apr 13, 2017
Comment thread pkg/credentials/file_minio.go Outdated
}
filename = filepath.Join(homeDir, ".mc", "config.json")
if runtime.GOOS == "windows" {
filepath.Join(homeDir, "mc", "config.json")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a typo

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops yes.

if endpoint == "" {
// IAM Roles for Amazon EC2
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html
endpoint = "http://169.254.169.254"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be https ?

Copy link
Copy Markdown
Member Author

@harshavardhana harshavardhana Apr 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope it is only accessible from inside EC2 instance.

if err != nil {
return nil, err
}
resp, err := client.Do(req)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close response body.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread pkg/credentials/iam_aws_s3.go Outdated
}

if err := s.Err(); err != nil {
return nil, errors.New("Failed to read from IAM metadata service")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is bettter to append s.Err() to the returned error here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if err != nil {
return ec2RoleCredRespBody{}, err
}
u.Path = path.Join(iamSecurityCredsPath, credsName)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we encode credsName here ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually its not needed.. u.String() encodes it.

return ec2RoleCredRespBody{}, err
}

resp, err := client.Do(req)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close resp body.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread pkg/credentials/lib.go
if e.CurrentTime == nil {
e.CurrentTime = time.Now
}
return e.expiration.Before(e.CurrentTime())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean 'After' here ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No Before is correct. Read it as expiration time is before current time.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes sorry, probably it is easier to read e.CurrentTime().After(e.expiration)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But usually it is better to use on the left side what you are validating against. Otherwise it would look like currentTime() is what we are validating not expiration.

Comment thread pkg/credentials/lib.go Outdated
}

// isExpired helper method wrapping the definition of expired credentials.
func (c *Info) isExpired() bool {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function doesn't seem to be useful, you can merge it in IsExpired()

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isExpired() is used at two places one in Get() and another is IsExpired()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isExpired() is used at two places one in Get() and another is IsExpired()

yes, right.

@vadmeste
Copy link
Copy Markdown
Member

Some missing LICENCES in some files

@harshavardhana
Copy link
Copy Markdown
Member Author

Some missing LICENCES in some files

Yeah will add license at the end.

@deekoder
Copy link
Copy Markdown
Contributor

@harshavardhana - is this ready for merge?

@harshavardhana
Copy link
Copy Markdown
Member Author

@harshavardhana - is this ready for merge?

Not yet waiting on Mattermost.

Comment thread pkg/credentials/iam_aws_s3.go Outdated
// NewIAM returns a pointer to a new Credentials object wrapping
// the IAM. Takes a ConfigProvider to create a EC2Metadata client.
// The ConfigProvider is satisfied by the session.Session type.
func NewIAM(endpoint string) *Info {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this API needs to accept a role name as a parameter, not only endpoint. The application needs to get credentials from the right role name before performing the needed action which is only authorized by that role.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RoleName is not needed since the IAM RoleName is attached to the EC2 instance where the GET originates. So you are bound to be under the RoleName already.

Why do you think it is necessary?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. I thought we can attach multiple IAM roles to one instance but we can only set one. (which is weird by the way and looks painful for the users)

I found this here, http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html

You *cannot* attach multiple IAM roles to a single instance, but you can attach a single IAM role to multiple instances. For more information about creating and using IAM roles,

@harshavardhana harshavardhana force-pushed the credentials branch 2 times, most recently from 8c3242f to 7a6ce85 Compare April 24, 2017 07:55
@harshavardhana harshavardhana force-pushed the credentials branch 2 times, most recently from 0eb4826 to 8deedd1 Compare May 7, 2017 07:16
Comment thread signature-type.go Outdated
// Different types of supported signatures - default is Latest i.e SignatureV4.
const (
Latest SignatureType = iota
// Default signature is always V4.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment on exported const SignatureDefault should be of the form "SignatureDefault ..."

@harshavardhana harshavardhana force-pushed the credentials branch 2 times, most recently from fdadb5b to 46c196c Compare May 8, 2017 00:04
@harshavardhana
Copy link
Copy Markdown
Member Author

Unblocking and ready for review @vadmeste

Comment thread api-presigned.go Outdated
signerType = parseSignatureType(value.SignatureType)
}

// FIXME: Session token should be supported.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't be fixed in this PR ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spec is little vague on post policy about this. Will need to explore more.

Comment thread api-presigned.go Outdated
// ask for anything specific signature style.. Check if the
// credentials set returned the type of signature that should
// be used, if not proceed to use default.
if signerType == SignatureDefault && value.SignatureType != "" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the signature type that comes from config, if it exists, should override the one specified in the application code. That way, we can give users more power to configure.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually no. We have methods to override that see NewV4 and NewV2. We need to honor that. Since they don't take dynamic credentials.

Comment thread api.go
c.secretAccessKey, location, metadata.contentLength, time.Now().UTC())
req = s3signer.SignV2(*req, accessKeyID, secretAccessKey)
case signerType.isStreamingV4() && method == "PUT":
req = s3signer.StreamingSignV4(req, accessKeyID,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No session token here ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to figure it out .. not sure right now.


// IsExpired returns if the credentials have been retrieved.
func (e *EnvAWS) IsExpired() bool {
return !e.retrieved
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably should always return true. A user can change his environment variables inside his application (who knows), and also it is no harm if we fetch from the environment each time Retrieve() is called.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That we already do.. it is to indicate if envs can ever be expired. They cannot be. It is not correct to call it not expired.. basically envs are similar to static creds in their behavior.

Comment thread pkg/credentials/chain.go

import "fmt"

// A Chain will search for a provider which returns credentials
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is well thought, I wasn't able to criticize this.

vadmeste
vadmeste previously approved these changes May 8, 2017
Comment thread api-presigned.go
return nil, nil, err
}

if sessionToken != "" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@harshavardhana harshavardhana force-pushed the credentials branch 3 times, most recently from b0ba992 to 64743cc Compare May 10, 2017 01:46
Comment thread pkg/credentials/static.go Outdated

// NewStatic returns a pointer to a new Credentials object
// wrapping a static credentials value provider.
func NewStatic(id, secret, token, signerType string) *Credentials {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't better to create a new exported signature data type and use it instead of string ? that way the user would be less prone to error since any typo will make this module uses anonymous.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it can be simplified..

Comment thread pkg/credentials/file_aws_credentials.go Outdated

// NewFileAWSCredentials returns a pointer to a new Credentials object
// wrapping the Profile file provider.
func NewFileAWSCredentials(filename string, profile string) (*Credentials, error) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NewFileAWSCredentials and NewFileMinioClient are always returnig nil, shall we remove the returned error ? if we do, it will be like other New() primitive for static, IAM and envs.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes looks like older problem. Will fix

This PR adds a new API

  - NewWithCredentials()

Internally NewWithCredentials is now used with
all APIs such as New(), NewV4(), NewV2() and NewWithRegion.

Also brings a new package called `credentials` to manage
various credentials type, currently the credentials
package supports

  - Reading file from `.aws/credentials`, `.mc/config.json`
  - Reading env variables for AWS*, MINIO*
  - Fetching from IAM roles assigned to an EC2 instance.
  - Static credentials which is the current default behavior.

Example code using IAM.

```go
        iam := credentials.NewIAM("")
        s3Client, err := minio.NewWithCredentials("s3.amazonaws.com", iam, true, "")
        if err != nil {
                log.Fatalln(err)
        }

        buckets, err := s3Client.ListBuckets()
        if err != nil {
                log.Fatalln(err)
        }
        for _, bucket := range buckets {
                log.Println(bucket)
        }
```

Fixes minio#643
Copy link
Copy Markdown
Member

@vadmeste vadmeste left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants