Simple SSH port forward in Golang

前端 未结 4 880
忘掉有多难
忘掉有多难 2020-12-28 09:45

I\'m trying to create (and later close) a simple TCP port forward over SSH with Go. I\'m new to Golang and statically typed languages. (Coming from Ruby.)

In a termi

4条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-28 10:19

    I finally figured out how to do this, I got hints from schmichael in an IRC channel. Thanks to all!

    EDIT: A little explanation: A big part of the problem I was having was that I did not realize a local net.Listener (not just a local net.Conn) needed setup to receive a local request and create the net.Conn before forwarding the bytes. Also, there exist both port forwards and reverse port forwards and I hadn't previously thought in detail about the fact that a regular port forward also sends bytes back, so copying the remote reader to local writer was not something I had implemented, yet it's very much needed. Here is an attempt to relate the essence of what this code does:

    • Listen on local port 9000.
    • Upon attempted read from local port 9000: (listener.Accept()),
    • Accept connection and return a local io.Reader and io.Writer and,
    • Connect to remote server and,
    • Connect to remote port 9999 returning a io.Reader and io.Writer.
    • Continually copy local io.Reader bytes to remote io.Writer,
    • Continually copy remote io.Reader bytes to local io.Writer.

    Here is the code:

    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)
        }
    }
    

提交回复
热议问题