137 lines
3.3 KiB
Go
137 lines
3.3 KiB
Go
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
|
||
}
|