mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Do not verify certificate when using --insecure-registry on an HTTPS registry
Signed-off-by: Tibor Vass <teabee89@gmail.com> Conflicts: registry/registry.go registry/registry_test.go registry/service.go registry/session.go
This commit is contained in:
parent
f43e77fc12
commit
e134f1f74a
8 changed files with 113 additions and 112 deletions
|
@ -56,7 +56,7 @@ func (config *Config) InstallFlags() {
|
||||||
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
|
flag.StringVar(&config.BridgeIP, []string{"#bip", "-bip"}, "", "Use this CIDR notation address for the network bridge's IP, not compatible with -b")
|
||||||
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
|
flag.StringVar(&config.BridgeIface, []string{"b", "-bridge"}, "", "Attach containers to a pre-existing network bridge\nuse 'none' to disable container networking")
|
||||||
flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)\nthis subnet must be nested in the bridge subnet (which is defined by -b or --bip)")
|
flag.StringVar(&config.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)\nthis subnet must be nested in the bridge subnet (which is defined by -b or --bip)")
|
||||||
opts.ListVar(&config.InsecureRegistries, []string{"-insecure-registry"}, "Make these registries use http")
|
opts.ListVar(&config.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback)")
|
||||||
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
|
flag.BoolVar(&config.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
|
||||||
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
|
flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Force the Docker runtime to use a specific storage driver")
|
||||||
flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver")
|
flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, "native", "Force the Docker runtime to use a specific exec driver")
|
||||||
|
|
|
@ -70,7 +70,7 @@ expect an integer, and they can only be specified once.
|
||||||
-g, --graph="/var/lib/docker" Path to use as the root of the Docker runtime
|
-g, --graph="/var/lib/docker" Path to use as the root of the Docker runtime
|
||||||
-H, --host=[] The socket(s) to bind to in daemon mode or connect to in client mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
|
-H, --host=[] The socket(s) to bind to in daemon mode or connect to in client mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
|
||||||
--icc=true Enable inter-container communication
|
--icc=true Enable inter-container communication
|
||||||
--insecure-registry=[] Make these registries use http
|
--insecure-registry=[] Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback)
|
||||||
--ip=0.0.0.0 Default IP address to use when binding container ports
|
--ip=0.0.0.0 Default IP address to use when binding container ports
|
||||||
--ip-forward=true Enable net.ipv4.ip_forward
|
--ip-forward=true Enable net.ipv4.ip_forward
|
||||||
--ip-masq=true Enable IP masquerading for bridge's IP range
|
--ip-masq=true Enable IP masquerading for bridge's IP range
|
||||||
|
@ -195,16 +195,16 @@ to other machines on the Internet. This may interfere with some network topologi
|
||||||
can be disabled with --ip-masq=false.
|
can be disabled with --ip-masq=false.
|
||||||
|
|
||||||
|
|
||||||
|
By default, Docker will assume all registries are secured via TLS with certificate verification
|
||||||
|
enabled. Prior versions of Docker used an auto fallback if a registry did not support TLS
|
||||||
|
(or if the TLS connection failed). This introduced the opportunity for Man In The Middle (MITM)
|
||||||
|
attacks, so as of Docker 1.3.1, the user must now specify the `--insecure-registry` daemon flag
|
||||||
|
for each insecure registry. An insecure registry is either not using TLS (i.e. plain text HTTP),
|
||||||
|
or is using TLS with a CA certificate not known by the Docker daemon (i.e. certification
|
||||||
|
verification disabled). For example, if there is a registry listening for HTTP at 127.0.0.1:5000,
|
||||||
|
as of Docker 1.3.1 you are required to specify `--insecure-registry 127.0.0.1:5000` when starting
|
||||||
|
the Docker daemon.
|
||||||
|
|
||||||
By default docker will assume all registries are securied via TLS. Prior versions
|
|
||||||
of docker used an auto fallback if a registry did not support TLS. This introduces
|
|
||||||
the opportunity for MITM attacks so in Docker 1.2 the user must specify `--insecure-registries`
|
|
||||||
when starting the Docker daemon to state which registries are not using TLS and to communicate
|
|
||||||
with these registries via plain text. If you are running a local registry over plain text
|
|
||||||
on `127.0.0.1:5000` you will be required to specify `--insecure-registries 127.0.0.1:500`
|
|
||||||
when starting the docker daemon to be able to push and pull images to that registry.
|
|
||||||
No automatic fallback will happen after Docker 1.2 to detect if a registry is using
|
|
||||||
HTTP or HTTPS.
|
|
||||||
|
|
||||||
Docker supports softlinks for the Docker data directory
|
Docker supports softlinks for the Docker data directory
|
||||||
(`/var/lib/docker`) and for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be set like this:
|
(`/var/lib/docker`) and for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be set like this:
|
||||||
|
|
|
@ -53,7 +53,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
store, err := NewTagStore(path.Join(root, "tags"), graph, nil)
|
store, err := NewTagStore(path.Join(root, "tags"), graph, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -34,9 +33,9 @@ func scanForApiVersion(hostname string) (string, APIVersion) {
|
||||||
return hostname, DefaultAPIVersion
|
return hostname, DefaultAPIVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEndpoint(hostname string) (*Endpoint, error) {
|
func NewEndpoint(hostname string, secure bool) (*Endpoint, error) {
|
||||||
var (
|
var (
|
||||||
endpoint Endpoint
|
endpoint = Endpoint{secure: secure}
|
||||||
trimmedHostname string
|
trimmedHostname string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
@ -49,14 +48,27 @@ func NewEndpoint(hostname string) (*Endpoint, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try HTTPS ping to registry
|
||||||
endpoint.URL.Scheme = "https"
|
endpoint.URL.Scheme = "https"
|
||||||
if _, err := endpoint.Ping(); err != nil {
|
if _, err := endpoint.Ping(); err != nil {
|
||||||
log.Debugf("Registry %s does not work (%s), falling back to http", endpoint, err)
|
|
||||||
// TODO: Check if http fallback is enabled
|
//TODO: triggering highland build can be done there without "failing"
|
||||||
endpoint.URL.Scheme = "http"
|
|
||||||
if _, err = endpoint.Ping(); err != nil {
|
if secure {
|
||||||
return nil, errors.New("Invalid Registry endpoint: " + err.Error())
|
// If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry`
|
||||||
|
// in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fallback to HTTP.
|
||||||
|
return nil, fmt.Errorf("Invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If registry is insecure and HTTPS failed, fallback to HTTP.
|
||||||
|
log.Debugf("Error from registry %q marked as insecure: %v. Insecurely falling back to HTTP", endpoint, err)
|
||||||
|
endpoint.URL.Scheme = "http"
|
||||||
|
_, err2 := endpoint.Ping()
|
||||||
|
if err2 == nil {
|
||||||
|
return &endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Invalid registry endpoint %q. HTTPS attempt: %v. HTTP attempt: %v", endpoint, err, err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &endpoint, nil
|
return &endpoint, nil
|
||||||
|
@ -65,6 +77,7 @@ func NewEndpoint(hostname string) (*Endpoint, error) {
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
URL *url.URL
|
URL *url.URL
|
||||||
Version APIVersion
|
Version APIVersion
|
||||||
|
secure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the formated URL for the root of this registry Endpoint
|
// Get the formated URL for the root of this registry Endpoint
|
||||||
|
@ -88,7 +101,7 @@ func (e Endpoint) Ping() (RegistryInfo, error) {
|
||||||
return RegistryInfo{Standalone: false}, err
|
return RegistryInfo{Standalone: false}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, _, err := doRequest(req, nil, ConnectTimeout)
|
resp, _, err := doRequest(req, nil, ConnectTimeout, e.secure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return RegistryInfo{Standalone: false}, err
|
return RegistryInfo{Standalone: false}, err
|
||||||
}
|
}
|
||||||
|
@ -127,3 +140,19 @@ func (e Endpoint) Ping() (RegistryInfo, error) {
|
||||||
log.Debugf("RegistryInfo.Standalone: %t", info.Standalone)
|
log.Debugf("RegistryInfo.Standalone: %t", info.Standalone)
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSecure returns false if the provided hostname is part of the list of insecure registries.
|
||||||
|
// Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs.
|
||||||
|
func IsSecure(hostname string, insecureRegistries []string) bool {
|
||||||
|
if hostname == IndexServerAddress() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, h := range insecureRegistries {
|
||||||
|
if hostname == h {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/log"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,13 +36,17 @@ const (
|
||||||
ConnectTimeout
|
ConnectTimeout
|
||||||
)
|
)
|
||||||
|
|
||||||
func newClient(jar http.CookieJar, roots *x509.CertPool, cert *tls.Certificate, timeout TimeoutType) *http.Client {
|
func newClient(jar http.CookieJar, roots *x509.CertPool, cert *tls.Certificate, timeout TimeoutType, secure bool) *http.Client {
|
||||||
tlsConfig := tls.Config{RootCAs: roots}
|
tlsConfig := tls.Config{RootCAs: roots}
|
||||||
|
|
||||||
if cert != nil {
|
if cert != nil {
|
||||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *cert)
|
tlsConfig.Certificates = append(tlsConfig.Certificates, *cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !secure {
|
||||||
|
tlsConfig.InsecureSkipVerify = true
|
||||||
|
}
|
||||||
|
|
||||||
httpTransport := &http.Transport{
|
httpTransport := &http.Transport{
|
||||||
DisableKeepAlives: true,
|
DisableKeepAlives: true,
|
||||||
Proxy: http.ProxyFromEnvironment,
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
@ -78,7 +83,13 @@ func newClient(jar http.CookieJar, roots *x509.CertPool, cert *tls.Certificate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType) (*http.Response, *http.Client, error) {
|
func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType, secure bool) (*http.Response, *http.Client, error) {
|
||||||
|
var (
|
||||||
|
pool *x509.CertPool
|
||||||
|
certs []*tls.Certificate
|
||||||
|
)
|
||||||
|
|
||||||
|
if secure && req.URL.Scheme == "https" {
|
||||||
hasFile := func(files []os.FileInfo, name string) bool {
|
hasFile := func(files []os.FileInfo, name string) bool {
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if f.Name() == name {
|
if f.Name() == name {
|
||||||
|
@ -89,21 +100,18 @@ func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType) (*htt
|
||||||
}
|
}
|
||||||
|
|
||||||
hostDir := path.Join("/etc/docker/certs.d", req.URL.Host)
|
hostDir := path.Join("/etc/docker/certs.d", req.URL.Host)
|
||||||
|
log.Debugf("hostDir: %s", hostDir)
|
||||||
fs, err := ioutil.ReadDir(hostDir)
|
fs, err := ioutil.ReadDir(hostDir)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
pool *x509.CertPool
|
|
||||||
certs []*tls.Certificate
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
if strings.HasSuffix(f.Name(), ".crt") {
|
if strings.HasSuffix(f.Name(), ".crt") {
|
||||||
if pool == nil {
|
if pool == nil {
|
||||||
pool = x509.NewCertPool()
|
pool = x509.NewCertPool()
|
||||||
}
|
}
|
||||||
|
log.Debugf("crt: %s", hostDir+"/"+f.Name())
|
||||||
data, err := ioutil.ReadFile(path.Join(hostDir, f.Name()))
|
data, err := ioutil.ReadFile(path.Join(hostDir, f.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -113,6 +121,7 @@ func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType) (*htt
|
||||||
if strings.HasSuffix(f.Name(), ".cert") {
|
if strings.HasSuffix(f.Name(), ".cert") {
|
||||||
certName := f.Name()
|
certName := f.Name()
|
||||||
keyName := certName[:len(certName)-5] + ".key"
|
keyName := certName[:len(certName)-5] + ".key"
|
||||||
|
log.Debugf("cert: %s", hostDir+"/"+f.Name())
|
||||||
if !hasFile(fs, keyName) {
|
if !hasFile(fs, keyName) {
|
||||||
return nil, nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
|
return nil, nil, fmt.Errorf("Missing key %s for certificate %s", keyName, certName)
|
||||||
}
|
}
|
||||||
|
@ -125,22 +134,25 @@ func doRequest(req *http.Request, jar http.CookieJar, timeout TimeoutType) (*htt
|
||||||
if strings.HasSuffix(f.Name(), ".key") {
|
if strings.HasSuffix(f.Name(), ".key") {
|
||||||
keyName := f.Name()
|
keyName := f.Name()
|
||||||
certName := keyName[:len(keyName)-4] + ".cert"
|
certName := keyName[:len(keyName)-4] + ".cert"
|
||||||
|
log.Debugf("key: %s", hostDir+"/"+f.Name())
|
||||||
if !hasFile(fs, certName) {
|
if !hasFile(fs, certName) {
|
||||||
return nil, nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
|
return nil, nil, fmt.Errorf("Missing certificate %s for key %s", certName, keyName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(certs) == 0 {
|
if len(certs) == 0 {
|
||||||
client := newClient(jar, pool, nil, timeout)
|
client := newClient(jar, pool, nil, timeout, secure)
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return res, client, nil
|
return res, client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, cert := range certs {
|
for i, cert := range certs {
|
||||||
client := newClient(jar, pool, cert, timeout)
|
client := newClient(jar, pool, cert, timeout, secure)
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
// If this is the last cert, otherwise, continue to next cert if 403 or 5xx
|
// If this is the last cert, otherwise, continue to next cert if 403 or 5xx
|
||||||
if i == len(certs)-1 || err == nil && res.StatusCode != 403 && res.StatusCode < 500 {
|
if i == len(certs)-1 || err == nil && res.StatusCode != 403 && res.StatusCode < 500 {
|
||||||
|
@ -202,49 +214,6 @@ func ResolveRepositoryName(reposName string) (string, string, error) {
|
||||||
return hostname, reposName, nil
|
return hostname, reposName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// this method expands the registry name as used in the prefix of a repo
|
|
||||||
// to a full url. if it already is a url, there will be no change.
|
|
||||||
func ExpandAndVerifyRegistryUrl(hostname string, secure bool) (string, error) {
|
|
||||||
if hostname == IndexServerAddress() {
|
|
||||||
return hostname, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
endpoint := fmt.Sprintf("http://%s/v1/", hostname)
|
|
||||||
|
|
||||||
if secure {
|
|
||||||
endpoint = fmt.Sprintf("https://%s/v1/", hostname)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, oerr := pingRegistryEndpoint(endpoint); oerr != nil {
|
|
||||||
//TODO: triggering highland build can be done there without "failing"
|
|
||||||
err := fmt.Errorf("Invalid registry endpoint '%s': %s ", endpoint, oerr)
|
|
||||||
|
|
||||||
if secure {
|
|
||||||
err = fmt.Errorf("%s. If this private registry supports only HTTP, please add `--insecure-registry %s` to the daemon's arguments.", oerr, hostname)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return endpoint, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// this method verifies if the provided hostname is part of the list of
|
|
||||||
// insecure registries and returns false if HTTP should be used
|
|
||||||
func IsSecure(hostname string, insecureRegistries []string) bool {
|
|
||||||
if hostname == IndexServerAddress() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, h := range insecureRegistries {
|
|
||||||
if hostname == h {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func trustedLocation(req *http.Request) bool {
|
func trustedLocation(req *http.Request) bool {
|
||||||
var (
|
var (
|
||||||
trusteds = []string{"docker.com", "docker.io"}
|
trusteds = []string{"docker.com", "docker.io"}
|
||||||
|
|
|
@ -18,7 +18,7 @@ var (
|
||||||
|
|
||||||
func spawnTestRegistrySession(t *testing.T) *Session {
|
func spawnTestRegistrySession(t *testing.T) *Session {
|
||||||
authConfig := &AuthConfig{}
|
authConfig := &AuthConfig{}
|
||||||
endpoint, err := NewEndpoint(makeURL("/v1/"))
|
endpoint, err := NewEndpoint(makeURL("/v1/"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPingRegistryEndpoint(t *testing.T) {
|
func TestPingRegistryEndpoint(t *testing.T) {
|
||||||
ep, err := NewEndpoint(makeURL("/v1/"))
|
ep, err := NewEndpoint(makeURL("/v1/"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,10 @@ func (s *Service) Search(job *engine.Job) engine.Status {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
endpoint, err := NewEndpoint(hostname)
|
|
||||||
|
secure := IsSecure(hostname, s.insecureRegistries)
|
||||||
|
|
||||||
|
endpoint, err := NewEndpoint(hostname, secure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return job.Error(err)
|
return job.Error(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, endpo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Session) doRequest(req *http.Request) (*http.Response, *http.Client, error) {
|
func (r *Session) doRequest(req *http.Request) (*http.Response, *http.Client, error) {
|
||||||
return doRequest(req, r.jar, r.timeout)
|
return doRequest(req, r.jar, r.timeout, r.indexEndpoint.secure)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the history of a given image from the Registry.
|
// Retrieve the history of a given image from the Registry.
|
||||||
|
|
Loading…
Reference in a new issue