Как выполнить команду на удаленном компьютере в CLI golang?

Как выполнить команду на удаленном компьютере в CLI golang? Мне нужно написать CLI golang, который может SSH на удаленную машину с помощью ключа и выполнить команду оболочки. Кроме того, я должен уметь это сделать. например SSH в машину (например, облачный бастион), а затем SSH в другую, внутреннюю, машину и выполнить команду оболочки.

Я еще не нашел примеров для этого.

Ответ 1

Попробуйте с 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)
    }
}

Чтобы перепрыгнуть через машины, используйте директиву ProxyCommand в конфигурационном файле ssh.

Host remote_machine_name
  ProxyCommand ssh -q bastion nc remote_machine_ip 22

Ответ 2

Вы можете запускать команды на удаленном компьютере через SSH с помощью пакета "golang.org/x/crypto/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", 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
}

Ответ 3

Другие решения здесь будут работать, но я опущу другой вариант, который вы можете попробовать: 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

Попробуйте пакет 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)
    }

}

Смотрите еще пример.