URL parse auth endpoints to normalize hostname to lowercase.
It is possible for a middlebox to lowercase the URL at somepoint causing a lookup in the auth challenges table to fail. Lowercase hostname before using as keys to challenge map. Signed-off-by: Richard Scothern <richard.scothern@gmail.com>master
							parent
							
								
									e430d77342
								
							
						
					
					
						commit
						8bd613aa11
					
				|  | @ -25,7 +25,7 @@ type Challenge struct { | |||
| type ChallengeManager interface { | ||||
| 	// GetChallenges returns the challenges for the given
 | ||||
| 	// endpoint URL.
 | ||||
| 	GetChallenges(endpoint string) ([]Challenge, error) | ||||
| 	GetChallenges(endpoint url.URL) ([]Challenge, error) | ||||
| 
 | ||||
| 	// AddResponse adds the response to the challenge
 | ||||
| 	// manager. The challenges will be parsed out of
 | ||||
|  | @ -48,8 +48,10 @@ func NewSimpleChallengeManager() ChallengeManager { | |||
| 
 | ||||
| type simpleChallengeManager map[string][]Challenge | ||||
| 
 | ||||
| func (m simpleChallengeManager) GetChallenges(endpoint string) ([]Challenge, error) { | ||||
| 	challenges := m[endpoint] | ||||
| func (m simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, error) { | ||||
| 	endpoint.Host = strings.ToLower(endpoint.Host) | ||||
| 
 | ||||
| 	challenges := m[endpoint.String()] | ||||
| 	return challenges, nil | ||||
| } | ||||
| 
 | ||||
|  | @ -60,11 +62,10 @@ func (m simpleChallengeManager) AddResponse(resp *http.Response) error { | |||
| 	} | ||||
| 	urlCopy := url.URL{ | ||||
| 		Path:   resp.Request.URL.Path, | ||||
| 		Host:   resp.Request.URL.Host, | ||||
| 		Host:   strings.ToLower(resp.Request.URL.Host), | ||||
| 		Scheme: resp.Request.URL.Scheme, | ||||
| 	} | ||||
| 	m[urlCopy.String()] = challenges | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,10 @@ | |||
| package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
|  | @ -36,3 +39,43 @@ func TestAuthChallengeParse(t *testing.T) { | |||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestAuthChallengeNormalization(t *testing.T) { | ||||
| 	testAuthChallengeNormalization(t, "reg.EXAMPLE.com") | ||||
| 	testAuthChallengeNormalization(t, "bɿɒʜɔiɿ-ɿɘƚƨim-ƚol-ɒ-ƨʞnɒʜƚ.com") | ||||
| } | ||||
| 
 | ||||
| func testAuthChallengeNormalization(t *testing.T, host string) { | ||||
| 
 | ||||
| 	scm := NewSimpleChallengeManager() | ||||
| 
 | ||||
| 	url, err := url.Parse(fmt.Sprintf("http://%s/v2/", host)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	resp := &http.Response{ | ||||
| 		Request: &http.Request{ | ||||
| 			URL: url, | ||||
| 		}, | ||||
| 		Header:     make(http.Header), | ||||
| 		StatusCode: http.StatusUnauthorized, | ||||
| 	} | ||||
| 	resp.Header.Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"https://%s/token\",service=\"registry.example.com\"", host)) | ||||
| 
 | ||||
| 	err = scm.AddResponse(resp) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	lowered := *url | ||||
| 	lowered.Host = strings.ToLower(lowered.Host) | ||||
| 	c, err := scm.GetChallenges(lowered) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(c) == 0 { | ||||
| 		t.Fatal("Expected challenge for lower-cased-host URL") | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -67,9 +67,7 @@ func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error { | |||
| 		Path:   req.URL.Path[:v2Root+4], | ||||
| 	} | ||||
| 
 | ||||
| 	pingEndpoint := ping.String() | ||||
| 
 | ||||
| 	challenges, err := ea.challenges.GetChallenges(pingEndpoint) | ||||
| 	challenges, err := ea.challenges.GetChallenges(ping) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  |  | |||
|  | @ -340,6 +340,7 @@ func (buh *blobUploadHandler) blobUploadResponse(w http.ResponseWriter, r *http. | |||
| 
 | ||||
| 	w.Header().Set("Docker-Upload-UUID", buh.UUID) | ||||
| 	w.Header().Set("Location", uploadURL) | ||||
| 
 | ||||
| 	w.Header().Set("Content-Length", "0") | ||||
| 	w.Header().Set("Range", fmt.Sprintf("0-%d", endRange)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,13 +22,13 @@ import ( | |||
| type proxyingRegistry struct { | ||||
| 	embedded       distribution.Namespace // provides local registry functionality
 | ||||
| 	scheduler      *scheduler.TTLExpirationScheduler | ||||
| 	remoteURL      string | ||||
| 	remoteURL      url.URL | ||||
| 	authChallenger authChallenger | ||||
| } | ||||
| 
 | ||||
| // NewRegistryPullThroughCache creates a registry acting as a pull through cache
 | ||||
| func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Namespace, driver driver.StorageDriver, config configuration.Proxy) (distribution.Namespace, error) { | ||||
| 	_, err := url.Parse(config.RemoteURL) | ||||
| 	remoteURL, err := url.Parse(config.RemoteURL) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | @ -99,9 +99,9 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name | |||
| 	return &proxyingRegistry{ | ||||
| 		embedded:  registry, | ||||
| 		scheduler: s, | ||||
| 		remoteURL: config.RemoteURL, | ||||
| 		remoteURL: *remoteURL, | ||||
| 		authChallenger: &remoteAuthChallenger{ | ||||
| 			remoteURL: config.RemoteURL, | ||||
| 			remoteURL: *remoteURL, | ||||
| 			cm:        auth.NewSimpleChallengeManager(), | ||||
| 			cs:        cs, | ||||
| 		}, | ||||
|  | @ -131,7 +131,7 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named | |||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	remoteRepo, err := client.NewRepository(ctx, name, pr.remoteURL, tr) | ||||
| 	remoteRepo, err := client.NewRepository(ctx, name, pr.remoteURL.String(), tr) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | @ -174,7 +174,7 @@ type authChallenger interface { | |||
| } | ||||
| 
 | ||||
| type remoteAuthChallenger struct { | ||||
| 	remoteURL string | ||||
| 	remoteURL url.URL | ||||
| 	sync.Mutex | ||||
| 	cm auth.ChallengeManager | ||||
| 	cs auth.CredentialStore | ||||
|  | @ -193,8 +193,9 @@ func (r *remoteAuthChallenger) tryEstablishChallenges(ctx context.Context) error | |||
| 	r.Lock() | ||||
| 	defer r.Unlock() | ||||
| 
 | ||||
| 	remoteURL := r.remoteURL + "/v2/" | ||||
| 	challenges, err := r.cm.GetChallenges(remoteURL) | ||||
| 	remoteURL := r.remoteURL | ||||
| 	remoteURL.Path = "/v2/" | ||||
| 	challenges, err := r.cm.GetChallenges(r.remoteURL) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | @ -204,7 +205,7 @@ func (r *remoteAuthChallenger) tryEstablishChallenges(ctx context.Context) error | |||
| 	} | ||||
| 
 | ||||
| 	// establish challenge type with upstream
 | ||||
| 	if err := ping(r.cm, remoteURL, challengeHeader); err != nil { | ||||
| 	if err := ping(r.cm, remoteURL.String(), challengeHeader); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue