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

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

问题描述:

How do I execute a command on a remote machine in a golang CLI? I need to write a golang CLI that can SSH into a remote machine via a key and execute a shell command. Furthermore, I need to be able to do this one hop away. e.g. SSH into a machine (like a cloud bastion) and then SSH into another, internal, machine and execute a shell command.

I haven't (yet) found any examples for this.

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

我(尚未)找到任何有关此示例。 / p> div>

Try with os/exec https://golang.org/pkg/os/exec/ to execute a 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)
    }
}

To jump over machines use the ProxyCommand directive in a ssh config file.

Host remote_machine_name
  ProxyCommand ssh -q bastion nc remote_machine_ip 22

You can run commands on a remote machine over SSH using the "golang.org/x/crypto/ssh" package.

Here is an example function demonstrating simple usage of running a single command on a remote machine and returning the output:

//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", net.JoinHostPort(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
}

The other solutions here will work, but I'll throw out another option you could try: simplessh. I think it is easier to use. For this question, I would use option 3 below where you can ssh using your key.

Option 1: SSH to a machine with a password, then run a command

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
}

Option 2: SSH to a machine using a set of possible passwords, then run a command

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
}

Option 3: SSH to a machine using your key

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
}

Try the package 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)
    }

}

See more example.