V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
not4jerk
V2EX  ›  Go 编程语言

Go 语言:crypto/ssh 执行远程命令

  •  
  •   not4jerk ·
    mojocn · 2019-05-21 19:13:28 +08:00 · 2822 次点击
    这是一个创建于 2014 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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
    
    

    高级用法

    2 条回复    2019-05-22 17:39:11 +08:00
    yegle
        1
    yegle  
       2019-05-22 05:29:21 +08:00
    敢写 `ssh.InsecureIgnoreHostKey()` 然后不在显著位置解释风险的都是耍流氓
    not4jerk
        2
    not4jerk  
    OP
       2019-05-22 17:39:11 +08:00
    @yegle 我写了! 大多数人都不看 hostkey 的.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3814 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 05:07 · PVG 13:07 · LAX 21:07 · JFK 00:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.