|
|
|
|
@ -296,15 +296,22 @@ func (c *Client) Login(host string, options ...LoginOption) error {
|
|
|
|
|
}
|
|
|
|
|
reg.PlainHTTP = c.plainHTTP
|
|
|
|
|
cred := auth.Credential{Username: c.username, Password: c.password}
|
|
|
|
|
c.authorizer.ForceAttemptOAuth2 = true
|
|
|
|
|
reg.Client = c.authorizer
|
|
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
if err := reg.Ping(ctx); err != nil {
|
|
|
|
|
c.authorizer.ForceAttemptOAuth2 = false
|
|
|
|
|
if err := reg.Ping(ctx); err != nil {
|
|
|
|
|
return fmt.Errorf("authenticating to %q: %w", host, err)
|
|
|
|
|
}
|
|
|
|
|
err = c.ping(ctx, reg)
|
|
|
|
|
if err != nil && !reg.PlainHTTP && c.forcedHTTP() {
|
|
|
|
|
// The registry is plain HTTP: the fallback transport downgraded the
|
|
|
|
|
// connection from https to http. ORAS v2.6.1+ refuses to forward
|
|
|
|
|
// credentials across that implicit scheme change (GHSA-vh4v-2xq2-g5cg),
|
|
|
|
|
// so the credentialed ping above fails. Now that the fallback has been
|
|
|
|
|
// detected, set PlainHTTP explicitly and retry so requests are built as
|
|
|
|
|
// http from the start and the scheme no longer changes mid-request.
|
|
|
|
|
reg.PlainHTTP = true
|
|
|
|
|
err = c.ping(ctx, reg)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("authenticating to %q: %w", host, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The credentialsStore loader does not handle empty files. So, there is a workaround.
|
|
|
|
|
@ -341,6 +348,30 @@ func (c *Client) Login(host string, options ...LoginOption) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ping authenticates against the registry, first attempting the OAuth2 token
|
|
|
|
|
// flow and falling back to the basic/refresh token flow on failure.
|
|
|
|
|
func (c *Client) ping(ctx context.Context, reg *remote.Registry) error {
|
|
|
|
|
c.authorizer.ForceAttemptOAuth2 = true
|
|
|
|
|
err := reg.Ping(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.authorizer.ForceAttemptOAuth2 = false
|
|
|
|
|
err = reg.Ping(ctx)
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// forcedHTTP reports whether the client's transport has fallen back to plain
|
|
|
|
|
// HTTP after a failed HTTPS attempt, indicating the registry is plain HTTP.
|
|
|
|
|
func (c *Client) forcedHTTP() bool {
|
|
|
|
|
if c.httpClient == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if ft, ok := c.httpClient.Transport.(*fallbackTransport); ok {
|
|
|
|
|
return ft.forcedHTTP()
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoginOptBasicAuth returns a function that sets the username/password settings on login
|
|
|
|
|
func LoginOptBasicAuth(username string, password string) LoginOption {
|
|
|
|
|
return func(o *loginOperation) {
|
|
|
|
|
|