Merge pull request #1502 from RichardScothern/auth-url-endpoints
URL parse auth endpoints to normalize hostname to lowercase.master
						commit
						a5d372535e
					
				|  | @ -25,7 +25,7 @@ type Challenge struct { | ||||||
| type ChallengeManager interface { | type ChallengeManager interface { | ||||||
| 	// GetChallenges returns the challenges for the given
 | 	// GetChallenges returns the challenges for the given
 | ||||||
| 	// endpoint URL.
 | 	// endpoint URL.
 | ||||||
| 	GetChallenges(endpoint string) ([]Challenge, error) | 	GetChallenges(endpoint url.URL) ([]Challenge, error) | ||||||
| 
 | 
 | ||||||
| 	// AddResponse adds the response to the challenge
 | 	// AddResponse adds the response to the challenge
 | ||||||
| 	// manager. The challenges will be parsed out of
 | 	// manager. The challenges will be parsed out of
 | ||||||
|  | @ -48,8 +48,10 @@ func NewSimpleChallengeManager() ChallengeManager { | ||||||
| 
 | 
 | ||||||
| type simpleChallengeManager map[string][]Challenge | type simpleChallengeManager map[string][]Challenge | ||||||
| 
 | 
 | ||||||
| func (m simpleChallengeManager) GetChallenges(endpoint string) ([]Challenge, error) { | func (m simpleChallengeManager) GetChallenges(endpoint url.URL) ([]Challenge, error) { | ||||||
| 	challenges := m[endpoint] | 	endpoint.Host = strings.ToLower(endpoint.Host) | ||||||
|  | 
 | ||||||
|  | 	challenges := m[endpoint.String()] | ||||||
| 	return challenges, nil | 	return challenges, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -60,11 +62,10 @@ func (m simpleChallengeManager) AddResponse(resp *http.Response) error { | ||||||
| 	} | 	} | ||||||
| 	urlCopy := url.URL{ | 	urlCopy := url.URL{ | ||||||
| 		Path:   resp.Request.URL.Path, | 		Path:   resp.Request.URL.Path, | ||||||
| 		Host:   resp.Request.URL.Host, | 		Host:   strings.ToLower(resp.Request.URL.Host), | ||||||
| 		Scheme: resp.Request.URL.Scheme, | 		Scheme: resp.Request.URL.Scheme, | ||||||
| 	} | 	} | ||||||
| 	m[urlCopy.String()] = challenges | 	m[urlCopy.String()] = challenges | ||||||
| 
 |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,10 @@ | ||||||
| package auth | package auth | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"strings" | ||||||
| 	"testing" | 	"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") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -77,9 +77,7 @@ func (ea *endpointAuthorizer) ModifyRequest(req *http.Request) error { | ||||||
| 		Path:   req.URL.Path[:v2Root+4], | 		Path:   req.URL.Path[:v2Root+4], | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	pingEndpoint := ping.String() | 	challenges, err := ea.challenges.GetChallenges(ping) | ||||||
| 
 |  | ||||||
| 	challenges, err := ea.challenges.GetChallenges(pingEndpoint) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -319,6 +319,7 @@ func (buh *blobUploadHandler) blobUploadResponse(w http.ResponseWriter, r *http. | ||||||
| 
 | 
 | ||||||
| 	w.Header().Set("Docker-Upload-UUID", buh.UUID) | 	w.Header().Set("Docker-Upload-UUID", buh.UUID) | ||||||
| 	w.Header().Set("Location", uploadURL) | 	w.Header().Set("Location", uploadURL) | ||||||
|  | 
 | ||||||
| 	w.Header().Set("Content-Length", "0") | 	w.Header().Set("Content-Length", "0") | ||||||
| 	w.Header().Set("Range", fmt.Sprintf("0-%d", endRange)) | 	w.Header().Set("Range", fmt.Sprintf("0-%d", endRange)) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,13 +22,13 @@ import ( | ||||||
| type proxyingRegistry struct { | type proxyingRegistry struct { | ||||||
| 	embedded       distribution.Namespace // provides local registry functionality
 | 	embedded       distribution.Namespace // provides local registry functionality
 | ||||||
| 	scheduler      *scheduler.TTLExpirationScheduler | 	scheduler      *scheduler.TTLExpirationScheduler | ||||||
| 	remoteURL      string | 	remoteURL      url.URL | ||||||
| 	authChallenger authChallenger | 	authChallenger authChallenger | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewRegistryPullThroughCache creates a registry acting as a pull through cache
 | // 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) { | 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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -99,9 +99,9 @@ func NewRegistryPullThroughCache(ctx context.Context, registry distribution.Name | ||||||
| 	return &proxyingRegistry{ | 	return &proxyingRegistry{ | ||||||
| 		embedded:  registry, | 		embedded:  registry, | ||||||
| 		scheduler: s, | 		scheduler: s, | ||||||
| 		remoteURL: config.RemoteURL, | 		remoteURL: *remoteURL, | ||||||
| 		authChallenger: &remoteAuthChallenger{ | 		authChallenger: &remoteAuthChallenger{ | ||||||
| 			remoteURL: config.RemoteURL, | 			remoteURL: *remoteURL, | ||||||
| 			cm:        auth.NewSimpleChallengeManager(), | 			cm:        auth.NewSimpleChallengeManager(), | ||||||
| 			cs:        cs, | 			cs:        cs, | ||||||
| 		}, | 		}, | ||||||
|  | @ -131,7 +131,7 @@ func (pr *proxyingRegistry) Repository(ctx context.Context, name reference.Named | ||||||
| 		return nil, err | 		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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | @ -182,7 +182,7 @@ type authChallenger interface { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type remoteAuthChallenger struct { | type remoteAuthChallenger struct { | ||||||
| 	remoteURL string | 	remoteURL url.URL | ||||||
| 	sync.Mutex | 	sync.Mutex | ||||||
| 	cm auth.ChallengeManager | 	cm auth.ChallengeManager | ||||||
| 	cs auth.CredentialStore | 	cs auth.CredentialStore | ||||||
|  | @ -201,8 +201,9 @@ func (r *remoteAuthChallenger) tryEstablishChallenges(ctx context.Context) error | ||||||
| 	r.Lock() | 	r.Lock() | ||||||
| 	defer r.Unlock() | 	defer r.Unlock() | ||||||
| 
 | 
 | ||||||
| 	remoteURL := r.remoteURL + "/v2/" | 	remoteURL := r.remoteURL | ||||||
| 	challenges, err := r.cm.GetChallenges(remoteURL) | 	remoteURL.Path = "/v2/" | ||||||
|  | 	challenges, err := r.cm.GetChallenges(r.remoteURL) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | @ -212,7 +213,7 @@ func (r *remoteAuthChallenger) tryEstablishChallenges(ctx context.Context) error | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// establish challenge type with upstream
 | 	// 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 | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue