package service import ( "fmt" "io" "io/ioutil" "log" "net" "os" "time" "github.com/pkg/sftp" "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" ) type SSHService struct { Client *ssh.Client LastResult string } func (ss *SSHService) Connect(user, password, host, key string, port int, cipherList []string) error { var ( auth []ssh.AuthMethod //认证方式 addr string clientConfig *ssh.ClientConfig config ssh.Config err error ) auth = make([]ssh.AuthMethod, 0) if key == "" { // 密码认证 auth = append(auth, ssh.Password(password)) } else { // 秘钥认证 pemBytes, err := ioutil.ReadFile(key) if err != nil { return err } var signer ssh.Signer if password == "" { signer, err = ssh.ParsePrivateKey(pemBytes) } else { signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(password)) } if err != nil { return err } // 加载秘钥 auth = append(auth, ssh.PublicKeys(signer)) } // 设置ssh 的配置参数 if len(cipherList) == 0 { config = ssh.Config{ // 连接所允许的加密算法, go的SSH包允许的算法 Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-gcm@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"}, } } else { config = ssh.Config{ Ciphers: cipherList, } } clientConfig = &ssh.ClientConfig{ User: user, Auth: auth, Timeout: time.Second * 30, Config: config, // 默认密钥不受信任时,Go 的 ssh 包会在 HostKeyCallback 里把连接干掉(1.8 之后加的应该)。但是我们使用用户名密码连接的时候,这个太正常了,所以让他 return nil 就好了 HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }, } addr = fmt.Sprintf("%s:%d", host, port) if ss.Client, err = ssh.Dial("tcp", addr, clientConfig); err != nil { return err } return nil } func (ss *SSHService) RunShell(shell string) { var ( session *ssh.Session err error ) //获取session,这个session是用来远程执行操作的 if session, err = ss.Client.NewSession(); err != nil { log.Fatalln("error occurred:", err) } // 使用 session.Shell() 模拟终端时,所建立的终端参数 modes := ssh.TerminalModes{ ssh.ECHO: 0, //disable echoing ssh.TTY_OP_ISPEED: 14400, //input speed=14.4kbaud ssh.TTY_OP_OSPEED: 14400, } if err := session.RequestPty("xterm", 80, 40, modes); err != nil { logrus.Error(err) } //执行shell if output, err := session.CombinedOutput(shell); err != nil { log.Fatalln("error occurred:", err) } else { ss.LastResult = string(output) } } func (ss *SSHService) Scp(srcFileName, targetFileName string) (int64, error) { sftpClient, err := sftp.NewClient(ss.Client) if err != nil { return 0, fmt.Errorf("new sftp client error: %w", err) } defer sftpClient.Close() source, err := sftpClient.Open(srcFileName) if err != nil { return 0, fmt.Errorf("sftp client open src file error: %w", err) } defer source.Close() target, err := os.OpenFile(targetFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return 0, fmt.Errorf("open local file error: %w", err) } defer target.Close() n, err := io.Copy(target, source) if err != nil { return 0, fmt.Errorf("copy file error: %w", err) } return n, nil }