github.com/dean7474/operator-registry@v1.21.1-0.20220418203638-d4717f98c2e5/pkg/image/containerdregistry/resolver.go (about)

     1  package containerdregistry
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"net"
     7  	"net/http"
     8  	"os"
     9  	"path/filepath"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/adrg/xdg"
    14  	"github.com/containerd/containerd/remotes"
    15  	"github.com/containerd/containerd/remotes/docker"
    16  	"github.com/docker/cli/cli/config"
    17  	"github.com/docker/cli/cli/config/configfile"
    18  	"github.com/docker/cli/cli/config/credentials"
    19  	"github.com/docker/docker/registry"
    20  )
    21  
    22  func NewResolver(configDir string, skipTlSVerify, plainHTTP bool, roots *x509.CertPool) (remotes.Resolver, error) {
    23  	transport := &http.Transport{
    24  		Proxy: http.ProxyFromEnvironment,
    25  		DialContext: (&net.Dialer{
    26  			Timeout:   30 * time.Second,
    27  			KeepAlive: 30 * time.Second,
    28  		}).DialContext,
    29  		MaxIdleConns:          10,
    30  		IdleConnTimeout:       30 * time.Second,
    31  		TLSHandshakeTimeout:   10 * time.Second,
    32  		ExpectContinueTimeout: 5 * time.Second,
    33  		TLSClientConfig: &tls.Config{
    34  			InsecureSkipVerify: false,
    35  			RootCAs:            roots,
    36  		},
    37  	}
    38  
    39  	if plainHTTP || skipTlSVerify {
    40  		transport.TLSClientConfig = &tls.Config{
    41  			InsecureSkipVerify: true,
    42  		}
    43  	}
    44  	headers := http.Header{}
    45  	headers.Set("User-Agent", "opm/alpha")
    46  
    47  	client := &http.Client{Transport: transport}
    48  
    49  	cfg, err := loadConfig(configDir)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	regopts := []docker.RegistryOpt{
    55  		docker.WithAuthorizer(docker.NewDockerAuthorizer(
    56  			docker.WithAuthClient(client),
    57  			docker.WithAuthHeader(headers),
    58  			docker.WithAuthCreds(credential(cfg)),
    59  		)),
    60  		docker.WithClient(client),
    61  	}
    62  	if plainHTTP {
    63  		regopts = append(regopts, docker.WithPlainHTTP(docker.MatchAllHosts))
    64  	}
    65  
    66  	opts := docker.ResolverOptions{
    67  		Hosts:   docker.ConfigureDefaultRegistries(regopts...),
    68  		Headers: headers,
    69  	}
    70  
    71  	return docker.NewResolver(opts), nil
    72  }
    73  
    74  func credential(cfg *configfile.ConfigFile) func(string) (string, string, error) {
    75  	return func(hostname string) (string, string, error) {
    76  		hostname = resolveHostname(hostname)
    77  		auth, err := cfg.GetAuthConfig(hostname)
    78  		if err != nil {
    79  			return "", "", err
    80  		}
    81  		if auth.IdentityToken != "" {
    82  			return "", auth.IdentityToken, nil
    83  		}
    84  		if auth.Username == "" && auth.Password == "" {
    85  			return "", "", nil
    86  		}
    87  
    88  		return auth.Username, auth.Password, nil
    89  	}
    90  }
    91  
    92  // protects against a data race inside the docker CLI
    93  // TODO: upstream issue for 20.10.x is tracked here https://github.com/docker/cli/pull/3410
    94  // newer versions already contain the fix
    95  var configMutex sync.Mutex
    96  
    97  func loadConfig(dir string) (*configfile.ConfigFile, error) {
    98  	configMutex.Lock()
    99  	defer configMutex.Unlock()
   100  
   101  	if dir == "" {
   102  		dir = config.Dir()
   103  	}
   104  
   105  	dockerConfigJSON := filepath.Join(dir, config.ConfigFileName)
   106  	cfg := configfile.New(dockerConfigJSON)
   107  
   108  	switch _, err := os.Stat(dockerConfigJSON); {
   109  	case err == nil:
   110  		cfg, err = config.Load(dir)
   111  		if err != nil {
   112  			return cfg, err
   113  		}
   114  	case os.IsNotExist(err):
   115  		podmanConfig := filepath.Join(xdg.RuntimeDir, "containers/auth.json")
   116  		if file, err := os.Open(podmanConfig); err == nil {
   117  			defer file.Close()
   118  			cfg, err = config.LoadFromReader(file)
   119  			if err != nil {
   120  				return cfg, err
   121  			}
   122  		} else if !os.IsNotExist(err) {
   123  			return cfg, err
   124  		}
   125  	}
   126  
   127  	if !cfg.ContainsAuth() {
   128  		cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore)
   129  	}
   130  
   131  	return cfg, nil
   132  }
   133  
   134  // resolveHostname resolves Docker specific hostnames
   135  func resolveHostname(hostname string) string {
   136  	switch hostname {
   137  	case registry.IndexHostname, registry.IndexName, registry.DefaultV2Registry.Host:
   138  		return registry.IndexServer
   139  	}
   140  	return hostname
   141  }