You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取代理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
}

关键细节说明

  1. 私钥路径处理:Go的文件读取函数不会自动解析~,所以我们需要自己实现expandHomeDir函数来扩展路径。
  2. 主机密钥检查:示例中用ssh.InsecureIgnoreHostKey()跳过了检查,这在测试环境没问题,但生产环境建议提前获取目标VM的主机密钥,用ssh.FixedHostKey()来验证,避免中间人攻击。
  3. 连接保活:通过ServerAliveInterval设置每600秒发送一次保活包,确保长时间空闲的连接不会被防火墙断开。
  4. 命令执行:通过session.Run()执行命令,你可以替换成任何需要在目标VM上运行的命令,比如cat /etc/os-release等。

额外注意事项

  • 确保代理VM上的prox_ssh_id私钥对应的公钥已经添加到目标VM的~/.ssh/authorized_keys文件中,否则会认证失败。
  • 目标VM的防火墙规则要允许代理VM的IP访问22端口(SSH端口)。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 11:43:06