diff --git a/lib/backend/registrybackend/blobclient.go b/lib/backend/registrybackend/blobclient.go index 2a879d593..276636643 100644 --- a/lib/backend/registrybackend/blobclient.go +++ b/lib/backend/registrybackend/blobclient.go @@ -75,42 +75,42 @@ func NewBlobClient(config Config) (*BlobClient, error) { // Stat sends a HEAD request to registry for a blob and returns the blob size. func (c *BlobClient) Stat(namespace, name string) (*core.BlobInfo, error) { - opt, err := c.authenticator.Authenticate(namespace) + opts, err := c.authenticator.Authenticate(namespace) if err != nil { return nil, fmt.Errorf("get security opt: %s", err) } - info, err := c.statHelper(namespace, name, _layerquery, opt) + info, err := c.statHelper(namespace, name, _layerquery, opts) if err != nil && err == backenderrors.ErrBlobNotFound { // Docker registry does not support querying manifests with blob path. log.Infof("Blob %s unknown to registry. Tring to stat manifest instead", name) - info, err = c.statHelper(namespace, name, _manifestquery, opt) + info, err = c.statHelper(namespace, name, _manifestquery, opts) } return info, err } // Download gets a blob from registry. func (c *BlobClient) Download(namespace, name string, dst io.Writer) error { - opt, err := c.authenticator.Authenticate(namespace) + opts, err := c.authenticator.Authenticate(namespace) if err != nil { return fmt.Errorf("get security opt: %s", err) } - err = c.downloadHelper(namespace, name, _layerquery, dst, opt) + err = c.downloadHelper(namespace, name, _layerquery, dst, opts) if err != nil && err == backenderrors.ErrBlobNotFound { // Docker registry does not support querying manifests with blob path. log.Infof("Blob %s unknown to registry. Tring to download manifest instead", name) - err = c.downloadHelper(namespace, name, _manifestquery, dst, opt) + err = c.downloadHelper(namespace, name, _manifestquery, dst, opts) } return err } -func (c *BlobClient) statHelper(namespace, name, query string, opt httputil.SendOption) (*core.BlobInfo, error) { +func (c *BlobClient) statHelper(namespace, name, query string, opts []httputil.SendOption) (*core.BlobInfo, error) { URL := fmt.Sprintf(query, c.config.Address, namespace, name) resp, err := httputil.Head( URL, - opt, - httputil.SendAcceptedCodes(http.StatusOK)) + append(opts, httputil.SendAcceptedCodes(http.StatusOK))..., + ) if err != nil { if httputil.IsNotFound(err) { return nil, backenderrors.ErrBlobNotFound @@ -125,13 +125,15 @@ func (c *BlobClient) statHelper(namespace, name, query string, opt httputil.Send return core.NewBlobInfo(size), nil } -func (c *BlobClient) downloadHelper(namespace, name, query string, dst io.Writer, opt httputil.SendOption) error { +func (c *BlobClient) downloadHelper(namespace, name, query string, dst io.Writer, opts []httputil.SendOption) error { URL := fmt.Sprintf(query, c.config.Address, namespace, name) resp, err := httputil.Get( URL, - opt, - httputil.SendAcceptedCodes(http.StatusOK), - httputil.SendTimeout(c.config.Timeout), + append( + opts, + httputil.SendAcceptedCodes(http.StatusOK), + httputil.SendTimeout(c.config.Timeout), + )..., ) if err != nil { if httputil.IsNotFound(err) { diff --git a/lib/backend/registrybackend/blobclient_test.go b/lib/backend/registrybackend/blobclient_test.go index fd26e3b27..65142bac5 100644 --- a/lib/backend/registrybackend/blobclient_test.go +++ b/lib/backend/registrybackend/blobclient_test.go @@ -55,7 +55,7 @@ func TestBlobDownloadBlobSuccess(t *testing.T) { addr, stop := testutil.StartServer(r) defer stop() - config := Config{Address: addr} + config := newTestConfig(addr) client, err := NewBlobClient(config) require.NoError(err) @@ -85,7 +85,7 @@ func TestBlobDownloadManifestSuccess(t *testing.T) { addr, stop := testutil.StartServer(r) defer stop() - config := Config{Address: addr} + config := newTestConfig(addr) client, err := NewBlobClient(config) require.NoError(err) @@ -115,7 +115,7 @@ func TestBlobDownloadFileNotFound(t *testing.T) { addr, stop := testutil.StartServer(r) defer stop() - config := Config{Address: addr} + config := newTestConfig(addr) client, err := NewBlobClient(config) require.NoError(err) diff --git a/lib/backend/registrybackend/config_test.go b/lib/backend/registrybackend/config_test.go new file mode 100644 index 000000000..897c015fe --- /dev/null +++ b/lib/backend/registrybackend/config_test.go @@ -0,0 +1,26 @@ +// Copyright (c) 2016-2019 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package registrybackend + +import "github.com/uber/kraken/lib/backend/registrybackend/security" + +func newTestConfig(addr string) Config { + return Config{ + Address: addr, + Security: security.Config{ + EnableHTTPFallback: true, + }, + } +} diff --git a/lib/backend/registrybackend/security/security.go b/lib/backend/registrybackend/security/security.go index 785da4594..0fcd6fccb 100644 --- a/lib/backend/registrybackend/security/security.go +++ b/lib/backend/registrybackend/security/security.go @@ -48,6 +48,7 @@ type Config struct { TLS httputil.TLSConfig `yaml:"tls"` BasicAuth *types.AuthConfig `yaml:"basic"` RemoteCredentialsStore string `yaml:"credsStore"` + EnableHTTPFallback bool `yaml:"enableHTTPFallback"` } // Authenticator creates send options to authenticate requests to registry @@ -55,7 +56,7 @@ type Config struct { type Authenticator interface { // Authenticate returns a send option to authenticate to the registry, // scoped to the given image repository. - Authenticate(repo string) (httputil.SendOption, error) + Authenticate(repo string) ([]httputil.SendOption, error) } type authenticator struct { @@ -89,18 +90,27 @@ func NewAuthenticator(address string, config Config) (Authenticator, error) { }, nil } -func (a *authenticator) Authenticate(repo string) (httputil.SendOption, error) { +func (a *authenticator) Authenticate(repo string) ([]httputil.SendOption, error) { config := a.config + + var opts []httputil.SendOption if config.TLS.Client.Disabled { - return httputil.SendNoop(), nil + opts = append(opts, httputil.SendNoop()) + return opts, nil + } + + if !config.EnableHTTPFallback { + opts = append(opts, httputil.DisableHTTPFallback()) } if !a.shouldAuth() { - return httputil.SendTLSTransport(a.roundTripper), nil + opts = append(opts, httputil.SendTLSTransport(a.roundTripper)) + return opts, nil } if err := a.updateChallenge(); err != nil { return nil, fmt.Errorf("could not update auth challenge: %s", err) } - return httputil.SendTLSTransport(a.transport(repo)), nil + opts = append(opts, httputil.SendTLSTransport(a.transport(repo))) + return opts, nil } func (a *authenticator) shouldAuth() bool { diff --git a/lib/backend/registrybackend/tagclient.go b/lib/backend/registrybackend/tagclient.go index 7b6316bdc..de899db73 100644 --- a/lib/backend/registrybackend/tagclient.go +++ b/lib/backend/registrybackend/tagclient.go @@ -82,7 +82,7 @@ func (c *TagClient) Stat(namespace, name string) (*core.BlobInfo, error) { } repo, tag := tokens[0], tokens[1] - opt, err := c.authenticator.Authenticate(repo) + opts, err := c.authenticator.Authenticate(repo) if err != nil { return nil, fmt.Errorf("get security opt: %s", err) } @@ -90,9 +90,12 @@ func (c *TagClient) Stat(namespace, name string) (*core.BlobInfo, error) { URL := fmt.Sprintf(_tagquery, c.config.Address, repo, tag) resp, err := httputil.Head( URL, - opt, - httputil.SendHeaders(map[string]string{"Accept": _v2ManifestType}), - httputil.SendAcceptedCodes(http.StatusOK, http.StatusNotFound)) + append( + opts, + httputil.SendHeaders(map[string]string{"Accept": _v2ManifestType}), + httputil.SendAcceptedCodes(http.StatusOK, http.StatusNotFound), + )..., + ) if err != nil { return nil, fmt.Errorf("check blob exists: %s", err) } @@ -115,7 +118,7 @@ func (c *TagClient) Download(namespace, name string, dst io.Writer) error { } repo, tag := tokens[0], tokens[1] - opt, err := c.authenticator.Authenticate(repo) + opts, err := c.authenticator.Authenticate(repo) if err != nil { return fmt.Errorf("get security opt: %s", err) } @@ -123,9 +126,12 @@ func (c *TagClient) Download(namespace, name string, dst io.Writer) error { URL := fmt.Sprintf(_tagquery, c.config.Address, repo, tag) resp, err := httputil.Get( URL, - opt, - httputil.SendHeaders(map[string]string{"Accept": _v2ManifestType}), - httputil.SendAcceptedCodes(http.StatusOK, http.StatusNotFound)) + append( + opts, + httputil.SendHeaders(map[string]string{"Accept": _v2ManifestType}), + httputil.SendAcceptedCodes(http.StatusOK, http.StatusNotFound), + )..., + ) if err != nil { return fmt.Errorf("check blob exists: %s", err) } diff --git a/lib/backend/registrybackend/tagclient_test.go b/lib/backend/registrybackend/tagclient_test.go index d09b3ad2e..4140d133b 100644 --- a/lib/backend/registrybackend/tagclient_test.go +++ b/lib/backend/registrybackend/tagclient_test.go @@ -57,7 +57,7 @@ func TestTagDownloadSuccess(t *testing.T) { addr, stop := testutil.StartServer(r) defer stop() - config := Config{Address: addr} + config := newTestConfig(addr) client, err := NewTagClient(config) require.NoError(err) @@ -87,7 +87,7 @@ func TestTagDownloadFileNotFound(t *testing.T) { addr, stop := testutil.StartServer(r) defer stop() - config := Config{Address: addr} + config := newTestConfig(addr) client, err := NewTagClient(config) require.NoError(err)