Last active
August 12, 2024 16:54
-
-
Save codref/473351a24a3ef90162cf10857fac0ff3 to your computer and use it in GitHub Desktop.
Go SSH reverse tunnel implementation (SSH -R)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Go-Language implementation of an SSH Reverse Tunnel, the equivalent of below SSH command: | |
ssh -R 8080:127.0.0.1:8080 [email protected] | |
which opens a tunnel between the two endpoints and permit to exchange information on this direction: | |
server:8080 -----> client:8080 | |
once authenticated a process on the SSH server can interact with the service answering to port 8080 of the client | |
without any NAT rule via firewall | |
Copyright 2017, Davide Dal Farra | |
MIT License, http://www.opensource.org/licenses/mit-license.php | |
*/ | |
package main | |
import ( | |
"fmt" | |
"io" | |
"io/ioutil" | |
"log" | |
"net" | |
"golang.org/x/crypto/ssh" | |
) | |
type Endpoint struct { | |
Host string | |
Port int | |
} | |
func (endpoint *Endpoint) String() string { | |
return fmt.Sprintf("%s:%d", endpoint.Host, endpoint.Port) | |
} | |
// From https://sosedoff.com/2015/05/25/ssh-port-forwarding-with-go.html | |
// Handle local client connections and tunnel data to the remote server | |
// Will use io.Copy - http://golang.org/pkg/io/#Copy | |
func handleClient(client net.Conn, remote net.Conn) { | |
defer client.Close() | |
chDone := make(chan bool) | |
// Start remote -> local data transfer | |
go func() { | |
_, err := io.Copy(client, remote) | |
if err != nil { | |
log.Println(fmt.Sprintf("error while copy remote->local: %s", err)) | |
} | |
chDone <- true | |
}() | |
// Start local -> remote data transfer | |
go func() { | |
_, err := io.Copy(remote, client) | |
if err != nil { | |
log.Println(fmt.Sprintf("error while copy local->remote: %s", err)) | |
} | |
chDone <- true | |
}() | |
<-chDone | |
} | |
func publicKeyFile(file string) ssh.AuthMethod { | |
buffer, err := ioutil.ReadFile(file) | |
if err != nil { | |
log.Fatalln(fmt.Sprintf("Cannot read SSH public key file %s", file)) | |
return nil | |
} | |
key, err := ssh.ParsePrivateKey(buffer) | |
if err != nil { | |
log.Fatalln(fmt.Sprintf("Cannot parse SSH public key file %s", file)) | |
return nil | |
} | |
return ssh.PublicKeys(key) | |
} | |
// local service to be forwarded | |
var localEndpoint = Endpoint{ | |
Host: "localhost", | |
Port: 8080, | |
} | |
// remote SSH server | |
var serverEndpoint = Endpoint{ | |
Host: "146.148.22.123", | |
Port: 22, | |
} | |
// remote forwarding port (on remote SSH server network) | |
var remoteEndpoint = Endpoint{ | |
Host: "localhost", | |
Port: 8080, | |
} | |
func main() { | |
// refer to https://godoc.org/golang.org/x/crypto/ssh for other authentication types | |
sshConfig := &ssh.ClientConfig{ | |
// SSH connection username | |
User: "operatore", | |
Auth: []ssh.AuthMethod{ | |
// put here your private key path | |
publicKeyFile("/home/operatore/.ssh/id_rsa"), | |
}, | |
HostKeyCallback: ssh.InsecureIgnoreHostKey(), | |
} | |
// Connect to SSH remote server using serverEndpoint | |
serverConn, err := ssh.Dial("tcp", serverEndpoint.String(), sshConfig) | |
if err != nil { | |
log.Fatalln(fmt.Printf("Dial INTO remote server error: %s", err)) | |
} | |
// Listen on remote server port | |
listener, err := serverConn.Listen("tcp", remoteEndpoint.String()) | |
if err != nil { | |
log.Fatalln(fmt.Printf("Listen open port ON remote server error: %s", err)) | |
} | |
defer listener.Close() | |
// handle incoming connections on reverse forwarded tunnel | |
for { | |
// Open a (local) connection to localEndpoint whose content will be forwarded so serverEndpoint | |
local, err := net.Dial("tcp", localEndpoint.String()) | |
if err != nil { | |
log.Fatalln(fmt.Printf("Dial INTO local service error: %s", err)) | |
} | |
client, err := listener.Accept() | |
if err != nil { | |
log.Fatalln(err) | |
} | |
handleClient(client, local) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sorry, I didn't get a notification here. But we worked out the issue: function61/holepunch-client#10 (in case anyone else has trouble)