Skip to content

registry: treat basic-auth 401 as unauthorized - #7254

Open
locker95 wants to merge 2 commits into
docker:masterfrom
locker95:fix/login-client-side-basic-401
Open

registry: treat basic-auth 401 as unauthorized#7254
locker95 wants to merge 2 commits into
docker:masterfrom
locker95:fix/login-client-side-basic-401

Conversation

@locker95

Copy link
Copy Markdown

daemon-less docker login was turning a 401 into a plain error, so Auth() tried the next endpoint. against a Basic-Auth registry that can print Login Succeeded for a wrong password (the HTTP fallback answering 200).

mark 401 as unauthorized and stop.

Fixes #7237

Client-side login (daemon down) was wrapping a 401 as a plain error, so
Auth() moved on to the next endpoint. That can print Login Succeeded if
the HTTP fallback happens to answer 200.

Fixes docker#7237

Signed-off-by: Dean Chen <862469039@qq.com>
Comment thread internal/registry/auth.go
Comment on lines 61 to 65
resp, err := loginClient.Do(req)
if err != nil {
err = translateV2AuthError(err)
return "", err
}

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 translateV2AuthError handle this already?

func translateV2AuthError(err error) error {
var e *url.Error
if errors.As(err, &e) {
var e2 errcode.Error
if errors.As(e, &e2) && errors.Is(e2.Code, errcode.ErrorCodeUnauthorized) {
return unauthorizedErr{err}
}
}
return err
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

it only unwraps a url.Error from Do() (the distribution errcode path). a 401 status never hits that, so I switched this to errhttp.ToNative.

Comment thread internal/registry/auth.go Outdated
Comment on lines +68 to +74
if resp.StatusCode != http.StatusOK {
// TODO(dmcgowan): Attempt to further interpret result, status code and error code string
return "", fmt.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
err := fmt.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
if resp.StatusCode == http.StatusUnauthorized {
return "", unauthorizedErr{err}
}
return "", err

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.

Possibly we should use github.com/containerd/errdefs/errhttp.ToNative here

// ToNative returns the error best matching the HTTP status code
func ToNative(statusCode int) error {
switch statusCode {
case http.StatusNotFound:
return errdefs.ErrNotFound
case http.StatusBadRequest:
return errdefs.ErrInvalidArgument
case http.StatusConflict:
return errdefs.ErrConflict
case http.StatusPreconditionFailed:
return errdefs.ErrFailedPrecondition
case http.StatusUnauthorized:
return errdefs.ErrUnauthenticated
case http.StatusForbidden:
return errdefs.ErrPermissionDenied
case http.StatusNotModified:
return errdefs.ErrNotModified
case http.StatusTooManyRequests:
return errdefs.ErrResourceExhausted
case http.StatusInternalServerError:
return errdefs.ErrInternal
case http.StatusNotImplemented:
return errdefs.ErrNotImplemented
case http.StatusServiceUnavailable:
return errdefs.ErrUnavailable
default:
return cause.ErrUnexpectedStatus{Status: statusCode}
}
}

But probably wrap / decorate it after that

func TestError(t *testing.T) {
	err := errhttp.ToNative(401)
	endpointStr := "example.com"
	err = fmt.Errorf("login attempt to %s failed: %w", endpointStr, err)
	assert.Check(t, is.ErrorType(err, errdefs.IsUnauthorized))
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

yeah, used that and wrapped it so the endpoint still shows up in the error.

translateV2AuthError only unwraps a url.Error from Do(); a 401
status never went through it. Use errhttp.ToNative so Auth()
treats that 401 as unauthorized and stops.

Signed-off-by: Dean Chen <862469039@qq.com>
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.

docker login reports "Login Succeeded" for wrong password against Basic-Auth registry when daemon is not running

2 participants