Golang中的简单SSH端口转发

dam*_*ick 10 go

我正在尝试使用Go创建(并稍后关闭)一个简单的TCP端口.我是Golang和静态类型语言的新手.(来自Ruby.)

在一个终端我会简单地运行ssh -L 9000:localhost:9999 user@server.com,这就完成了我需要的东西.我想用Go编程实现同样的功能.

我已经尝试使用这个例子作为起点和最近的测试试图理解该怎么做,但现在我有一堆令人困惑的混乱代码,看起来这实际上是一个非常简单的事情.

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

dam*_*ick 15

我终于想出了如何做到这一点,我在一个IRC频道得到了schmichael的提示.谢谢大家!

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

  • 听当地港口9000.
  • 尝试从本地端口9000读取:( listener.Accept()),
  • 接受连接并返回本地io.Readerio.Writer和,
  • 连接到远程服务器,
  • 连接到远程端口9999返回a 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 只是一个注意事项--`go.grypto`包现在托管在`golang.org/x/crypto/ssh`,加上为了让这个优秀的例子起作用(谢谢@damick!),你需要深入研究根据对包API的更改,使用`ssh.AuthMethod`交换`ssh.ClientAuth`. (2认同)

Som*_*ing 5

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

因此,任何人都可以在 GitHub 上免费获得该代码:请随意将其用于学习目的或其他任何目的:) 我已经提到了您的昵称,也链接到了这个问题。

最好的问候,托尔斯滕。