如何在golang CLI中在远程计算机上执行命令?我需要编写一个golang CLI,它可以通过密钥SSH连接到远程机器并执行shell命令。此外,我需要能够做到这一步。例如SSH进入机器(如云堡垒),然后SSH到另一台内部机器并执行shell命令。
我还没有找到任何例子。
答案 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)
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。