Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent invalid credentials: no missing server URL or username #62

Merged
merged 6 commits into from
May 29, 2017

Conversation

n4ss
Copy link
Contributor

@n4ss n4ss commented May 22, 2017

We should not do operations on credential stores with invalid (empty) server URLs or usernames.

This causes credentials management issues in some platforms' helpers depending on the credentials management system implementation.

This needs to be merged ASAP as next docker version is probably released tomorrow (05/23) morning CET.

Signed-off-by: Nassim 'Nass' Eddequiouaq eddequiouaq.nassim@gmail.com

Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
@n4ss n4ss requested review from vdemeester and aaronlehmann May 22, 2017 09:45
// was caused by not having a credentials server URL when
// required.
func IsCredentialsMissingServerURLMessage(err string) bool {
return err == errCredentialsMissingServerURLMessage
Copy link
Member

@thaJeztah thaJeztah May 22, 2017

Choose a reason for hiding this comment

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

Is there a use for this utility function?

Copy link
Member

Choose a reason for hiding this comment

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

(and the other Is..Message functions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, I forgot to add the error checks in the output logs from credentials store client.

Done in : b69480e

// required.
func IsCredentialsMissingUsernameMessage(err string) bool {
return err == errCredentialsMissingUsernameMessage
}
Copy link
Member

Choose a reason for hiding this comment

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

nit: missing newline at end of file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done!

n4ss added 2 commits May 22, 2017 13:32
Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
@n4ss
Copy link
Contributor Author

n4ss commented May 22, 2017

/cc @vdemeester 😇

Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
@aaronlehmann
Copy link

Are empty strings the only value that can trigger this issue? I know that the OS X keychain API returns the first "matching" credential, but I'm not sure whether "matching" means an exact match for all non-empty strings, or if prefix matching is done in some cases.

client/client.go Outdated
// isValidCredsMessage checks if 'msg' contains invalid credentials error message.
// It returns whether the logs are free of invalid credentials errors and the error if it isn't.
// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername.
func isValidCredsMessage(msg string) (bool, error) {

Choose a reason for hiding this comment

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

Can we use a return code, or something similar, to convey this information? I really don't like the idea of baking an exact error string into the "protocol" here.

Copy link
Contributor Author

@n4ss n4ss May 22, 2017

Choose a reason for hiding this comment

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

That was being done before this PR.

You're totally right, I'd certainly prefer a return code too but this should go in another PR imho as I prefer not to alter anything that was here before and just add additional checks to fix the issue asap.

The helpers should use errors from credentials package so I guess that makes sense in the meantime.

Copy link

Choose a reason for hiding this comment

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

we probably could just have this function only return an error instead of (bool, error) - I don't see a use-case for them not matching

@n4ss
Copy link
Contributor Author

n4ss commented May 22, 2017

@aaronlehmann Regarding your question, when trying to do get creds by the prefix of the stored credentials' serverURL, I got nothing. There is no documentation on the way the attributes are used to match existing keychain objects.

Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

LGTM 🐯

client/client.go Outdated
// isValidCredsMessage checks if 'msg' contains invalid credentials error message.
// It returns whether the logs are free of invalid credentials errors and the error if it isn't.
// error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername.
func isValidCredsMessage(msg string) (bool, error) {
Copy link

Choose a reason for hiding this comment

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

we probably could just have this function only return an error instead of (bool, error) - I don't see a use-case for them not matching

cmd := program("store")

buffer := new(bytes.Buffer)
if err := json.NewEncoder(buffer).Encode(credentials); err != nil {
if err := json.NewEncoder(buffer).Encode(creds); err != nil {
return err
}
cmd.Input(buffer)

out, err := cmd.Output()
if err != nil {
t := strings.TrimSpace(string(out))
Copy link

Choose a reason for hiding this comment

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

for a followup PR: it might be useful to combine the cmd.Output(), strings.TrimSpace, and isValidCredsMessage parsing into a shared function that all of these exported functions can use.
The same functionality seems to be replicated across Get, Store, Erase, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, added to my todo list :)

@@ -100,13 +120,17 @@ func Get(helper Helper, reader io.Reader, writer io.Writer) error {
}

serverURL := strings.TrimSpace(buffer.String())
if len(serverURL) == 0 {
Copy link

Choose a reason for hiding this comment

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

instead of this individual check, should we instead call resp.isValid() after forming it below? That way we can also verify the username

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the Get operation on a credentials store only needs a server URL, username is not needed, as the operation is the following:

  • I have a repository foobar.io, get me the credentials for this repo so I can authenticate on it

Copy link

Choose a reason for hiding this comment

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

got it. But potentially, the username could create an invalid Credentials struct in the code below right? I would prefer to validate so that any invalid Credentials struct is rejected, in case the semantics of the Get operation change.

This can be a followup since I acknowledge the importance of this fix and that this is not an issue at present - I can help with the PR.

Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
client/client.go Outdated
return err
}
cmd.Input(buffer)

out, err := cmd.Output()
if err != nil {
t := strings.TrimSpace(string(out))

if err := isValidCredsMessage(t); err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

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

It the error is one of these "special cases", only the bare message is returned, whereas other errors will show "error storing credentials - err: ..." Without that it may be less apparent in what stage the error occurred (storing or retrieving the credentials)

Is there a reason doing so? If we need to have access to the plain error message, perhaps we should consider using errors.Wrap()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was actually following the "CredentialsNotFound" error check behavior, a follow-up PR fixing this is ready.

I prefer to have it separately though and not modify existing code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you ok with an additional parameter on errCredentialsMissingUsername and errCredentialsMissingServerURL errors that would include the operation in the error message?

Signed-off-by: Nassim 'Nass' Eddequiouaq <eddequiouaq.nassim@gmail.com>
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

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

LGTM

ping @vdemeester PTAL if it still LGTY

Also, can you squash some commits @n4ss ?

Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

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

still LGTM 👼

Copy link

@ashfall ashfall left a comment

Choose a reason for hiding this comment

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

Thanks for giving me enough context to review. :) Looks good to me!

@n4ss
Copy link
Contributor Author

n4ss commented May 29, 2017

I'm squashing and merging, this will go in 17.06. Thanks all for reviewing.

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.

6 participants