Go語言:crypto/ssh執行遠端命令

jjjjerk發表於2019-05-21

felix

前言

遠端執行命令有什麼用?為什麼要遠端執行命令? 如果你只有2,3臺伺服器需要管理的時候,遠端執行命令確實沒有沒多大作用,你可以登入到每臺伺服器上去完成各種操作。 當你的伺服器大於3臺的時候,遠端執行的命令的方式就可以大大提高你的生產力了。

如果你有一個可以遠端執行命令的工具,那麼就可以像操作單臺機器那樣操作多臺機器,機器越多,效率提高的越多。 遠端執行命令最常用的方法就是利用 SSH 協議,將命令傳送到遠端機器上執行,並獲取返回結果。

連線

連線包含了認證,可以使用 password 或者 sshkey 2種方式來認證。下面的示例為了簡單,使用了密碼認證的方式來完成連線。

package main

import (
    "fmt"
    "github.com/mitchellh/go-homedir"
    "golang.org/x/crypto/ssh"
    "io/ioutil"
    "log"
    "time"
)

func main(){
    sshHost := "home.xxx.cn"
    sshUser := "x"
    sshPassword := "xxxxxx"
    sshType := "password"//password 或者 key
    sshKeyPath := ""//ssh id_rsa.id 路徑"
    sshPort := 22

    //建立sshp登陸配置
    config := &ssh.ClientConfig{
        Timeout:         time.Second,//ssh 連線time out 時間一秒鐘, 如果ssh驗證錯誤 會在一秒內返回
        User:            sshUser,
        HostKeyCallback: ssh.InsecureIgnoreHostKey(), //這個可以, 但是不夠安全
        //HostKeyCallback: hostKeyCallBackFunc(h.Host),
    }
    if sshType == "password" {
        config.Auth = []ssh.AuthMethod{ssh.Password(sshPassword)}
    } else {
        config.Auth = []ssh.AuthMethod{publicKeyAuthFunc(sshKeyPath)}
    }

    //dial 獲取ssh client
    addr := fmt.Sprintf("%s:%d", sshHost, sshPort)
    sshClient, err := ssh.Dial("tcp", addr, config)
    if err != nil {
        log.Fatal("建立ssh client 失敗",err)
    }
    defer sshClient.Close()

    //建立ssh-session
    session, err := sshClient.NewSession()
    if err != nil {
        log.Fatal("建立ssh session 失敗",err)
    }
    defer session.Close()
    //執行遠端命令
    combo,err := session.CombinedOutput("whoami; cd /; ls -al;echo https://github.com/dejavuzhou/felix")
    if err != nil {
        log.Fatal("遠端執行cmd 失敗",err)
    }
    log.Println("命令輸出:",string(combo))

}

func publicKeyAuthFunc(kPath string) ssh.AuthMethod {
    keyPath, err := homedir.Expand(kPath)
    if err != nil {
        log.Fatal("find key's home dir failed", err)
    }
    key, err := ioutil.ReadFile(keyPath)
    if err != nil {
        log.Fatal("ssh key file read failed", err)
    }
    // Create the Signer for this private key.
    signer, err := ssh.ParsePrivateKey(key)
    if err != nil {
        log.Fatal("ssh key signer failed", err)
    }
    return ssh.PublicKeys(signer)
}

程式碼詳解

1 配置ssh.ClientConfig

  • 建議TimeOut自定義一個比較端的時間
  • 自定義HostKeyCallback 如果像簡便就使用 ssh.InsecureIgnoreHostKey回撥, 這種方式不是很安全
  • publicKeyAuthFunc 如果使用key登陸 就需要著用這個函式量讀取id_rsa私鑰,當然你可以自定義這個訪問讓他支援字串

2 ssh.Dial建立ssh客戶端

拼接字串得到ssh連線地址,同時不要忘記 defer client.Close()

3 sshClient.NewSession 建立session 會話

  • 可以自定義stdin,stdout
  • 可以建立pty
  • 可以SetEnv

3 執行命令 CombinedOutput run ...

列印結果

2019/05/21 18:39:22 命令輸出: pi
總用量 91
drwxr-xr-x  23 root root  4096 5月  20 11:13 .
drwxr-xr-x  23 root root  4096 5月  20 11:13 ..
drwxr-xr-x   2 root root  4096 4月   8 17:51 bin
drwxr-xr-x   6 root root  2560 1月   1  1970 boot
drwxr-xr-x  14 root root  3280 5月  21 12:17 dev
drwxr-xr-x  87 root root  4096 5月  17 09:57 etc
drwxr-xr-x   4 root root  4096 5月  17 09:56 home
drwxr-xr-x  16 root root  4096 4月   8 17:58 lib
drwx------   2 root root 16384 4月   8 18:24 lost+found
drwxr-xr-x   2 root root  4096 4月   8 17:37 media
drwxr-xr-x   2 root root  4096 4月  18 22:18 miwifi
drwxr-xr-x   2 root root  4096 4月   8 17:37 mnt
-rw-r--r--   1 root root  2787 4月  19 10:42 nginx_default_site.conf
drwxr-xr-x   3 root root  4096 4月   8 17:48 opt
dr-xr-xr-x 139 root root     0 1月   1  1970 proc
drwx------   6 root root  4096 5月  20 11:12 root
drwxr-xr-x  24 root root   760 5月  21 18:39 run
drwxr-xr-x   2 root root  4096 4月  19 13:48 sbin
drwxr-xr-x   2 root root  4096 4月   8 17:37 srv
dr-xr-xr-x  12 root root     0 5月  21 18:25 sys
drwxrwxrwt   8 root root  4096 5月  21 18:35 tmp
drwxr-xr-x  10 root root  4096 4月   8 17:37 usr
drwxr-xr-x  12 root root  4096 4月  19 10:10 var
drwxrwxrwx   3 root root  4096 4月  19 10:35 www
https://github.com/dejavuzhou/felix

高階用法

原文地址tech.mojotv.cn

相關文章