github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/cmd/puppeth/ssh.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 12:09:31</date> 10 //</624342604028317696> 11 12 13 package main 14 15 import ( 16 "bufio" 17 "bytes" 18 "errors" 19 "fmt" 20 "io/ioutil" 21 "net" 22 "os" 23 "os/user" 24 "path/filepath" 25 "strings" 26 27 "github.com/ethereum/go-ethereum/log" 28 "golang.org/x/crypto/ssh" 29 "golang.org/x/crypto/ssh/terminal" 30 ) 31 32 //ssh client是Go的ssh客户机的一个小包装,有几个实用方法 33 //在顶部实施。 34 type sshClient struct { 35 server string //没有端口号的服务器名或IP 36 address string //远程服务器的IP地址 37 pubkey []byte //用于对服务器进行身份验证的RSA公钥 38 client *ssh.Client 39 logger log.Logger 40 } 41 42 //拨号使用当前用户和 43 // 44 // 45 func dial(server string, pubkey []byte) (*sshClient, error) { 46 // 47 hostname := "" 48 hostport := server 49 username := "" 50 identity := "id_rsa" //违约 51 52 if strings.Contains(server, "@") { 53 prefix := server[:strings.Index(server, "@")] 54 if strings.Contains(prefix, ":") { 55 username = prefix[:strings.Index(prefix, ":")] 56 identity = prefix[strings.Index(prefix, ":")+1:] 57 } else { 58 username = prefix 59 } 60 hostport = server[strings.Index(server, "@")+1:] 61 } 62 if strings.Contains(hostport, ":") { 63 hostname = hostport[:strings.Index(hostport, ":")] 64 } else { 65 hostname = hostport 66 hostport += ":22" 67 } 68 logger := log.New("server", server) 69 logger.Debug("Attempting to establish SSH connection") 70 71 user, err := user.Current() 72 if err != nil { 73 return nil, err 74 } 75 if username == "" { 76 username = user.Username 77 } 78 // 79 var auths []ssh.AuthMethod 80 81 path := filepath.Join(user.HomeDir, ".ssh", identity) 82 if buf, err := ioutil.ReadFile(path); err != nil { 83 log.Warn("No SSH key, falling back to passwords", "path", path, "err", err) 84 } else { 85 key, err := ssh.ParsePrivateKey(buf) 86 if err != nil { 87 fmt.Printf("What's the decryption password for %s? (won't be echoed)\n>", path) 88 blob, err := terminal.ReadPassword(int(os.Stdin.Fd())) 89 fmt.Println() 90 if err != nil { 91 log.Warn("Couldn't read password", "err", err) 92 } 93 key, err := ssh.ParsePrivateKeyWithPassphrase(buf, blob) 94 if err != nil { 95 log.Warn("Failed to decrypt SSH key, falling back to passwords", "path", path, "err", err) 96 } else { 97 auths = append(auths, ssh.PublicKeys(key)) 98 } 99 } else { 100 auths = append(auths, ssh.PublicKeys(key)) 101 } 102 } 103 auths = append(auths, ssh.PasswordCallback(func() (string, error) { 104 fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", username, server) 105 blob, err := terminal.ReadPassword(int(os.Stdin.Fd())) 106 107 fmt.Println() 108 return string(blob), err 109 })) 110 // 111 addr, err := net.LookupHost(hostname) 112 if err != nil { 113 return nil, err 114 } 115 if len(addr) == 0 { 116 return nil, errors.New("no IPs associated with domain") 117 } 118 // 119 logger.Trace("Dialing remote SSH server", "user", username) 120 keycheck := func(hostname string, remote net.Addr, key ssh.PublicKey) error { 121 // 122 if pubkey == nil { 123 fmt.Println() 124 fmt.Printf("The authenticity of host '%s (%s)' can't be established.\n", hostname, remote) 125 fmt.Printf("SSH key fingerprint is %s [MD5]\n", ssh.FingerprintLegacyMD5(key)) 126 fmt.Printf("Are you sure you want to continue connecting (yes/no)? ") 127 128 text, err := bufio.NewReader(os.Stdin).ReadString('\n') 129 switch { 130 case err != nil: 131 return err 132 case strings.TrimSpace(text) == "yes": 133 pubkey = key.Marshal() 134 return nil 135 default: 136 return fmt.Errorf("unknown auth choice: %v", text) 137 } 138 } 139 // 140 if bytes.Equal(pubkey, key.Marshal()) { 141 return nil 142 } 143 // 144 return errors.New("ssh key mismatch, readd the machine to update") 145 } 146 client, err := ssh.Dial("tcp", hostport, &ssh.ClientConfig{User: username, Auth: auths, HostKeyCallback: keycheck}) 147 if err != nil { 148 return nil, err 149 } 150 // 151 c := &sshClient{ 152 server: hostname, 153 address: addr[0], 154 pubkey: pubkey, 155 client: client, 156 logger: logger, 157 } 158 if err := c.init(); err != nil { 159 client.Close() 160 return nil, err 161 } 162 return c, nil 163 } 164 165 // 166 // 167 func (client *sshClient) init() error { 168 client.logger.Debug("Verifying if docker is available") 169 if out, err := client.Run("docker version"); err != nil { 170 if len(out) == 0 { 171 return err 172 } 173 return fmt.Errorf("docker configured incorrectly: %s", out) 174 } 175 client.logger.Debug("Verifying if docker-compose is available") 176 if out, err := client.Run("docker-compose version"); err != nil { 177 if len(out) == 0 { 178 return err 179 } 180 return fmt.Errorf("docker-compose configured incorrectly: %s", out) 181 } 182 return nil 183 } 184 185 // 186 func (client *sshClient) Close() error { 187 return client.client.Close() 188 } 189 190 // 191 // 192 func (client *sshClient) Run(cmd string) ([]byte, error) { 193 // 194 session, err := client.client.NewSession() 195 if err != nil { 196 return nil, err 197 } 198 defer session.Close() 199 200 // 201 client.logger.Trace("Running command on remote server", "cmd", cmd) 202 return session.CombinedOutput(cmd) 203 } 204 205 // 206 // 207 func (client *sshClient) Stream(cmd string) error { 208 // 209 session, err := client.client.NewSession() 210 if err != nil { 211 return err 212 } 213 defer session.Close() 214 215 session.Stdout = os.Stdout 216 session.Stderr = os.Stderr 217 218 // 219 client.logger.Trace("Streaming command on remote server", "cmd", cmd) 220 return session.Run(cmd) 221 } 222 223 // 224 //同时存在文件夹。 225 func (client *sshClient) Upload(files map[string][]byte) ([]byte, error) { 226 //建立一个单一的命令会话 227 session, err := client.client.NewSession() 228 if err != nil { 229 return nil, err 230 } 231 defer session.Close() 232 233 //创建流式处理SCP内容的goroutine 234 go func() { 235 out, _ := session.StdinPipe() 236 defer out.Close() 237 238 for file, content := range files { 239 client.logger.Trace("Uploading file to server", "file", file, "bytes", len(content)) 240 241 fmt.Fprintln(out, "D0755", 0, filepath.Dir(file)) //Ensure the folder exists 242 fmt.Fprintln(out, "C0644", len(content), filepath.Base(file)) //创建实际文件 243 out.Write(content) // 244 fmt.Fprint(out, "\x00") // 245 fmt.Fprintln(out, "E") //离开目录(更简单) 246 } 247 }() 248 return session.CombinedOutput("/usr/bin/scp -v -tr ./") 249 } 250