Add a daemon flag to prevent contact with v1 registries.
| ... | ... |
@@ -100,3 +100,9 @@ docker was automatically creating the `/host/path` if it didn't already exist. |
| 100 | 100 |
|
| 101 | 101 |
This auto-creation of the host path is deprecated and docker will error out if |
| 102 | 102 |
the path does not exist. |
| 103 |
+ |
|
| 104 |
+### Interacting with V1 registries |
|
| 105 |
+ |
|
| 106 |
+Version 1.9 adds a flag (`--no-legacy-registry=false`) which prevents the docker daemon from `pull`, `push`, and `login` operations against v1 registries. Though disabled by default, this signals the intent to deprecate the v1 protocol. |
|
| 107 |
+ |
|
| 108 |
+ |
| ... | ... |
@@ -49,6 +49,7 @@ weight=1 |
| 49 | 49 |
--log-driver="json-file" Default driver for container logs |
| 50 | 50 |
--log-opt=[] Log driver specific options |
| 51 | 51 |
--mtu=0 Set the containers network MTU |
| 52 |
+ --no-legacy-registry=false Do not contact legacy registries |
|
| 52 | 53 |
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file |
| 53 | 54 |
--registry-mirror=[] Preferred Docker registry mirror |
| 54 | 55 |
-s, --storage-driver="" Storage driver to use |
| ... | ... |
@@ -457,6 +458,10 @@ because its use creates security vulnerabilities it should ONLY be enabled for |
| 457 | 457 |
testing purposes. For increased security, users should add their CA to their |
| 458 | 458 |
system's list of trusted CAs instead of enabling `--insecure-registry`. |
| 459 | 459 |
|
| 460 |
+## Legacy Registries |
|
| 461 |
+ |
|
| 462 |
+Enabling `--no-legacy-registry` forces a docker daemon to only interact with registries which support the V2 protocol. Specifically, the daemon will not attempt `push`, `pull` and `login` to v1 registries. The exception to this is `search` which can still be performed on v1 registries. |
|
| 463 |
+ |
|
| 460 | 464 |
## Running a Docker daemon behind a HTTPS_PROXY |
| 461 | 465 |
|
| 462 | 466 |
When running inside a LAN that uses a `HTTPS` proxy, the Docker Hub |
| ... | ... |
@@ -60,6 +60,9 @@ func (p *v1Puller) Pull(tag string) (fallback bool, err error) {
|
| 60 | 60 |
// TODO(dmcgowan): Check if should fallback |
| 61 | 61 |
return false, err |
| 62 | 62 |
} |
| 63 |
+ out := p.config.OutStream |
|
| 64 |
+ out.Write(p.sf.FormatStatus("", "%s: this image was pulled from a legacy registry. Important: This registry version will not be supported in future versions of docker.", p.repoInfo.CanonicalName))
|
|
| 65 |
+ |
|
| 63 | 66 |
return false, nil |
| 64 | 67 |
} |
| 65 | 68 |
|
| ... | ... |
@@ -32,10 +32,13 @@ func init() {
|
| 32 | 32 |
type DockerRegistrySuite struct {
|
| 33 | 33 |
ds *DockerSuite |
| 34 | 34 |
reg *testRegistryV2 |
| 35 |
+ d *Daemon |
|
| 35 | 36 |
} |
| 36 | 37 |
|
| 37 | 38 |
func (s *DockerRegistrySuite) SetUpTest(c *check.C) {
|
| 39 |
+ testRequires(c, DaemonIsLinux) |
|
| 38 | 40 |
s.reg = setupRegistry(c) |
| 41 |
+ s.d = NewDaemon(c) |
|
| 39 | 42 |
} |
| 40 | 43 |
|
| 41 | 44 |
func (s *DockerRegistrySuite) TearDownTest(c *check.C) {
|
| ... | ... |
@@ -45,6 +48,7 @@ func (s *DockerRegistrySuite) TearDownTest(c *check.C) {
|
| 45 | 45 |
if s.ds != nil {
|
| 46 | 46 |
s.ds.TearDownTest(c) |
| 47 | 47 |
} |
| 48 |
+ s.d.Stop() |
|
| 48 | 49 |
} |
| 49 | 50 |
|
| 50 | 51 |
func init() {
|
| 51 | 52 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,147 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "github.com/go-check/check" |
|
| 5 |
+ "io/ioutil" |
|
| 6 |
+ "net/http" |
|
| 7 |
+ "os" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func makefile(contents string) (string, func(), error) {
|
|
| 11 |
+ cleanup := func() {
|
|
| 12 |
+ |
|
| 13 |
+ } |
|
| 14 |
+ |
|
| 15 |
+ f, err := ioutil.TempFile(".", "tmp")
|
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ return "", cleanup, err |
|
| 18 |
+ } |
|
| 19 |
+ err = ioutil.WriteFile(f.Name(), []byte(contents), os.ModePerm) |
|
| 20 |
+ if err != nil {
|
|
| 21 |
+ return "", cleanup, err |
|
| 22 |
+ } |
|
| 23 |
+ |
|
| 24 |
+ cleanup = func() {
|
|
| 25 |
+ err := os.Remove(f.Name()) |
|
| 26 |
+ if err != nil {
|
|
| 27 |
+ fmt.Println("Error removing tmpfile")
|
|
| 28 |
+ } |
|
| 29 |
+ } |
|
| 30 |
+ return f.Name(), cleanup, nil |
|
| 31 |
+ |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+// TestV2Only ensures that a daemon in v2-only mode does not |
|
| 35 |
+// attempt to contact any v1 registry endpoints. |
|
| 36 |
+func (s *DockerRegistrySuite) TestV2Only(c *check.C) {
|
|
| 37 |
+ reg, err := newTestRegistry(c) |
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ c.Fatal(err.Error()) |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 43 |
+ w.WriteHeader(404) |
|
| 44 |
+ }) |
|
| 45 |
+ |
|
| 46 |
+ reg.registerHandler("/v1/.*", func(w http.ResponseWriter, r *http.Request) {
|
|
| 47 |
+ c.Fatal("V1 registry contacted")
|
|
| 48 |
+ }) |
|
| 49 |
+ |
|
| 50 |
+ repoName := fmt.Sprintf("%s/busybox", reg.hostport)
|
|
| 51 |
+ |
|
| 52 |
+ err = s.d.Start("--insecure-registry", reg.hostport, "--no-legacy-registry=true")
|
|
| 53 |
+ if err != nil {
|
|
| 54 |
+ c.Fatalf("Error starting daemon: %s", err.Error())
|
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.hostport))
|
|
| 58 |
+ if err != nil {
|
|
| 59 |
+ c.Fatalf("Unable to create test dockerfile")
|
|
| 60 |
+ } |
|
| 61 |
+ defer cleanup() |
|
| 62 |
+ |
|
| 63 |
+ s.d.Cmd("build", "--file", dockerfileName, ".")
|
|
| 64 |
+ |
|
| 65 |
+ s.d.Cmd("run", repoName)
|
|
| 66 |
+ s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport)
|
|
| 67 |
+ s.d.Cmd("tag", "busybox", repoName)
|
|
| 68 |
+ s.d.Cmd("push", repoName)
|
|
| 69 |
+ s.d.Cmd("pull", repoName)
|
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// TestV1 starts a daemon in 'normal' mode |
|
| 73 |
+// and ensure v1 endpoints are hit for the following operations: |
|
| 74 |
+// login, push, pull, build & run |
|
| 75 |
+func (s *DockerRegistrySuite) TestV1(c *check.C) {
|
|
| 76 |
+ reg, err := newTestRegistry(c) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ c.Fatal(err.Error()) |
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ v2Pings := 0 |
|
| 82 |
+ reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 83 |
+ v2Pings++ |
|
| 84 |
+ // V2 ping 404 causes fallback to v1 |
|
| 85 |
+ w.WriteHeader(404) |
|
| 86 |
+ }) |
|
| 87 |
+ |
|
| 88 |
+ v1Pings := 0 |
|
| 89 |
+ reg.registerHandler("/v1/_ping", func(w http.ResponseWriter, r *http.Request) {
|
|
| 90 |
+ v1Pings++ |
|
| 91 |
+ }) |
|
| 92 |
+ |
|
| 93 |
+ v1Logins := 0 |
|
| 94 |
+ reg.registerHandler("/v1/users/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 95 |
+ v1Logins++ |
|
| 96 |
+ }) |
|
| 97 |
+ |
|
| 98 |
+ v1Repo := 0 |
|
| 99 |
+ reg.registerHandler("/v1/repositories/busybox/", func(w http.ResponseWriter, r *http.Request) {
|
|
| 100 |
+ v1Repo++ |
|
| 101 |
+ }) |
|
| 102 |
+ |
|
| 103 |
+ reg.registerHandler("/v1/repositories/busybox/images", func(w http.ResponseWriter, r *http.Request) {
|
|
| 104 |
+ v1Repo++ |
|
| 105 |
+ }) |
|
| 106 |
+ |
|
| 107 |
+ err = s.d.Start("--insecure-registry", reg.hostport, "--no-legacy-registry=false")
|
|
| 108 |
+ if err != nil {
|
|
| 109 |
+ c.Fatalf("Error starting daemon: %s", err.Error())
|
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.hostport))
|
|
| 113 |
+ if err != nil {
|
|
| 114 |
+ c.Fatalf("Unable to create test dockerfile")
|
|
| 115 |
+ } |
|
| 116 |
+ defer cleanup() |
|
| 117 |
+ |
|
| 118 |
+ s.d.Cmd("build", "--file", dockerfileName, ".")
|
|
| 119 |
+ if v1Repo == 0 {
|
|
| 120 |
+ c.Errorf("Expected v1 repository access after build")
|
|
| 121 |
+ } |
|
| 122 |
+ |
|
| 123 |
+ repoName := fmt.Sprintf("%s/busybox", reg.hostport)
|
|
| 124 |
+ s.d.Cmd("run", repoName)
|
|
| 125 |
+ if v1Repo == 1 {
|
|
| 126 |
+ c.Errorf("Expected v1 repository access after run")
|
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport)
|
|
| 130 |
+ if v1Logins == 0 {
|
|
| 131 |
+ c.Errorf("Expected v1 login attempt")
|
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ s.d.Cmd("tag", "busybox", repoName)
|
|
| 135 |
+ s.d.Cmd("push", repoName)
|
|
| 136 |
+ |
|
| 137 |
+ if v1Repo != 2 || v1Pings != 1 {
|
|
| 138 |
+ c.Error("Not all endpoints contacted after push", v1Repo, v1Pings)
|
|
| 139 |
+ } |
|
| 140 |
+ |
|
| 141 |
+ s.d.Cmd("pull", repoName)
|
|
| 142 |
+ if v1Repo != 3 {
|
|
| 143 |
+ c.Errorf("Expected v1 repository access after pull")
|
|
| 144 |
+ } |
|
| 145 |
+ |
|
| 146 |
+} |
| 0 | 147 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package main |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "net/http" |
|
| 4 |
+ "net/http/httptest" |
|
| 5 |
+ "regexp" |
|
| 6 |
+ "strings" |
|
| 7 |
+ "sync" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/go-check/check" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+type handlerFunc func(w http.ResponseWriter, r *http.Request) |
|
| 13 |
+ |
|
| 14 |
+type testRegistry struct {
|
|
| 15 |
+ server *httptest.Server |
|
| 16 |
+ hostport string |
|
| 17 |
+ handlers map[string]handlerFunc |
|
| 18 |
+ mu sync.Mutex |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func (tr *testRegistry) registerHandler(path string, h handlerFunc) {
|
|
| 22 |
+ tr.mu.Lock() |
|
| 23 |
+ defer tr.mu.Unlock() |
|
| 24 |
+ tr.handlers[path] = h |
|
| 25 |
+} |
|
| 26 |
+ |
|
| 27 |
+func newTestRegistry(c *check.C) (*testRegistry, error) {
|
|
| 28 |
+ testReg := &testRegistry{handlers: make(map[string]handlerFunc)}
|
|
| 29 |
+ |
|
| 30 |
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
| 31 |
+ url := r.URL.String() |
|
| 32 |
+ |
|
| 33 |
+ var matched bool |
|
| 34 |
+ var err error |
|
| 35 |
+ for re, function := range testReg.handlers {
|
|
| 36 |
+ matched, err = regexp.MatchString(re, url) |
|
| 37 |
+ if err != nil {
|
|
| 38 |
+ c.Fatalf("Error with handler regexp")
|
|
| 39 |
+ return |
|
| 40 |
+ } |
|
| 41 |
+ if matched {
|
|
| 42 |
+ function(w, r) |
|
| 43 |
+ break |
|
| 44 |
+ } |
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ if !matched {
|
|
| 48 |
+ c.Fatal("Unable to match", url, "with regexp")
|
|
| 49 |
+ } |
|
| 50 |
+ })) |
|
| 51 |
+ |
|
| 52 |
+ testReg.server = ts |
|
| 53 |
+ testReg.hostport = strings.Replace(ts.URL, "http://", "", 1) |
|
| 54 |
+ return testReg, nil |
|
| 55 |
+} |
| ... | ... |
@@ -40,6 +40,7 @@ docker-daemon - Enable daemon mode |
| 40 | 40 |
[**--log-driver**[=*json-file*]] |
| 41 | 41 |
[**--log-opt**[=*map[]*]] |
| 42 | 42 |
[**--mtu**[=*0*]] |
| 43 |
+[**--no-legacy-registry**[=*false*]] |
|
| 43 | 44 |
[**-p**|**--pidfile**[=*/var/run/docker.pid*]] |
| 44 | 45 |
[**--registry-mirror**[=*[]*]] |
| 45 | 46 |
[**-s**|**--storage-driver**[=*STORAGE-DRIVER*]] |
| ... | ... |
@@ -170,6 +171,9 @@ unix://[/path/to/socket] to use. |
| 170 | 170 |
**--mtu**=VALUE |
| 171 | 171 |
Set the containers network mtu. Default is `0`. |
| 172 | 172 |
|
| 173 |
+**--no-legacy-registry**=*true*|*false* |
|
| 174 |
+ Do not contact legacy registries |
|
| 175 |
+ |
|
| 173 | 176 |
**-p**, **--pidfile**="" |
| 174 | 177 |
Path to use for daemon PID file. Default is `/var/run/docker.pid` |
| 175 | 178 |
|
| ... | ... |
@@ -44,6 +44,10 @@ var ( |
| 44 | 44 |
ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
|
| 45 | 45 |
|
| 46 | 46 |
emptyServiceConfig = NewServiceConfig(nil) |
| 47 |
+ |
|
| 48 |
+ // V2Only controls access to legacy registries. If it is set to true via the |
|
| 49 |
+ // command line flag the daemon will not attempt to contact v1 legacy registries |
|
| 50 |
+ V2Only = false |
|
| 47 | 51 |
) |
| 48 | 52 |
|
| 49 | 53 |
// InstallFlags adds command-line options to the top-level flag parser for |
| ... | ... |
@@ -53,6 +57,7 @@ func (options *Options) InstallFlags(cmd *flag.FlagSet, usageFn func(string) str |
| 53 | 53 |
cmd.Var(&options.Mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
|
| 54 | 54 |
options.InsecureRegistries = opts.NewListOpts(ValidateIndexName) |
| 55 | 55 |
cmd.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
|
| 56 |
+ cmd.BoolVar(&V2Only, []string{"-no-legacy-registry"}, false, "Do not contact legacy registries")
|
|
| 56 | 57 |
} |
| 57 | 58 |
|
| 58 | 59 |
type netIPNet net.IPNet |
| ... | ... |
@@ -42,8 +42,9 @@ func scanForAPIVersion(address string) (string, APIVersion) {
|
| 42 | 42 |
return address, APIVersionUnknown |
| 43 | 43 |
} |
| 44 | 44 |
|
| 45 |
-// NewEndpoint parses the given address to return a registry endpoint. |
|
| 46 |
-func NewEndpoint(index *IndexInfo, metaHeaders http.Header) (*Endpoint, error) {
|
|
| 45 |
+// NewEndpoint parses the given address to return a registry endpoint. v can be used to |
|
| 46 |
+// specify a specific endpoint version |
|
| 47 |
+func NewEndpoint(index *IndexInfo, metaHeaders http.Header, v APIVersion) (*Endpoint, error) {
|
|
| 47 | 48 |
tlsConfig, err := newTLSConfig(index.Name, index.Secure) |
| 48 | 49 |
if err != nil {
|
| 49 | 50 |
return nil, err |
| ... | ... |
@@ -52,6 +53,9 @@ func NewEndpoint(index *IndexInfo, metaHeaders http.Header) (*Endpoint, error) {
|
| 52 | 52 |
if err != nil {
|
| 53 | 53 |
return nil, err |
| 54 | 54 |
} |
| 55 |
+ if v != APIVersionUnknown {
|
|
| 56 |
+ endpoint.Version = v |
|
| 57 |
+ } |
|
| 55 | 58 |
if err := validateEndpoint(endpoint); err != nil {
|
| 56 | 59 |
return nil, err |
| 57 | 60 |
} |
| ... | ... |
@@ -111,11 +115,6 @@ func newEndpoint(address string, tlsConfig *tls.Config, metaHeaders http.Header) |
| 111 | 111 |
return endpoint, nil |
| 112 | 112 |
} |
| 113 | 113 |
|
| 114 |
-// GetEndpoint returns a new endpoint with the specified headers |
|
| 115 |
-func (repoInfo *RepositoryInfo) GetEndpoint(metaHeaders http.Header) (*Endpoint, error) {
|
|
| 116 |
- return NewEndpoint(repoInfo.Index, metaHeaders) |
|
| 117 |
-} |
|
| 118 |
- |
|
| 119 | 114 |
// Endpoint stores basic information about a registry endpoint. |
| 120 | 115 |
type Endpoint struct {
|
| 121 | 116 |
client *http.Client |
| ... | ... |
@@ -49,6 +49,10 @@ func init() {
|
| 49 | 49 |
httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})
|
| 50 | 50 |
|
| 51 | 51 |
dockerUserAgent = useragent.AppendVersions("", httpVersion...)
|
| 52 |
+ |
|
| 53 |
+ if runtime.GOOS != "linux" {
|
|
| 54 |
+ V2Only = true |
|
| 55 |
+ } |
|
| 52 | 56 |
} |
| 53 | 57 |
|
| 54 | 58 |
func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
|
| ... | ... |
@@ -23,7 +23,7 @@ const ( |
| 23 | 23 |
|
| 24 | 24 |
func spawnTestRegistrySession(t *testing.T) *Session {
|
| 25 | 25 |
authConfig := &cliconfig.AuthConfig{}
|
| 26 |
- endpoint, err := NewEndpoint(makeIndex("/v1/"), nil)
|
|
| 26 |
+ endpoint, err := NewEndpoint(makeIndex("/v1/"), nil, APIVersionUnknown)
|
|
| 27 | 27 |
if err != nil {
|
| 28 | 28 |
t.Fatal(err) |
| 29 | 29 |
} |
| ... | ... |
@@ -50,7 +50,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
|
| 50 | 50 |
|
| 51 | 51 |
func TestPingRegistryEndpoint(t *testing.T) {
|
| 52 | 52 |
testPing := func(index *IndexInfo, expectedStandalone bool, assertMessage string) {
|
| 53 |
- ep, err := NewEndpoint(index, nil) |
|
| 53 |
+ ep, err := NewEndpoint(index, nil, APIVersionUnknown) |
|
| 54 | 54 |
if err != nil {
|
| 55 | 55 |
t.Fatal(err) |
| 56 | 56 |
} |
| ... | ... |
@@ -70,7 +70,7 @@ func TestPingRegistryEndpoint(t *testing.T) {
|
| 70 | 70 |
func TestEndpoint(t *testing.T) {
|
| 71 | 71 |
// Simple wrapper to fail test if err != nil |
| 72 | 72 |
expandEndpoint := func(index *IndexInfo) *Endpoint {
|
| 73 |
- endpoint, err := NewEndpoint(index, nil) |
|
| 73 |
+ endpoint, err := NewEndpoint(index, nil, APIVersionUnknown) |
|
| 74 | 74 |
if err != nil {
|
| 75 | 75 |
t.Fatal(err) |
| 76 | 76 |
} |
| ... | ... |
@@ -79,7 +79,7 @@ func TestEndpoint(t *testing.T) {
|
| 79 | 79 |
|
| 80 | 80 |
assertInsecureIndex := func(index *IndexInfo) {
|
| 81 | 81 |
index.Secure = true |
| 82 |
- _, err := NewEndpoint(index, nil) |
|
| 82 |
+ _, err := NewEndpoint(index, nil, APIVersionUnknown) |
|
| 83 | 83 |
assertNotEqual(t, err, nil, index.Name+": Expected error for insecure index") |
| 84 | 84 |
assertEqual(t, strings.Contains(err.Error(), "insecure-registry"), true, index.Name+": Expected insecure-registry error for insecure index") |
| 85 | 85 |
index.Secure = false |
| ... | ... |
@@ -87,7 +87,7 @@ func TestEndpoint(t *testing.T) {
|
| 87 | 87 |
|
| 88 | 88 |
assertSecureIndex := func(index *IndexInfo) {
|
| 89 | 89 |
index.Secure = true |
| 90 |
- _, err := NewEndpoint(index, nil) |
|
| 90 |
+ _, err := NewEndpoint(index, nil, APIVersionUnknown) |
|
| 91 | 91 |
assertNotEqual(t, err, nil, index.Name+": Expected cert error for secure index") |
| 92 | 92 |
assertEqual(t, strings.Contains(err.Error(), "certificate signed by unknown authority"), true, index.Name+": Expected cert error for secure index") |
| 93 | 93 |
index.Secure = false |
| ... | ... |
@@ -153,7 +153,7 @@ func TestEndpoint(t *testing.T) {
|
| 153 | 153 |
} |
| 154 | 154 |
for _, address := range badEndpoints {
|
| 155 | 155 |
index.Name = address |
| 156 |
- _, err := NewEndpoint(index, nil) |
|
| 156 |
+ _, err := NewEndpoint(index, nil, APIVersionUnknown) |
|
| 157 | 157 |
checkNotEqual(t, err, nil, "Expected error while expanding bad endpoint") |
| 158 | 158 |
} |
| 159 | 159 |
} |
| ... | ... |
@@ -2,15 +2,11 @@ package registry |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"crypto/tls" |
| 5 |
- "fmt" |
|
| 6 | 5 |
"net/http" |
| 7 | 6 |
"net/url" |
| 8 |
- "runtime" |
|
| 9 |
- "strings" |
|
| 10 | 7 |
|
| 11 | 8 |
"github.com/docker/distribution/registry/client/auth" |
| 12 | 9 |
"github.com/docker/docker/cliconfig" |
| 13 |
- "github.com/docker/docker/pkg/tlsconfig" |
|
| 14 | 10 |
) |
| 15 | 11 |
|
| 16 | 12 |
// Service is a registry service. It tracks configuration data such as a list |
| ... | ... |
@@ -40,7 +36,14 @@ func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
|
| 40 | 40 |
if err != nil {
|
| 41 | 41 |
return "", err |
| 42 | 42 |
} |
| 43 |
- endpoint, err := NewEndpoint(index, nil) |
|
| 43 |
+ |
|
| 44 |
+ endpointVersion := APIVersion(APIVersionUnknown) |
|
| 45 |
+ if V2Only {
|
|
| 46 |
+ // Override the endpoint to only attempt a v2 ping |
|
| 47 |
+ endpointVersion = APIVersion2 |
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ endpoint, err := NewEndpoint(index, nil, endpointVersion) |
|
| 44 | 51 |
if err != nil {
|
| 45 | 52 |
return "", err |
| 46 | 53 |
} |
| ... | ... |
@@ -57,10 +60,11 @@ func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers |
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 | 59 |
// *TODO: Search multiple indexes. |
| 60 |
- endpoint, err := repoInfo.GetEndpoint(http.Header(headers)) |
|
| 60 |
+ endpoint, err := NewEndpoint(repoInfo.Index, http.Header(headers), APIVersionUnknown) |
|
| 61 | 61 |
if err != nil {
|
| 62 | 62 |
return nil, err |
| 63 | 63 |
} |
| 64 |
+ |
|
| 64 | 65 |
r, err := NewSession(endpoint.client, authConfig, endpoint) |
| 65 | 66 |
if err != nil {
|
| 66 | 67 |
return nil, err |
| ... | ... |
@@ -132,97 +136,20 @@ func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint, |
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 | 134 |
func (s *Service) lookupEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
| 135 |
- var cfg = tlsconfig.ServerDefault |
|
| 136 |
- tlsConfig := &cfg |
|
| 137 |
- if strings.HasPrefix(repoName, DefaultNamespace+"/") {
|
|
| 138 |
- // v2 mirrors |
|
| 139 |
- for _, mirror := range s.Config.Mirrors {
|
|
| 140 |
- mirrorTLSConfig, err := s.tlsConfigForMirror(mirror) |
|
| 141 |
- if err != nil {
|
|
| 142 |
- return nil, err |
|
| 143 |
- } |
|
| 144 |
- endpoints = append(endpoints, APIEndpoint{
|
|
| 145 |
- URL: mirror, |
|
| 146 |
- // guess mirrors are v2 |
|
| 147 |
- Version: APIVersion2, |
|
| 148 |
- Mirror: true, |
|
| 149 |
- TrimHostname: true, |
|
| 150 |
- TLSConfig: mirrorTLSConfig, |
|
| 151 |
- }) |
|
| 152 |
- } |
|
| 153 |
- // v2 registry |
|
| 154 |
- endpoints = append(endpoints, APIEndpoint{
|
|
| 155 |
- URL: DefaultV2Registry, |
|
| 156 |
- Version: APIVersion2, |
|
| 157 |
- Official: true, |
|
| 158 |
- TrimHostname: true, |
|
| 159 |
- TLSConfig: tlsConfig, |
|
| 160 |
- }) |
|
| 161 |
- if runtime.GOOS == "linux" { // do not inherit legacy API for OSes supported in the future
|
|
| 162 |
- // v1 registry |
|
| 163 |
- endpoints = append(endpoints, APIEndpoint{
|
|
| 164 |
- URL: DefaultV1Registry, |
|
| 165 |
- Version: APIVersion1, |
|
| 166 |
- Official: true, |
|
| 167 |
- TrimHostname: true, |
|
| 168 |
- TLSConfig: tlsConfig, |
|
| 169 |
- }) |
|
| 170 |
- } |
|
| 171 |
- return endpoints, nil |
|
| 172 |
- } |
|
| 173 |
- |
|
| 174 |
- slashIndex := strings.IndexRune(repoName, '/') |
|
| 175 |
- if slashIndex <= 0 {
|
|
| 176 |
- return nil, fmt.Errorf("invalid repo name: missing '/': %s", repoName)
|
|
| 177 |
- } |
|
| 178 |
- hostname := repoName[:slashIndex] |
|
| 179 |
- |
|
| 180 |
- tlsConfig, err = s.TLSConfig(hostname) |
|
| 135 |
+ endpoints, err = s.lookupV2Endpoints(repoName) |
|
| 181 | 136 |
if err != nil {
|
| 182 | 137 |
return nil, err |
| 183 | 138 |
} |
| 184 |
- isSecure := !tlsConfig.InsecureSkipVerify |
|
| 185 | 139 |
|
| 186 |
- v2Versions := []auth.APIVersion{
|
|
| 187 |
- {
|
|
| 188 |
- Type: "registry", |
|
| 189 |
- Version: "2.0", |
|
| 190 |
- }, |
|
| 191 |
- } |
|
| 192 |
- endpoints = []APIEndpoint{
|
|
| 193 |
- {
|
|
| 194 |
- URL: "https://" + hostname, |
|
| 195 |
- Version: APIVersion2, |
|
| 196 |
- TrimHostname: true, |
|
| 197 |
- TLSConfig: tlsConfig, |
|
| 198 |
- VersionHeader: DefaultRegistryVersionHeader, |
|
| 199 |
- Versions: v2Versions, |
|
| 200 |
- }, |
|
| 201 |
- {
|
|
| 202 |
- URL: "https://" + hostname, |
|
| 203 |
- Version: APIVersion1, |
|
| 204 |
- TrimHostname: true, |
|
| 205 |
- TLSConfig: tlsConfig, |
|
| 206 |
- }, |
|
| 140 |
+ if V2Only {
|
|
| 141 |
+ return endpoints, nil |
|
| 207 | 142 |
} |
| 208 | 143 |
|
| 209 |
- if !isSecure {
|
|
| 210 |
- endpoints = append(endpoints, APIEndpoint{
|
|
| 211 |
- URL: "http://" + hostname, |
|
| 212 |
- Version: APIVersion2, |
|
| 213 |
- TrimHostname: true, |
|
| 214 |
- // used to check if supposed to be secure via InsecureSkipVerify |
|
| 215 |
- TLSConfig: tlsConfig, |
|
| 216 |
- VersionHeader: DefaultRegistryVersionHeader, |
|
| 217 |
- Versions: v2Versions, |
|
| 218 |
- }, APIEndpoint{
|
|
| 219 |
- URL: "http://" + hostname, |
|
| 220 |
- Version: APIVersion1, |
|
| 221 |
- TrimHostname: true, |
|
| 222 |
- // used to check if supposed to be secure via InsecureSkipVerify |
|
| 223 |
- TLSConfig: tlsConfig, |
|
| 224 |
- }) |
|
| 144 |
+ legacyEndpoints, err := s.lookupV1Endpoints(repoName) |
|
| 145 |
+ if err != nil {
|
|
| 146 |
+ return nil, err |
|
| 225 | 147 |
} |
| 148 |
+ endpoints = append(endpoints, legacyEndpoints...) |
|
| 226 | 149 |
|
| 227 | 150 |
return endpoints, nil |
| 228 | 151 |
} |
| 229 | 152 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,54 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/docker/pkg/tlsconfig" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func (s *Service) lookupV1Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
|
| 10 |
+ var cfg = tlsconfig.ServerDefault |
|
| 11 |
+ tlsConfig := &cfg |
|
| 12 |
+ if strings.HasPrefix(repoName, DefaultNamespace+"/") {
|
|
| 13 |
+ endpoints = append(endpoints, APIEndpoint{
|
|
| 14 |
+ URL: DefaultV1Registry, |
|
| 15 |
+ Version: APIVersion1, |
|
| 16 |
+ Official: true, |
|
| 17 |
+ TrimHostname: true, |
|
| 18 |
+ TLSConfig: tlsConfig, |
|
| 19 |
+ }) |
|
| 20 |
+ return endpoints, nil |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ slashIndex := strings.IndexRune(repoName, '/') |
|
| 24 |
+ if slashIndex <= 0 {
|
|
| 25 |
+ return nil, fmt.Errorf("invalid repo name: missing '/': %s", repoName)
|
|
| 26 |
+ } |
|
| 27 |
+ hostname := repoName[:slashIndex] |
|
| 28 |
+ |
|
| 29 |
+ tlsConfig, err = s.TLSConfig(hostname) |
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ return nil, err |
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ endpoints = []APIEndpoint{
|
|
| 35 |
+ {
|
|
| 36 |
+ URL: "https://" + hostname, |
|
| 37 |
+ Version: APIVersion1, |
|
| 38 |
+ TrimHostname: true, |
|
| 39 |
+ TLSConfig: tlsConfig, |
|
| 40 |
+ }, |
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ if tlsConfig.InsecureSkipVerify {
|
|
| 44 |
+ endpoints = append(endpoints, APIEndpoint{ // or this
|
|
| 45 |
+ URL: "http://" + hostname, |
|
| 46 |
+ Version: APIVersion1, |
|
| 47 |
+ TrimHostname: true, |
|
| 48 |
+ // used to check if supposed to be secure via InsecureSkipVerify |
|
| 49 |
+ TLSConfig: tlsConfig, |
|
| 50 |
+ }) |
|
| 51 |
+ } |
|
| 52 |
+ return endpoints, nil |
|
| 53 |
+} |
| 0 | 54 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,83 @@ |
| 0 |
+package registry |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/distribution/registry/client/auth" |
|
| 7 |
+ "github.com/docker/docker/pkg/tlsconfig" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+func (s *Service) lookupV2Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
|
|
| 11 |
+ var cfg = tlsconfig.ServerDefault |
|
| 12 |
+ tlsConfig := &cfg |
|
| 13 |
+ if strings.HasPrefix(repoName, DefaultNamespace+"/") {
|
|
| 14 |
+ // v2 mirrors |
|
| 15 |
+ for _, mirror := range s.Config.Mirrors {
|
|
| 16 |
+ mirrorTLSConfig, err := s.tlsConfigForMirror(mirror) |
|
| 17 |
+ if err != nil {
|
|
| 18 |
+ return nil, err |
|
| 19 |
+ } |
|
| 20 |
+ endpoints = append(endpoints, APIEndpoint{
|
|
| 21 |
+ URL: mirror, |
|
| 22 |
+ // guess mirrors are v2 |
|
| 23 |
+ Version: APIVersion2, |
|
| 24 |
+ Mirror: true, |
|
| 25 |
+ TrimHostname: true, |
|
| 26 |
+ TLSConfig: mirrorTLSConfig, |
|
| 27 |
+ }) |
|
| 28 |
+ } |
|
| 29 |
+ // v2 registry |
|
| 30 |
+ endpoints = append(endpoints, APIEndpoint{
|
|
| 31 |
+ URL: DefaultV2Registry, |
|
| 32 |
+ Version: APIVersion2, |
|
| 33 |
+ Official: true, |
|
| 34 |
+ TrimHostname: true, |
|
| 35 |
+ TLSConfig: tlsConfig, |
|
| 36 |
+ }) |
|
| 37 |
+ |
|
| 38 |
+ return endpoints, nil |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ slashIndex := strings.IndexRune(repoName, '/') |
|
| 42 |
+ if slashIndex <= 0 {
|
|
| 43 |
+ return nil, fmt.Errorf("invalid repo name: missing '/': %s", repoName)
|
|
| 44 |
+ } |
|
| 45 |
+ hostname := repoName[:slashIndex] |
|
| 46 |
+ |
|
| 47 |
+ tlsConfig, err = s.TLSConfig(hostname) |
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ return nil, err |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ v2Versions := []auth.APIVersion{
|
|
| 53 |
+ {
|
|
| 54 |
+ Type: "registry", |
|
| 55 |
+ Version: "2.0", |
|
| 56 |
+ }, |
|
| 57 |
+ } |
|
| 58 |
+ endpoints = []APIEndpoint{
|
|
| 59 |
+ {
|
|
| 60 |
+ URL: "https://" + hostname, |
|
| 61 |
+ Version: APIVersion2, |
|
| 62 |
+ TrimHostname: true, |
|
| 63 |
+ TLSConfig: tlsConfig, |
|
| 64 |
+ VersionHeader: DefaultRegistryVersionHeader, |
|
| 65 |
+ Versions: v2Versions, |
|
| 66 |
+ }, |
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ if tlsConfig.InsecureSkipVerify {
|
|
| 70 |
+ endpoints = append(endpoints, APIEndpoint{
|
|
| 71 |
+ URL: "http://" + hostname, |
|
| 72 |
+ Version: APIVersion2, |
|
| 73 |
+ TrimHostname: true, |
|
| 74 |
+ // used to check if supposed to be secure via InsecureSkipVerify |
|
| 75 |
+ TLSConfig: tlsConfig, |
|
| 76 |
+ VersionHeader: DefaultRegistryVersionHeader, |
|
| 77 |
+ Versions: v2Versions, |
|
| 78 |
+ }) |
|
| 79 |
+ } |
|
| 80 |
+ |
|
| 81 |
+ return endpoints, nil |
|
| 82 |
+} |