Skip to content

Instantly share code, notes, and snippets.

@iamralch
Last active August 21, 2024 05:15
Show Gist options
  • Select an option

  • Save iamralch/5d695dcc4cc6ad5dd275 to your computer and use it in GitHub Desktop.

Select an option

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

cvvs commented Dec 23, 2015

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

@kevinpostal
Copy link
Copy Markdown

This code does not work?

@netmiller
Copy link
Copy Markdown

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

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

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
Copy Markdown
Author

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

@iamralch
Copy link
Copy Markdown
Author

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

MIT

@daihu
Copy link
Copy Markdown

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