Skip to content
GitHub

Go SSH

Go 实现 ssh 连接远端并发送命令执行
golang.org/x/crypto/ssh

创建 sshClient 项目, 添加 ssh 库

 $ mkdir sshClient
 $ cd sshClient && go mod init sshClient
 $ touch main.go
 $ go get -u "golang.org/x/crypto/ssh"

 $ ls -l
 > total 12
 > -rw-r--r-- 1 root root 115 Jul 27 02:58 go.mod
 > -rw-r--r-- 1 root root 312 Jul 27 02:58 go.sum
 > -rw-r--r-- 1 root root 973 Jul 27 04:20 main.go
# ssh.Dial 配置 server 信息生成 client 对象
# client 创建一个 session 执行 linux 命令并获取返回值

      server config               new                      command
ssh.Dial ---> client(*ssh.Client) ---> session(*ssh.Session) ---> server
                         1                      N

# 单个 client 可以创建多个 session 执行 linux 指令
# session 执行指令失败或者命令返回值不为 0 则报错
type Server struct {
    host     string
    port     string
    username string
    password string
    timeout  int32
    command  string
}

func Connect(server Server) (*ssh.Client, error) { ... }
func Run(client *ssh.Client, command string) (string, error) { ... }

func Connect(server Server) (*ssh.Client, error)
接受一个 Server 结构体, 通过 server 属性生成 *ssh.Client 对象

func Connect(server Server) (*ssh.Client, error) {
    config := ssh.ClientConfig{
        User:            server.username,
        Auth:            []ssh.AuthMethod{ssh.Password(server.password)},
        Timeout:         time.Duration(server.timeout) * time.Second,
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }

    sshClient, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", server.host, server.port), &config)
    if err != nil {
        fmt.Printf("Connect host %s error: %v\n", server.host, err)
        return nil, err
    }
    return sshClient, nil
}

func Run(client *ssh.Client, command string) (string, error) { ... }
接受 Connect 返回的 client 对象, 和执行的 linux 命令, 返回执行结果

func Run(client *ssh.Client, command string) (string, error) {
    session, err := client.NewSession()
    if err != nil {
        return fmt.Sprintf("create session failed: %v\n", err), err
    }
    defer session.Close()

    output, err := session.CombinedOutput(command)
    if err != nil {
        return string(output), err
    }
    return string(output), nil
}
package main

import (
    "fmt"
    "time"

    "golang.org/x/crypto/ssh"
)

type Server struct {
    host     string
    port     string
    username string
    password string
    timeout  int32
    command  string
}

func main() {
    server := Server {
        host    : "127.0.0.1",
        port    : "22",
        username: "root",
        password: "admin",
        timeout : 60,
        command : "date",
    }

    client, err := Connect(server)
    if err != nil {
        panic(fmt.Sprintf("Connect %s failed!", server.host))
    }

    output, err := Run(client, server.command)
    if err != nil {
        panic(fmt.Sprintf("%s run error: %v\n", server.command, err))
    }
    fmt.Println(output)
}

func Connect(server Server) (*ssh.Client, error) {
    config := ssh.ClientConfig{
        User:            server.username,
        Auth:            []ssh.AuthMethod{ssh.Password(server.password)},
        Timeout:         time.Duration(server.timeout) * time.Second,
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }

    sshClient, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", server.host, server.port), &config)
    if err != nil {
        fmt.Printf("Connect host %s error: %v\n", server.host, err)
        return nil, err
    }
    return sshClient, nil
}

func Run(client *ssh.Client, command string) (string, error) {
    session, err := client.NewSession()
    if err != nil {
        return fmt.Sprintf("create session failed: %v\n", err), err
    }
    defer session.Close()

    output, err := session.CombinedOutput(command)
    if err != nil {
        return string(output), err
    }
    return string(output), nil
}

模拟实现终端

func Termiunal(client *ssh.Client) {
    session, err := client.NewSession()
    if err != nil {
        panic(fmt.Sprintf("create session failed: %v\n", err))
    }
    defer session.Close()

    session.Stdout = os.Stdout                   // 会话输出关联到系统标准输出设备
    session.Stderr = os.Stderr                   // 会话错误输出关联到系统标准错误输出设备
    session.Stdin = os.Stdin                     // 会话输入关联到系统标准输入设备
    modes := ssh.TerminalModes{
        ssh.ECHO:          0,                    // 禁用回显(不重复显示执行命令 0禁用,1启动)
        ssh.TTY_OP_ISPEED: 14400,                // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400,                // output speed = 14.4kbaud
    }

    if err = session.RequestPty("linux", 32, 160, modes); err != nil {
        panic(fmt.Sprintf("request pty error: %v\n", err))
    }
    if err = session.Shell(); err != nil {
        panic(fmt.Sprintf("start shell error: %v", err))
    }
    if err = session.Wait(); err != nil {
        panic(fmt.Sprintf("return error: %v", err))
    }
}