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  }