package ssh import ( "errors" "fmt" "net" "os" "strconv" "time" "github.com/pkg/sftp" "golang.org/x/crypto/ssh" ) const DefaultTimeout = 30 * time.Second type Client struct { *Config SSHClient *ssh.Client SFTPClient *sftp.Client } // New 创建SSH client func New(cnf *Config) (client *Client, err error) { clientConfig := &ssh.ClientConfig{ User: cnf.User, Timeout: DefaultTimeout, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } if cnf.Port == 0 { cnf.Port = 22 } // 1. privite key file if len(cnf.KeyFiles) == 0 { keyPath := os.Getenv("HOME") + "/.ssh/id_rsa" if auth, err := WithPrivateKey(keyPath, cnf.Passphrase); err == nil { clientConfig.Auth = append(clientConfig.Auth, auth) } } else { if auth, err := WithPrivateKeys(cnf.KeyFiles, cnf.Passphrase); err == nil { clientConfig.Auth = append(clientConfig.Auth, auth) } } // 2. 密码方式 放在key之后,这样密钥失败之后可以使用Password方式 if cnf.Password != "" { clientConfig.Auth = append(clientConfig.Auth, ssh.Password(cnf.Password)) } // 3. agent 模式放在最后,这样当前两者都不能使用时可以采用Agent模式 if auth, err := WithAgent(); err == nil { clientConfig.Auth = append(clientConfig.Auth, auth) } // hostPort := config.Host + ":" + strconv.Itoa(config.Port) sshClient, err := ssh.Dial("tcp", net.JoinHostPort(cnf.Host, strconv.Itoa(cnf.Port)), clientConfig) if err != nil { return client, errors.New("Failed to dial ssh: " + err.Error()) } // create sftp client var sftpClient *sftp.Client if sftpClient, err = sftp.NewClient(sshClient); err != nil { return client, errors.New("Failed to conn sftp: " + err.Error()) } return &Client{SSHClient: sshClient, SFTPClient: sftpClient}, nil } // NewClient 根据配置 func NewClient(host, port, user, password string) (client *Client, err error) { p, _ := strconv.Atoi(port) // if err != nil { // p = 22 // } if user == "" { user = "root" } var config = &Config{ Host: host, Port: p, User: user, Password: password, // KeyFiles: []string{"~/.ssh/id_rsa"}, Passphrase: password, } return New(config) } func NewWithAgent(Host, Port, User string) (client *Client, err error) { clientConfig := &ssh.ClientConfig{ User: User, Timeout: DefaultTimeout, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } auth, err := WithAgent() if err != nil { return nil, err } clientConfig.Auth = append(clientConfig.Auth, auth) // hostPort := config.Host + ":" + strconv.Itoa(config.Port) sshClient, err := ssh.Dial("tcp", net.JoinHostPort(Host, Port), clientConfig) if err != nil { return client, errors.New("Failed to dial ssh: " + err.Error()) } // create sftp client var sftpClient *sftp.Client if sftpClient, err = sftp.NewClient(sshClient); err != nil { return client, errors.New("Failed to conn sftp: " + err.Error()) } return &Client{SSHClient: sshClient, SFTPClient: sftpClient}, nil } func NewWithPrivateKey(Host, Port, User, Passphrase string) (client *Client, err error) { clientConfig := &ssh.ClientConfig{ User: User, Timeout: DefaultTimeout, HostKeyCallback: ssh.InsecureIgnoreHostKey(), } // 3. privite key file keyPath := os.Getenv("HOME") + "/.ssh/id_rsa" auth, err := WithPrivateKey(keyPath, Passphrase) if err != nil { fmt.Println(err) return nil, err } clientConfig.Auth = append(clientConfig.Auth, auth) // hostPort := config.Host + ":" + strconv.Itoa(config.Port) sshClient, err := ssh.Dial("tcp", net.JoinHostPort(Host, Port), clientConfig) if err != nil { return client, errors.New("Failed to dial ssh: " + err.Error()) } // create sftp client var sftpClient *sftp.Client if sftpClient, err = sftp.NewClient(sshClient); err != nil { return client, errors.New("Failed to conn sftp: " + err.Error()) } return &Client{SSHClient: sshClient, SFTPClient: sftpClient}, nil } // Close the underlying SSH connection func (c *Client) Close() { c.SFTPClient.Close() c.SSHClient.Close() }