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 }