github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/cmd/puppeth/ssh.go (about) 1 2 //此源码被清华学神尹成大魔王专业翻译分析并修改 3 //尹成QQ77025077 4 //尹成微信18510341407 5 //尹成所在QQ群721929980 6 //尹成邮箱 yinc13@mails.tsinghua.edu.cn 7 //尹成毕业于清华大学,微软区块链领域全球最有价值专家 8 //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620 9 //版权所有2017 Go Ethereum作者 10 //此文件是Go以太坊的一部分。 11 // 12 //Go以太坊是免费软件:您可以重新发布和/或修改它 13 //根据GNU通用公共许可证的条款 14 //自由软件基金会,或者许可证的第3版,或者 15 //(由您选择)任何更高版本。 16 // 17 //Go以太坊的分布希望它会有用, 18 //但没有任何保证;甚至没有 19 //适销性或特定用途的适用性。见 20 //GNU通用公共许可证了解更多详细信息。 21 // 22 //你应该已经收到一份GNU通用公共许可证的副本 23 //一起去以太坊吧。如果没有,请参见<http://www.gnu.org/licenses/>。 24 25 package main 26 27 import ( 28 "bufio" 29 "bytes" 30 "errors" 31 "fmt" 32 "io/ioutil" 33 "net" 34 "os" 35 "os/user" 36 "path/filepath" 37 "strings" 38 39 "github.com/ethereum/go-ethereum/log" 40 "golang.org/x/crypto/ssh" 41 "golang.org/x/crypto/ssh/terminal" 42 ) 43 44 //ssh client是Go的ssh客户机的一个小包装,有几个实用方法 45 //在顶部实施。 46 type sshClient struct { 47 server string //没有端口号的服务器名或IP 48 address string //远程服务器的IP地址 49 pubkey []byte //用于对服务器进行身份验证的RSA公钥 50 client *ssh.Client 51 logger log.Logger 52 } 53 54 //拨号使用当前用户和 55 // 56 // 57 func dial(server string, pubkey []byte) (*sshClient, error) { 58 // 59 hostname := "" 60 hostport := server 61 username := "" 62 identity := "id_rsa" //违约 63 64 if strings.Contains(server, "@") { 65 prefix := server[:strings.Index(server, "@")] 66 if strings.Contains(prefix, ":") { 67 username = prefix[:strings.Index(prefix, ":")] 68 identity = prefix[strings.Index(prefix, ":")+1:] 69 } else { 70 username = prefix 71 } 72 hostport = server[strings.Index(server, "@")+1:] 73 } 74 if strings.Contains(hostport, ":") { 75 hostname = hostport[:strings.Index(hostport, ":")] 76 } else { 77 hostname = hostport 78 hostport += ":22" 79 } 80 logger := log.New("server", server) 81 logger.Debug("Attempting to establish SSH connection") 82 83 user, err := user.Current() 84 if err != nil { 85 return nil, err 86 } 87 if username == "" { 88 username = user.Username 89 } 90 // 91 var auths []ssh.AuthMethod 92 93 path := filepath.Join(user.HomeDir, ".ssh", identity) 94 if buf, err := ioutil.ReadFile(path); err != nil { 95 log.Warn("No SSH key, falling back to passwords", "path", path, "err", err) 96 } else { 97 key, err := ssh.ParsePrivateKey(buf) 98 if err != nil { 99 fmt.Printf("What's the decryption password for %s? (won't be echoed)\n>", path) 100 blob, err := terminal.ReadPassword(int(os.Stdin.Fd())) 101 fmt.Println() 102 if err != nil { 103 log.Warn("Couldn't read password", "err", err) 104 } 105 key, err := ssh.ParsePrivateKeyWithPassphrase(buf, blob) 106 if err != nil { 107 log.Warn("Failed to decrypt SSH key, falling back to passwords", "path", path, "err", err) 108 } else { 109 auths = append(auths, ssh.PublicKeys(key)) 110 } 111 } else { 112 auths = append(auths, ssh.PublicKeys(key)) 113 } 114 } 115 auths = append(auths, ssh.PasswordCallback(func() (string, error) { 116 fmt.Printf("What's the login password for %s at %s? (won't be echoed)\n> ", username, server) 117 blob, err := terminal.ReadPassword(int(os.Stdin.Fd())) 118 119 fmt.Println() 120 return string(blob), err 121 })) 122 // 123 addr, err := net.LookupHost(hostname) 124 if err != nil { 125 return nil, err 126 } 127 if len(addr) == 0 { 128 return nil, errors.New("no IPs associated with domain") 129 } 130 // 131 logger.Trace("Dialing remote SSH server", "user", username) 132 keycheck := func(hostname string, remote net.Addr, key ssh.PublicKey) error { 133 // 134 if pubkey == nil { 135 fmt.Println() 136 fmt.Printf("The authenticity of host '%s (%s)' can't be established.\n", hostname, remote) 137 fmt.Printf("SSH key fingerprint is %s [MD5]\n", ssh.FingerprintLegacyMD5(key)) 138 fmt.Printf("Are you sure you want to continue connecting (yes/no)? ") 139 140 text, err := bufio.NewReader(os.Stdin).ReadString('\n') 141 switch { 142 case err != nil: 143 return err 144 case strings.TrimSpace(text) == "yes": 145 pubkey = key.Marshal() 146 return nil 147 default: 148 return fmt.Errorf("unknown auth choice: %v", text) 149 } 150 } 151 // 152 if bytes.Equal(pubkey, key.Marshal()) { 153 return nil 154 } 155 // 156 return errors.New("ssh key mismatch, readd the machine to update") 157 } 158 client, err := ssh.Dial("tcp", hostport, &ssh.ClientConfig{User: username, Auth: auths, HostKeyCallback: keycheck}) 159 if err != nil { 160 return nil, err 161 } 162 // 163 c := &sshClient{ 164 server: hostname, 165 address: addr[0], 166 pubkey: pubkey, 167 client: client, 168 logger: logger, 169 } 170 if err := c.init(); err != nil { 171 client.Close() 172 return nil, err 173 } 174 return c, nil 175 } 176 177 // 178 // 179 func (client *sshClient) init() error { 180 client.logger.Debug("Verifying if docker is available") 181 if out, err := client.Run("docker version"); err != nil { 182 if len(out) == 0 { 183 return err 184 } 185 return fmt.Errorf("docker configured incorrectly: %s", out) 186 } 187 client.logger.Debug("Verifying if docker-compose is available") 188 if out, err := client.Run("docker-compose version"); err != nil { 189 if len(out) == 0 { 190 return err 191 } 192 return fmt.Errorf("docker-compose configured incorrectly: %s", out) 193 } 194 return nil 195 } 196 197 // 198 func (client *sshClient) Close() error { 199 return client.client.Close() 200 } 201 202 // 203 // 204 func (client *sshClient) Run(cmd string) ([]byte, error) { 205 // 206 session, err := client.client.NewSession() 207 if err != nil { 208 return nil, err 209 } 210 defer session.Close() 211 212 // 213 client.logger.Trace("Running command on remote server", "cmd", cmd) 214 return session.CombinedOutput(cmd) 215 } 216 217 // 218 // 219 func (client *sshClient) Stream(cmd string) error { 220 // 221 session, err := client.client.NewSession() 222 if err != nil { 223 return err 224 } 225 defer session.Close() 226 227 session.Stdout = os.Stdout 228 session.Stderr = os.Stderr 229 230 // 231 client.logger.Trace("Streaming command on remote server", "cmd", cmd) 232 return session.Run(cmd) 233 } 234 235 // 236 //同时存在文件夹。 237 func (client *sshClient) Upload(files map[string][]byte) ([]byte, error) { 238 //建立一个单一的命令会话 239 session, err := client.client.NewSession() 240 if err != nil { 241 return nil, err 242 } 243 defer session.Close() 244 245 //创建流式处理SCP内容的goroutine 246 go func() { 247 out, _ := session.StdinPipe() 248 defer out.Close() 249 250 for file, content := range files { 251 client.logger.Trace("Uploading file to server", "file", file, "bytes", len(content)) 252 253 fmt.Fprintln(out, "D0755", 0, filepath.Dir(file)) //Ensure the folder exists 254 fmt.Fprintln(out, "C0644", len(content), filepath.Base(file)) //创建实际文件 255 out.Write(content) // 256 fmt.Fprint(out, "\x00") // 257 fmt.Fprintln(out, "E") //离开目录(更简单) 258 } 259 }() 260 return session.CombinedOutput("/usr/bin/scp -v -tr ./") 261 }