Skip to content

Instantly share code, notes, and snippets.

@Manzanit0
Last active July 12, 2022 21:26
Show Gist options
  • Save Manzanit0/25fd3ad4dc1de2640d8aa8da47462ce4 to your computer and use it in GitHub Desktop.
Save Manzanit0/25fd3ad4dc1de2640d8aa8da47462ce4 to your computer and use it in GitHub Desktop.
Go: Running a request through an SSH tunnel
package main
import (
"io"
"io/ioutil"
"log"
"net"
"net/http"
"golang.org/x/crypto/ssh"
)
const (
// given the equivalent OpenSSH command:
// ssh -L 9201:my-service.prod.service.private:80 manzanit0@my-bastion.com
jumpServerUser = "manzanit0"
jumpServer = "my-bastion.com:22"
localAddr = "localhost:9201"
remoteAddr = "my-service.prod.service.private:80"
)
func main() {
authKey := AuthPrivateKeyFile("/Users/manzanit0/.ssh/id_rsa")
clientConfig, err := config(authKey)
if err != nil {
log.Fatalf("failed to create ssh config: %q", err)
}
clientConn, err := ssh.Dial("tcp", jumpServer, clientConfig)
if err != nil {
log.Fatalf("failed to connect to the ssh server: %q", err)
}
go func() {
if err := tunnel(clientConn, localAddr, remoteAddr); err != nil {
log.Fatalf("failed to tunnel traffic: %q", err)
}
}()
resp, err := http.Get("http://" + localAddr + "/health")
if err != nil {
log.Fatalf("failed make request to tunneled server: %q", err)
}
log.Printf("response: %s", resp.Status)
}
func tunnel(conn *ssh.Client, local, remote string) error {
pipe := func(writer, reader net.Conn) {
defer writer.Close()
defer reader.Close()
_, err := io.Copy(writer, reader)
if err != nil {
log.Printf("failed to copy: %s", err)
}
}
listener, err := net.Listen("tcp", local)
if err != nil {
return err
}
log.Printf("listening on %s", local)
for {
here, err := listener.Accept()
if err != nil {
return err
}
go func(here net.Conn) {
there, err := conn.Dial("tcp", remote)
if err != nil {
log.Fatalf("failed to dial to remote: %q", err)
}
go pipe(there, here)
go pipe(here, there)
}(here)
}
}
func config(methods ...ssh.AuthMethod) (*ssh.ClientConfig, error) {
return &ssh.ClientConfig{
User: jumpServerUser,
Auth: methods,
// you should not pass this option, but for the sake of simplicity
// we use it here
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
func AuthPrivateKeyFile(file string) ssh.AuthMethod {
buffer, err := ioutil.ReadFile(file)
if err != nil {
return nil
}
key, err := ssh.ParsePrivateKey(buffer)
if err != nil {
return nil
}
return ssh.PublicKeys(key)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment