如何在golang CLI中在远程计算机上执行命令?

时间:2016-06-07 12:52:01

标签: go ssh command-line-interface

如何在golang CLI中在远程计算机上执行命令?我需要编写一个golang CLI,它可以通过密钥SSH连接到远程机器并执行shell命令。此外,我需要能够做到这一步。例如SSH进入机器(如云堡垒),然后SSH到另一台内部机器并执行shell命令。

我还没有找到任何例子。

5 个答案:

答案 0 :(得分:9)

尝试使用os / exec https://golang.org/pkg/os/exec/执行ssh

package main

import (
    "bytes"
    "log"
    "os/exec"
)

func main() {
    cmd := exec.Command("ssh", "remote-machine", "bash-command")
    var out bytes.Buffer
    cmd.Stdout = &out
    err := cmd.Run()
    if err != nil {
        log.Fatal(err)
    }
}

要跳过计算机,请在ssh配置文件中使用ProxyCommand指令。

Host remote_machine_name
  ProxyCommand ssh -q bastion nc remote_machine_ip 22

答案 1 :(得分:5)

您可以使用"golang.org/x/crypto/ssh"包通过SSH在远程计算机上运行命令。

这是一个示例函数,演示了在远程计算机上运行单个命令并返回输出的简单用法:

//e.g. output, err := remoteRun("root", "MY_IP", "PRIVATE_KEY", "ls")
func remoteRun(user string, addr string, privateKey string, cmd string) (string, error) {
    // privateKey could be read from a file, or retrieved from another storage
    // source, such as the Secret Service / GNOME Keyring
    key, err := ssh.ParsePrivateKey([]byte(privateKey))
    if err != nil {
        return "", err
    }
    // Authentication
    config := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{
            ssh.PublicKeys(key),
        },
        //alternatively, you could use a password
        /*
            Auth: []ssh.AuthMethod{
                ssh.Password("PASSWORD"),
            },
        */
    }
    // Connect
    client, err := ssh.Dial("tcp", addr+":22", config)
    if err != nil {
        return "", err
    }
    // Create a session. It is one session per command.
    session, err := client.NewSession()
    if err != nil {
        return "", err
    }
    defer session.Close()
    var b bytes.Buffer  // import "bytes"
    session.Stdout = &b // get output
    // you can also pass what gets input to the stdin, allowing you to pipe
    // content from client to server
    //      session.Stdin = bytes.NewBufferString("My input")

    // Finally, run the command
    err = session.Run(cmd)
    return b.String(), err
}

答案 2 :(得分:1)

golang SSH 执行带有超时选项的 shell 命令

import (
    "bytes"
    "context"
    "errors"
    "fmt"
    "golang.org/x/crypto/ssh"
    "time"
)

func SshRemoteRunCommandWithTimeout(sshClient *ssh.Client, command string, timeout time.Duration) (string, error) {
    if timeout < 1 {
        return "", errors.New("timeout must be valid")
    }

    session, err := sshClient.NewSession()
    if err != nil {
        return "", err
    }
    defer session.Close()

    ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
    defer cancelFunc()
    resChan := make(chan string, 1)
    errChan := make(chan error, 1)

    go func() {
        // run shell script
        if output, err := session.CombinedOutput(command); err != nil {
            errChan <- err
        } else {
            resChan <- string(output)
        }
    }()

    select {
    case err := <-errChan:
        return "", err
    case ms := <-resChan:
        return ms, nil
    case <-ctx.Done():
        return "", ctx.Err()
    }
}

答案 3 :(得分:0)

这里的其他解决方案都可以使用,但是我将抛出另一个可以尝试的选项: simplessh 。我认为它更易于使用。对于此问题,我将在下面使用选项3,在其中您可以使用密钥进行SSH。

选项1:使用密码SSH到计算机,然后运行命令

import (
    "log"

    "github.com/sfreiberg/simplessh"
)

func main() error {
    var client *simplessh.Client
    var err error

    if client, err = simplessh.ConnectWithPassword("hostname_to_ssh_to", "username", "password"); err != nil {
        return err
    }

    defer client.Close()

    // Now run the commands on the remote machine:
    if _, err := client.Exec("cat /tmp/somefile"); err != nil {
        log.Println(err)
    }

    return nil
}

选项2:使用可能的密码 SSH到计算机,然后运行命令

import (
    "log"

    "github.com/sfreiberg/simplessh"
)

type access struct {
    login    string
    password string
}

var loginAccess []access

func init() {
    // Initialize all password to try
    loginAccess = append(loginAccess, access{"root", "rootpassword1"})
    loginAccess = append(loginAccess, access{"someuser", "newpassword"})
}

func main() error {
    var client *simplessh.Client
    var err error

    // Try to connect with first password, then tried second else fails gracefully
    for _, credentials := range loginAccess {
        if client, err = simplessh.ConnectWithPassword("hostname_to_ssh_to", credentials.login, credentials.password); err == nil {
            break
        }
    }

    if err != nil {
        return err
    }

    defer client.Close()

    // Now run the commands on the remote machine:
    if _, err := client.Exec("cat /tmp/somefile"); err != nil {
        log.Println(err)
    }

    return nil
}

选项3:使用您的密钥SSH到计算机

import (
    "log"

    "github.com/sfreiberg/simplessh"
)

func SshAndRunCommand() error {
    var client *simplessh.Client
    var err error

    // Option A: Using a specific private key path:
    //if client, err = simplessh.ConnectWithKeyFile("hostname_to_ssh_to", "username", "/home/user/.ssh/id_rsa"); err != nil {

    // Option B: Using your default private key at $HOME/.ssh/id_rsa:
    //if client, err = simplessh.ConnectWithKeyFile("hostname_to_ssh_to", "username"); err != nil {

    // Option C: Use the current user to ssh and the default private key file:
    if client, err = simplessh.ConnectWithKeyFile("hostname_to_ssh_to"); err != nil {
        return err
    }

    defer client.Close()

    // Now run the commands on the remote machine:
    if _, err := client.Exec("cat /tmp/somefile"); err != nil {
        log.Println(err)
    }

    return nil
}

答案 4 :(得分:0)

尝试打包https://github.com/appleboy/easyssh-proxy

package main

import (
    "fmt"
    "time"

    "github.com/appleboy/easyssh-proxy"
)

func main() {
    // Create MakeConfig instance with remote username, server address and path to private key.
    ssh := &easyssh.MakeConfig{
        User:   "appleboy",
        Server: "example.com",
        // Optional key or Password without either we try to contact your agent SOCKET
        //Password: "password",
        // Paste your source content of private key
        // Key: `-----BEGIN RSA PRIVATE KEY-----
        // MIIEpAIBAAKCAQEA4e2D/qPN08pzTac+a8ZmlP1ziJOXk45CynMPtva0rtK/RB26
        // 7XC9wlRna4b3Ln8ew3q1ZcBjXwD4ppbTlmwAfQIaZTGJUgQbdsO9YA==
        // -----END RSA PRIVATE KEY-----
        // `,
        KeyPath: "/Users/username/.ssh/id_rsa",
        Port:    "22",
        Timeout: 60 * time.Second,
    }

    // Call Run method with command you want to run on remote server.
    stdout, stderr, done, err := ssh.Run("ls -al", 60*time.Second)
    // Handle errors
    if err != nil {
        panic("Can't run remote command: " + err.Error())
    } else {
        fmt.Println("don is :", done, "stdout is :", stdout, ";   stderr is :", stderr)
    }

}

请参见more example