Golang中的简单SSH端口转发

时间:2014-01-28 00:48:28

标签: go

我尝试使用Go创建(并稍后关闭)通过SSH转发的简单TCP端口。我是Golang和静态类型语言的新手。 (来自Ruby。)

在终端中,我只需运行ssh -L 9000:localhost:9999 user@server.com,这就完成了我的需要。我想以编程方式使用Go。

我已经尝试使用this example作为起点而this recent test试图了解该做什么,但现在我有一堆令人困惑的混乱代码,因为它看起来实际上非常简单要做的事。

非常感谢任何帮助! : - )

4 个答案:

答案 0 :(得分:15)

我终于弄清楚如何做到这一点,我在一个IRC频道得到了schmichael的提示。谢谢大家!

编辑:一点解释: 我遇到的问题很大一部分是我没有意识到本地net.Listener(不仅仅是本地net.Conn)需要设置来接收本地请求并在转发之前创建net.Conn字节。 此外,还存在端口转发和反向端口转发,我以前没有详细考虑过常规端口转发还会返回字节的事实,因此将远程读取器复制到本地编写器并不是我实现的,但它是非常需要。 以下是尝试关联此代码的本质:

  • 收听本地端口9000。
  • 尝试从本地端口9000读取:(listener.Accept()),
  • 接受连接并返回本地io.Readerio.Writer以及
  • 连接到远程服务器,
  • 连接到远程端口9999,返回io.Readerio.Writer
  • 将本地io.Reader字节连续复制到远程io.Writer
  • 将远程io.Reader字节连续复制到本地io.Writer

以下是代码:

package main

// Forward from local port 9000 to remote port 9999

import (
    "io"
    "log"
    "net"
    "golang.org/x/crypto/ssh"
)

var (
    username         = "root"
    password         = "password"
    serverAddrString = "192.168.1.100:22"
    localAddrString  = "localhost:9000"
    remoteAddrString = "localhost:9999"
)

func forward(localConn net.Conn, config *ssh.ClientConfig) {
    // Setup sshClientConn (type *ssh.ClientConn)
    sshClientConn, err := ssh.Dial("tcp", serverAddrString, config)
    if err != nil {
        log.Fatalf("ssh.Dial failed: %s", err)
    }

    // Setup sshConn (type net.Conn)
    sshConn, err := sshClientConn.Dial("tcp", remoteAddrString)

    // Copy localConn.Reader to sshConn.Writer
    go func() {
        _, err = io.Copy(sshConn, localConn)
        if err != nil {
            log.Fatalf("io.Copy failed: %v", err)
        }
    }()

    // Copy sshConn.Reader to localConn.Writer
    go func() {
        _, err = io.Copy(localConn, sshConn)
        if err != nil {
            log.Fatalf("io.Copy failed: %v", err)
        }
    }()
}

func main() {
    // Setup SSH config (type *ssh.ClientConfig)
    config := &ssh.ClientConfig{
        User: username,
        Auth: []ssh.AuthMethod{
            ssh.Password(password),
        },
    }

    // Setup localListener (type net.Listener)
    localListener, err := net.Listen("tcp", localAddrString)
    if err != nil {
        log.Fatalf("net.Listen failed: %v", err)
    }

    for {
        // Setup localConn (type net.Conn)
        localConn, err := localListener.Accept()
        if err != nil {
            log.Fatalf("listen.Accept failed: %v", err)
        }
        go forward(localConn, config)
    }
}

答案 1 :(得分:4)

我已经使用您的(damick)示例代码构建了一个小型开源工具: SSHTunnel https://github.com/SommerEngineering/SSHTunnel

因此,GitHub上的任何人都可以免费获得该代码:请随时将其用于学习目的或其他任何内容:)我已经提到了您的昵称并且也与此问题相关联。

祝你好运, 和Thorsten。

答案 2 :(得分:2)

我完成了一个名为mallory的简单SSH端口转发工具。 它提供HTTP代理而不是SOCKS代理,这与ssh -D非常相似。

核心代码类似于达米克的答案。

  1. 创建ClientConfig
  2. ssh.Dial使用配置远程SSH服务器并返回Client
  3. 现在,您可以使用Client.Dial 转发您喜欢的任何内容。

      

    拨号从远程主机启动与addr的连接。结果连接的LocalAddr()和RemoteAddr()为零。

  4.   
  5. 如果要为SOCKS代理服务器提供服务,请使用Client.Dial连接到远程服务器。
  6.   

答案 3 :(得分:0)

我用这个工具编写了一个名为gosshtool的工具,你可以通过SSH轻松创建一个简单的TCP端口。https://github.com/scottkiss/gosshtool

我使用此工具实现了一个端口转发服务器示例项目: https://github.com/scottkiss/gooverssh