如何获取代理GCE虚拟机的SSH客户端以在目标GCE虚拟机上执行命令?
如何获取代理GCE虚拟机的SSH客户端以在目标GCE虚拟机上执行命令?
看起来你现在是通过在代理VM上生成gcloud命令间接执行目标VM的操作,想要改用Go的ssh.Client直接实现这个需求对吧?结合你的场景(Go代码运行在代理VM上,且代理VM能直接访问目标VM的本地IP),其实不需要复杂的代理跳转,直接在Go代码中创建连接目标VM的SSH客户端即可,我给你一步步拆解实现:
核心思路
因为你的Go代码已经部署在代理VM上,而代理VM可以直接访问目标VM的私有IP(192.168.1.205),所以我们可以跳过gcloud命令,直接用Go的x/crypto/ssh包建立到目标VM的SSH连接,然后创建会话执行命令。
完整代码示例
package main import ( "bufio" "fmt" "io/ioutil" "log" "net" "os" "time" "golang.org/x/crypto/ssh" ) func main() { // 目标VM的配置信息 targetUser := "myUser" targetIP := "192.168.1.205" targetPort := "22" // 代理VM上的私钥路径(对应你命令里的~/prox_ssh_id) privateKeyPath := "~/prox_ssh_id" // 自动扩展路径中的~为用户主目录 expandedKeyPath, err := expandHomeDir(privateKeyPath) if err != nil { log.Fatalf("解析私钥路径失败: %v", err) } // 读取私钥文件内容 privateKeyBytes, err := ioutil.ReadFile(expandedKeyPath) if err != nil { log.Fatalf("读取私钥文件失败: %v", err) } // 解析私钥(如果私钥有密码,需要用ParsePrivateKeyWithPassphrase) signer, err := ssh.ParsePrivateKey(privateKeyBytes) if err != nil { log.Fatalf("解析私钥失败: %v", err) } // 配置SSH客户端参数 sshConfig := &ssh.ClientConfig{ User: targetUser, Auth: []ssh.AuthMethod{ ssh.PublicKeys(signer), // 使用私钥认证 }, // 禁用主机密钥检查(对应你命令里的-o StrictHostKeyChecking=no) // 注意:生产环境不建议这么做,最好提前验证目标VM的主机密钥 HostKeyCallback: ssh.InsecureIgnoreHostKey(), } // 设置连接保活参数(对应你命令里的-o ServerAliveInterval=600) sshConfig.SetDefaults() sshConfig.ServerAliveInterval = 600 * time.Second // 每600秒发送一次保活包 sshConfig.ServerAliveCountMax = 3 // 连续3次未收到响应则断开连接 // 建立到目标VM的SSH连接 client, err := ssh.Dial("tcp", net.JoinHostPort(targetIP, targetPort), sshConfig) if err != nil { log.Fatalf("连接目标VM失败: %v", err) } defer client.Close() // 确保连接最终关闭 // 创建SSH会话用于执行命令 session, err := client.NewSession() if err != nil { log.Fatalf("创建SSH会话失败: %v", err) } defer session.Close() // 捕获命令的标准输出 stdout, err := session.StdoutPipe() if err != nil { log.Fatalf("获取标准输出管道失败: %v", err) } // 执行你需要的命令(比如ls) err = session.Run("ls") if err != nil { log.Fatalf("执行命令失败: %v", err) } // 读取并打印命令输出 scanner := bufio.NewScanner(stdout) for scanner.Scan() { fmt.Println(scanner.Text()) } if err := scanner.Err(); err != nil { log.Fatalf("读取命令输出失败: %v", err) } } // expandHomeDir 扩展路径中的~为当前用户的主目录 func expandHomeDir(path string) (string, error) { if len(path) == 0 || path[0] != '~' { return path, nil } homeDir, err := os.UserHomeDir() if err != nil { return "", fmt.Errorf("获取用户主目录失败: %w", err) } return homeDir + path[1:], nil }
关键细节说明
- 私钥路径处理:Go的文件读取函数不会自动解析
~,所以我们需要自己实现expandHomeDir函数来扩展路径。 - 主机密钥检查:示例中用
ssh.InsecureIgnoreHostKey()跳过了检查,这在测试环境没问题,但生产环境建议提前获取目标VM的主机密钥,用ssh.FixedHostKey()来验证,避免中间人攻击。 - 连接保活:通过
ServerAliveInterval设置每600秒发送一次保活包,确保长时间空闲的连接不会被防火墙断开。 - 命令执行:通过
session.Run()执行命令,你可以替换成任何需要在目标VM上运行的命令,比如cat /etc/os-release等。
额外注意事项
- 确保代理VM上的
prox_ssh_id私钥对应的公钥已经添加到目标VM的~/.ssh/authorized_keys文件中,否则会认证失败。 - 目标VM的防火墙规则要允许代理VM的IP访问22端口(SSH端口)。
内容来源于stack exchange
相关产品推荐
相关产品推荐

