From f5ce3d1eb07e4e1441be9ebac2af3f01b5e9eaea Mon Sep 17 00:00:00 2001 From: Liron Levin Date: Wed, 4 May 2016 09:58:52 +0300 Subject: [PATCH] Enable providing username/password for read operations Currently notary fails to authenticate private Docker repositories. That is, notary fetch a token from `auth.docker.io` but receives 401 (since basic authentication is required to fetch the bearer token for private Docker repositories). Since the `AuthenticationHandler` succeed, we must retry the entire flow with a different handler. Signed-off-by: Liron Levin --- cmd/notary/tuf.go | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index 36e0630371..ff1b0c6e0b 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -666,8 +666,16 @@ func tokenAuth(trustServerURL string, baseTransport *http.Transport, gun string, } tokenHandler := auth.NewTokenHandler(authTransport, ps, gun, actions...) basicHandler := auth.NewBasicHandler(ps) - modifier := transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) - return transport.NewTransport(baseTransport, modifier), nil + + modifier := auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler) + + if !readOnly { + return newAuthRoundTripper(transport.NewTransport(baseTransport, modifier)), nil + } + + // Try to authenticate read only repositories using basic username/password authentication + return newAuthRoundTripper(transport.NewTransport(baseTransport, modifier), + transport.NewTransport(baseTransport, auth.NewAuthorizer(challengeManager, auth.NewTokenHandler(authTransport, passwordStore{anonymous: false}, gun, actions...)))), nil } func getRemoteTrustServer(config *viper.Viper) string { @@ -703,3 +711,34 @@ func getTrustPinning(config *viper.Viper) (trustpinning.TrustPinConfig, error) { Certs: resultCertMap, }, nil } + +// authRoundTripper tries to authenticate the requests via multiple HTTP transactions (until first succeed) +type authRoundTripper struct { + trippers []http.RoundTripper +} + +func newAuthRoundTripper(trippers ...http.RoundTripper) http.RoundTripper { + return &authRoundTripper{trippers: trippers} +} + +func (a *authRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + + var resp *http.Response + // Try all run all transactions + for _, t := range a.trippers { + var err error + resp, err = t.RoundTrip(req) + // Reject on error + if err != nil { + return resp, err + } + + // Stop when request is authorized/unknown error + if resp.StatusCode != http.StatusUnauthorized { + return resp, nil + } + } + + // Return the last response + return resp, nil +}