github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/cmd/ctr/commands/resolver.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package commands 18 19 import ( 20 "bufio" 21 gocontext "context" 22 "crypto/tls" 23 "crypto/x509" 24 "fmt" 25 "io/ioutil" 26 "strings" 27 28 "github.com/containerd/console" 29 "github.com/containerd/containerd/remotes" 30 "github.com/containerd/containerd/remotes/docker" 31 "github.com/containerd/containerd/remotes/docker/config" 32 "github.com/pkg/errors" 33 "github.com/urfave/cli" 34 ) 35 36 // PushTracker returns a new InMemoryTracker which tracks the ref status 37 var PushTracker = docker.NewInMemoryTracker() 38 39 func passwordPrompt() (string, error) { 40 c := console.Current() 41 defer c.Reset() 42 43 if err := c.DisableEcho(); err != nil { 44 return "", errors.Wrap(err, "failed to disable echo") 45 } 46 47 line, _, err := bufio.NewReader(c).ReadLine() 48 if err != nil { 49 return "", errors.Wrap(err, "failed to read line") 50 } 51 return string(line), nil 52 } 53 54 // GetResolver prepares the resolver from the environment and options 55 func GetResolver(ctx gocontext.Context, clicontext *cli.Context) (remotes.Resolver, error) { 56 username := clicontext.String("user") 57 var secret string 58 if i := strings.IndexByte(username, ':'); i > 0 { 59 secret = username[i+1:] 60 username = username[0:i] 61 } 62 options := docker.ResolverOptions{ 63 Tracker: PushTracker, 64 } 65 if username != "" { 66 if secret == "" { 67 fmt.Printf("Password: ") 68 69 var err error 70 secret, err = passwordPrompt() 71 if err != nil { 72 return nil, err 73 } 74 75 fmt.Print("\n") 76 } 77 } else if rt := clicontext.String("refresh"); rt != "" { 78 secret = rt 79 } 80 81 hostOptions := config.HostOptions{} 82 hostOptions.Credentials = func(host string) (string, string, error) { 83 // If host doesn't match... 84 // Only one host 85 return username, secret, nil 86 } 87 if clicontext.Bool("plain-http") { 88 hostOptions.DefaultScheme = "http" 89 } 90 defaultTLS, err := resolverDefaultTLS(clicontext) 91 if err != nil { 92 return nil, err 93 } 94 hostOptions.DefaultTLS = defaultTLS 95 if hostDir := clicontext.String("hosts-dir"); hostDir != "" { 96 hostOptions.HostDir = config.HostDirFromRoot(hostDir) 97 } 98 99 options.Hosts = config.ConfigureHosts(ctx, hostOptions) 100 101 return docker.NewResolver(options), nil 102 } 103 104 func resolverDefaultTLS(clicontext *cli.Context) (*tls.Config, error) { 105 config := &tls.Config{} 106 107 if clicontext.Bool("skip-verify") { 108 config.InsecureSkipVerify = true 109 } 110 111 if tlsRootPath := clicontext.String("tlscacert"); tlsRootPath != "" { 112 tlsRootData, err := ioutil.ReadFile(tlsRootPath) 113 if err != nil { 114 return nil, errors.Wrapf(err, "failed to read %q", tlsRootPath) 115 } 116 117 config.RootCAs = x509.NewCertPool() 118 if !config.RootCAs.AppendCertsFromPEM(tlsRootData) { 119 return nil, fmt.Errorf("failed to load TLS CAs from %q: invalid data", tlsRootPath) 120 } 121 } 122 123 tlsCertPath := clicontext.String("tlscert") 124 tlsKeyPath := clicontext.String("tlskey") 125 if tlsCertPath != "" || tlsKeyPath != "" { 126 if tlsCertPath == "" || tlsKeyPath == "" { 127 return nil, errors.New("flags --tlscert and --tlskey must be set together") 128 } 129 keyPair, err := tls.LoadX509KeyPair(tlsCertPath, tlsKeyPath) 130 if err != nil { 131 return nil, errors.Wrapf(err, "failed to load TLS client credentials (cert=%q, key=%q)", tlsCertPath, tlsKeyPath) 132 } 133 config.Certificates = []tls.Certificate{keyPair} 134 } 135 136 return config, nil 137 }