Skip to content

Instantly share code, notes, and snippets.

@iamralch
Last active April 16, 2023 03:07
Show Gist options
  • Save iamralch/5d695dcc4cc6ad5dd275 to your computer and use it in GitHub Desktop.
Save iamralch/5d695dcc4cc6ad5dd275 to your computer and use it in GitHub Desktop.
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) {
defer writer.Close()
defer reader.Close()
_, 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
Copy link

cvvs commented Dec 23, 2015

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

@kevinpostal
Copy link

This code does not work?

@netmiller
Copy link

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
Copy link

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!

@yefriddavid
Copy link

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!

yeah, this was a huge bug for me, now is fixed here elliotchance/sshtunnel#1

@iamralch
Copy link
Author

I updated the copyConn to close the connections when the function finishes its job. Thanks for the feedback.

@iamralch
Copy link
Author

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

MIT

@daihu
Copy link

daihu commented Jul 8, 2020

work for me

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