您好,登錄后才能下訂單哦!
這篇文章主要介紹了如何使用golang封裝ssh用于在遠程主機上執行命令,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
python中可以paramiko實現在遠程主機上執行命令,上傳和下載文件,用go也可以封裝一個,在go中用ssh就sftp包可以實現,實現了下面的功能
在遠程主機執行命令返回結果、返回值
上傳和下載文件遠程主機上,以及傳輸了多少個字節
如果指定了密碼,那么采用用戶+密碼的方式認證,否則采用用戶+秘鑰的方式
如果沒有指定用戶,則默認使用當前的用戶
如果沒有指定密碼,將采用用戶+秘鑰方式,默認取~/.ssh/id_rsa文件私鑰文件獲取秘鑰
和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)}
感謝你能夠認真閱讀完這篇文章,希望小編分享的“如何使用golang封裝ssh用于在遠程主機上執行命令”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。