Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
SSH tunnelling in Golang
package main
import (
"log"
"bufio"
"time"
"os"
"fmt"
"io"
"net"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
type Endpoint struct {
Host string
Port int
}
func (endpoint *Endpoint) String() string {
return fmt.Sprintf("%s:%d", endpoint.Host, endpoint.Port)
}
type SSHtunnel struct {
Local *Endpoint
Server *Endpoint
Remote *Endpoint
Config *ssh.ClientConfig
}
func (tunnel *SSHtunnel) Start() error {
listener, err := net.Listen("tcp", tunnel.Local.String())
if err != nil {
return err
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
return err
}
go tunnel.forward(conn)
}
}
func (tunnel *SSHtunnel) forward(localConn net.Conn) {
serverConn, err := ssh.Dial("tcp", tunnel.Server.String(), tunnel.Config)
if err != nil {
fmt.Printf("Server dial error: %s\n", err)
return
}
remoteConn, err := serverConn.Dial("tcp", tunnel.Remote.String())
if err != nil {
fmt.Printf("Remote dial error: %s\n", err)
return
}
copyConn:=func(writer, reader net.Conn) {
_, err:= io.Copy(writer, reader)
if err != nil {
fmt.Printf("io.Copy error: %s", err)
}
}
go copyConn(localConn, remoteConn)
go copyConn(remoteConn, localConn)
}
func SSHAgent() ssh.AuthMethod {
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
return ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers)
}
return nil
}
func main() {
localEndpoint := &Endpoint{
Host: "localhost",
Port: 9000,
}
serverEndpoint := &Endpoint{
Host: "example.com",
Port: 22,
}
remoteEndpoint := &Endpoint{
Host: "localhost",
Port: 8080,
}
sshConfig := &ssh.ClientConfig{
User: "vcap",
Auth: []ssh.AuthMethod{
SSHAgent(),
},
}
tunnel := &SSHtunnel{
Config: sshConfig,
Local: localEndpoint,
Server: serverEndpoint,
Remote: remoteEndpoint,
}
tunnel.Start()
}
@cvvs

This comment has been minimized.

Show comment Hide comment
@cvvs

cvvs Dec 23, 2015

Svett, thanks for the sample code. Under which license, if any, is this released?

cvvs commented Dec 23, 2015

Svett, thanks for the sample code. Under which license, if any, is this released?

@kevinpostal

This comment has been minimized.

Show comment Hide comment
@kevinpostal

kevinpostal Nov 21, 2016

This code does not work?

This code does not work?

@netmiller

This comment has been minimized.

Show comment Hide comment
@netmiller

netmiller Jan 22, 2017

Works for me: OS X 10.12 -> Ubuntu 16.04 with OpenSSH.
Modified sshConfig/AuthMethod :

sshConfig := &ssh.ClientConfig{
        User: "testusr",
        Auth: []ssh.AuthMethod{ PublicKeyFile("./test_rsa") },
        // Auth: []ssh.AuthMethod{
        //     SSHAgent(),
        // },
    }

Nice work.

Works for me: OS X 10.12 -> Ubuntu 16.04 with OpenSSH.
Modified sshConfig/AuthMethod :

sshConfig := &ssh.ClientConfig{
        User: "testusr",
        Auth: []ssh.AuthMethod{ PublicKeyFile("./test_rsa") },
        // Auth: []ssh.AuthMethod{
        //     SSHAgent(),
        // },
    }

Nice work.

@tkandal

This comment has been minimized.

Show comment Hide comment
@tkandal

tkandal Sep 24, 2017

Excuse me, but if one use this code as general ssh-forwarder it will eventually exhaust the connetion-pool,- because it will dial a new ssh-connection every time it accepts a new connection on the local port.
I would instead have tested if it has a ssh-connection already and re-use that one,- and that is a very easy and simple solution.
Also I would have added reader.Close(), writer.Close() in copyConn, and a Close()-method for gracefully shutting down the listener and server-connection.
That said,- thank You very much for the code! I learned that I could use ssh and ssh-forwarding very easily in Go!

tkandal commented Sep 24, 2017

Excuse me, but if one use this code as general ssh-forwarder it will eventually exhaust the connetion-pool,- because it will dial a new ssh-connection every time it accepts a new connection on the local port.
I would instead have tested if it has a ssh-connection already and re-use that one,- and that is a very easy and simple solution.
Also I would have added reader.Close(), writer.Close() in copyConn, and a Close()-method for gracefully shutting down the listener and server-connection.
That said,- thank You very much for the code! I learned that I could use ssh and ssh-forwarding very easily in Go!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment