关于用golang封装ssh用于在远程主机上执行命令,上传或下载文件
时间:2022-02-24 10:28
下面由golang教程栏目给大家介绍用golang封装ssh用于在远程主机上执行命令,上传或下载文件,希望对需要的朋友有所帮助! python中可以paramiko实现在远程主机上执行命令,上传和下载文件,用go也可以封装一个,在go中用ssh就sftp包可以实现,实现了下面的功能 直接上代码 以上就是关于用golang封装ssh用于在远程主机上执行命令,上传或下载文件的详细内容,更多请关注gxlsystem.com其它相关文章!认证方式
和paramik类似package mainimport (
"errors"
"fmt"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
"io"
"io/ioutil"
"log"
"os"
"os/user"
"time")var (
DefaultSShTcpTimeout = 15 * time.Second // 与ssh建立连接的默认时间,自己设置一个就行)// 错误定义var (
InvalidHostName = errors.New("invalid parameters: hostname is empty")
InvalidPort = errors.New("invalid parameters: port must be range 0 ~ 65535"))// 返回当前用户名func getCurrentUser() string {
user, _ := user.Current()
return user.Username}// 存放上传或下载的信息type TransferInfo struct {
Kind string // upload或download
Local string // 本地路径
Dst string // 目标路径
TransferByte int64 // 传输的字节数(byte)}func (t *TransferInfo) String() string {
return fmt.Sprintf(`TransforInfo(Kind:"%s", Local: "%s", Dst: "%s", TransferByte: %d)`,
t.Kind, t.Local, t.Dst, t.TransferByte)}// 存放执行结果的结构体信息type ExecInfo struct {
Cmd string
Output []byte
ExitCode int}func (e *ExecInfo) OutputString() string {
return string(e.Output)}func (e *ExecInfo) String() string {
return fmt.Sprintf(`ExecInfo(cmd: "%s", exitcode: %d)`,
e.Cmd, e.ExitCode)}type AuthConfig struct {
*ssh.ClientConfig
User string
Password string
KeyFile string
Timeout time.Duration}func (a *AuthConfig) setDefault() {
if a.User == "" {
a.User = getCurrentUser()
}
if a.KeyFile == "" {
userHome, _ := os.UserHomeDir()
a.KeyFile = fmt.Sprintf("%s/.ssh/id_rsa", userHome)
}
if a.Timeout == 0 {
a.Timeout = DefaultSShTcpTimeout }}func (a *AuthConfig) SetAuthMethod() (ssh.AuthMethod, error) {
a.setDefault()
if a.Password != "" {
return ssh.Password(a.Password), nil }
data, err := ioutil.ReadFile(a.KeyFile)
if err != nil {
return nil, err }
singer, err := ssh.ParsePrivateKey(data)
if err != nil {
return nil, err }
return ssh.PublicKeys(singer), nil}func (a *AuthConfig) ApplyConfig() error {
authMethod, err := a.SetAuthMethod()
if err != nil {
return err }
a.ClientConfig = &ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{authMethod},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: a.Timeout,
}
return nil}// 存放连接的结构体type conn struct {
client *ssh.Client
sftpClient *sftp.Client}func (c *conn) Close() {
if c.sftpClient != nil {
c.sftpClient.Close()
c.sftpClient = nil }
if c.client != nil {
c.client.Close()
c.client = nil }}// SSHClient结构体type SSHClient struct {
conn
HostName string
Port int
AuthConfig AuthConfig}// 设置默认端口信息func (s *SSHClient) setDefaultValue() {
if s.Port == 0 {
s.Port = 22
}}// 与远程主机连接func (s *SSHClient) Connect() error {
if s.client != nil {
log.Println("Already Login")
return nil }
if err := s.AuthConfig.ApplyConfig(); err != nil {
return err }
s.setDefaultValue()
addr := fmt.Sprintf("%s:%d", s.HostName, s.Port)
var err error
s.client, err = ssh.Dial("tcp", addr, s.AuthConfig.ClientConfig)
if err != nil {
return err }
return nil}// 一个session只能执行一次命令,也就是说不能在同一个session执行多次s.session.CombinedOutput// 如果想执行多次,需要每条为每个命令创建一个session(这里是这样做)func (s *SSHClient) Exec(cmd string) (*ExecInfo, error) {
session, err := s.client.NewSession()
if err != nil {
return nil, err }
defer session.Close()
output, err := session.CombinedOutput(cmd)
var exitcode int if err != nil {
// 断言转成具体实现类型,获取返回值
exitcode = err.(*ssh.ExitError).ExitStatus()
}
return &ExecInfo{
Cmd: cmd,
Output: output,
ExitCode: exitcode,
}, nil}// 将本地文件上传到远程主机上func (s *SSHClient) Upload(localPath string, dstPath string) (*TransferInfo, error) {
transferInfo := &TransferInfo{Kind: "upload", Local: localPath, Dst: dstPath, TransferByte: 0}
var err error // 如果sftp客户端没有打开,就打开,为了复用
if s.sftpClient == nil {
if s.sftpClient, err = sftp.NewClient(s.client); err != nil {
return transferInfo, err }
}
localFileObj, err := os.Open(localPath)
if err != nil {
return transferInfo, err }
defer localFileObj.Close()
dstFileObj, err := s.sftpClient.Create(dstPath)
if err != nil {
return transferInfo, err }
defer dstFileObj.Close()
written, err := io.Copy(dstFileObj, localFileObj)
if err != nil {
return transferInfo, err }
transferInfo.TransferByte = written return transferInfo, nil}// 从远程主机上下载文件到本地func (s *SSHClient) Download(dstPath string, localPath string) (*TransferInfo, error) {
transferInfo := &TransferInfo{Kind: "download", Local: localPath, Dst: dstPath, TransferByte: 0}
var err error if s.sftpClient == nil {
if s.sftpClient, err = sftp.NewClient(s.client); err != nil {
return transferInfo, err }
}
//defer s.sftpClient.Close()
localFileObj, err := os.Create(localPath)
if err != nil {
return transferInfo, err }
defer localFileObj.Close()
dstFileObj, err := s.sftpClient.Open(dstPath)
if err != nil {
return transferInfo, err }
defer dstFileObj.Close()
written, err := io.Copy(localFileObj, dstFileObj)
if err != nil {
return transferInfo, err }
transferInfo.TransferByte = written return transferInfo, nil}// SSHclient的构造方法func NewSSHClient(hostname string, port int, authConfig AuthConfig) (*SSHClient, error) {
switch {
case hostname == "":
return nil, InvalidHostName case port > 65535 || port < 0:
return nil, InvalidPort }
sshClient := &SSHClient{HostName: hostname, Port: port, AuthConfig: authConfig}
err := sshClient.Connect()
if err != nil {
return nil, err }
return sshClient, nil}func main() {// 测试
sshClient, err := NewSSHClient("172.16.0.178", 22, AuthConfig{User: "root"})
if err != nil {
fmt.Println(err)
return
}
defer sshClient.Close()
//第一次 执行命令
execinfo, err := sshClient.Exec("ls -l")
fmt.Println(execinfo.OutputString(), err)
//第二次执行命令
out1, exitcode2 := sshClient.Exec("ifconfig -a")
fmt.Println(string(out1), exitcode2)
// 上传文件
transInfoUpload, err := sshClient.Upload("/tmp/passwd", "/tmp/password_upload")
fmt.Println(transInfoUpload, err)
// 下载文件
transInfoDownload, err := sshClient.Download("/etc/passwd", "/tmp/passwd_download")
fmt.Println(transInfoDownload, err)}