d43e61758a
This field was used when the code supported both "v1" and "v2" registries. We no longer support v1 registries, and the only v1 endpoint that's still used is for the legacy "search" endpoint, which does not use the APIEndpoint type. As no code is using this field, and the value will always be set to "v2", we can deprecated the Version field. I'm keeping this field for 1 release, to give notice to any potential external consumer, after which we can delete it. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
120 lines
3.1 KiB
Go
120 lines
3.1 KiB
Go
package distribution // import "github.com/docker/docker/distribution"
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/containerd/containerd/log"
|
|
"github.com/docker/distribution/reference"
|
|
"github.com/docker/docker/api/types/registry"
|
|
registrypkg "github.com/docker/docker/registry"
|
|
)
|
|
|
|
const secretRegistryToken = "mysecrettoken"
|
|
|
|
type tokenPassThruHandler struct {
|
|
reached bool
|
|
gotToken bool
|
|
shouldSend401 func(url string) bool
|
|
}
|
|
|
|
func (h *tokenPassThruHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
h.reached = true
|
|
if strings.Contains(r.Header.Get("Authorization"), secretRegistryToken) {
|
|
log.G(context.TODO()).Debug("Detected registry token in auth header")
|
|
h.gotToken = true
|
|
}
|
|
if h.shouldSend401 == nil || h.shouldSend401(r.RequestURI) {
|
|
w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`)
|
|
w.WriteHeader(401)
|
|
}
|
|
}
|
|
|
|
func testTokenPassThru(t *testing.T, ts *httptest.Server) {
|
|
uri, err := url.Parse(ts.URL)
|
|
if err != nil {
|
|
t.Fatalf("could not parse url from test server: %v", err)
|
|
}
|
|
|
|
endpoint := registrypkg.APIEndpoint{
|
|
Mirror: false,
|
|
URL: uri,
|
|
Official: false,
|
|
TrimHostname: false,
|
|
TLSConfig: nil,
|
|
}
|
|
n, _ := reference.ParseNormalizedNamed("testremotename")
|
|
repoInfo := ®istrypkg.RepositoryInfo{
|
|
Name: n,
|
|
Index: ®istry.IndexInfo{
|
|
Name: "testrepo",
|
|
Mirrors: nil,
|
|
Secure: false,
|
|
Official: false,
|
|
},
|
|
Official: false,
|
|
}
|
|
imagePullConfig := &ImagePullConfig{
|
|
Config: Config{
|
|
MetaHeaders: http.Header{},
|
|
AuthConfig: ®istry.AuthConfig{
|
|
RegistryToken: secretRegistryToken,
|
|
},
|
|
},
|
|
}
|
|
p := newPuller(endpoint, repoInfo, imagePullConfig, nil)
|
|
ctx := context.Background()
|
|
p.repo, err = newRepository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
log.G(ctx).Debug("About to pull")
|
|
// We expect it to fail, since we haven't mock'd the full registry exchange in our handler above
|
|
tag, _ := reference.WithTag(n, "tag_goes_here")
|
|
_ = p.pullRepository(ctx, tag)
|
|
}
|
|
|
|
func TestTokenPassThru(t *testing.T) {
|
|
handler := &tokenPassThruHandler{shouldSend401: func(url string) bool { return url == "/v2/" }}
|
|
ts := httptest.NewServer(handler)
|
|
defer ts.Close()
|
|
|
|
testTokenPassThru(t, ts)
|
|
|
|
if !handler.reached {
|
|
t.Fatal("Handler not reached")
|
|
}
|
|
if !handler.gotToken {
|
|
t.Fatal("Failed to receive registry token")
|
|
}
|
|
}
|
|
|
|
func TestTokenPassThruDifferentHost(t *testing.T) {
|
|
handler := new(tokenPassThruHandler)
|
|
ts := httptest.NewServer(handler)
|
|
defer ts.Close()
|
|
|
|
tsredirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.RequestURI == "/v2/" {
|
|
w.Header().Set("WWW-Authenticate", `Bearer realm="foorealm"`)
|
|
w.WriteHeader(401)
|
|
return
|
|
}
|
|
http.Redirect(w, r, ts.URL+r.URL.Path, http.StatusMovedPermanently)
|
|
}))
|
|
defer tsredirect.Close()
|
|
|
|
testTokenPassThru(t, tsredirect)
|
|
|
|
if !handler.reached {
|
|
t.Fatal("Handler not reached")
|
|
}
|
|
if handler.gotToken {
|
|
t.Fatal("Redirect should not forward Authorization header to another host")
|
|
}
|
|
}
|