如何用Go代码实现等效于指定SSH隧道与ProxyJump的功能?
用Go实现带跳转机的SSH本地端口转发
你要实现的SSH命令等效于:通过IPv6跳转机root@[IPv6 address]建立到目标机myuser@100.1.1.100的SSH连接,并将本地9999端口的流量转发到目标机可访问的192.168.1.1:80。以下是具体实现步骤和代码:
核心逻辑拆解
- 先建立到跳转机的SSH客户端连接
- 通过跳转机的连接,建立到目标机的SSH连接
- 监听本地9999端口,将每个本地连接的流量通过目标机转发到
192.168.1.1:80
完整代码示例
package main import ( "io" "log" "net" "sync" "golang.org/x/crypto/ssh" ) // 创建SSH客户端配置(示例用密码认证,可替换为密钥认证) func createSSHConfig(user, password string) *ssh.ClientConfig { return &ssh.ClientConfig{ User: user, Auth: []ssh.AuthMethod{ ssh.Password(password), }, // 生产环境请替换为安全的主机密钥验证逻辑,不要忽略主机密钥 HostKeyCallback: ssh.InsecureIgnoreHostKey(), } } func main() { // 配置参数,替换为你的实际信息 jumpIPv6Addr := "[2001:db8::1]" // 跳转机IPv6地址,保留方括号 jumpUser := "root" jumpPassword := "your_jump_password" targetHost := "100.1.1.100" targetUser := "myuser" targetPassword := "your_target_password" localPort := ":9999" remoteTarget := "192.168.1.1:80" // 1. 建立到跳转机的SSH连接 jumpSSHAddr := net.JoinHostPort(jumpIPv6Addr, "22") jumpConfig := createSSHConfig(jumpUser, jumpPassword) jumpClient, err := ssh.Dial("tcp", jumpSSHAddr, jumpConfig) if err != nil { log.Fatalf("连接跳转机失败: %v", err) } defer jumpClient.Close() // 2. 通过跳转机连接目标机的SSH服务 targetSSHAddr := net.JoinHostPort(targetHost, "22") targetConfig := createSSHConfig(targetUser, targetPassword) // 通过跳转机的TCP连接建立到目标机的SSH握手 targetRawConn, err := jumpClient.Dial("tcp", targetSSHAddr) if err != nil { log.Fatalf("通过跳转机连接目标机失败: %v", err) } targetConn, chans, reqs, err := ssh.NewClientConn(targetRawConn, targetSSHAddr, targetConfig) if err != nil { log.Fatalf("建立目标机SSH连接失败: %v", err) } targetClient := ssh.NewClient(targetConn, chans, reqs) defer targetClient.Close() // 3. 监听本地端口,开始转发流量 localListener, err := net.Listen("tcp", localPort) if err != nil { log.Fatalf("监听本地端口失败: %v", err) } defer localListener.Close() log.Printf("本地端口%s已监听,流量将转发到%s", localPort, remoteTarget) // 循环处理本地连接 for { localConn, err := localListener.Accept() if err != nil { log.Printf("接受本地连接失败: %v", err) continue } // 每个连接启动单独协程处理 go func(local net.Conn) { defer local.Close() // 通过目标机SSH连接建立到最终目标的TCP连接 remoteConn, err := targetClient.Dial("tcp", remoteTarget) if err != nil { log.Printf("连接远程目标%s失败: %v", remoteTarget, err) return } defer remoteConn.Close() // 双向拷贝数据,完成流量转发 var wg sync.WaitGroup wg.Add(2) go func() { io.Copy(local, remoteConn) wg.Done() }() go func() { io.Copy(remoteConn, local) wg.Done() }() wg.Wait() }(localConn) } }
关键注意事项
- 主机密钥验证:示例中用
ssh.InsecureIgnoreHostKey()跳过验证,生产环境必须实现安全的主机密钥校验,比如从known_hosts文件加载密钥进行验证。 - 认证方式替换:如果要用密钥认证,可将
ssh.Password(password)替换为ssh.PublicKeys(signer),其中signer通过ssh.ParsePrivateKey读取私钥文件生成。 - IPv6地址处理:跳转机的IPv6地址必须保留方括号,
net.JoinHostPort会自动处理成[IPv6]:22的正确格式。 - 错误处理:示例用
log.Fatalf终止程序,实际应用可根据需求添加重试、告警等逻辑。
内容的提问来源于stack exchange,提问作者Yulek
相关产品推荐
相关产品推荐

