该链接为讲解
https://elliotchance.medium.com/how-to-create-an-ssh-tunnel-in-go-b63722d682aa
package main import ( "fmt" "golang.org/x/crypto/ssh" "io" "io/ioutil" "log" "net" "strconv" "strings" ) type Endpoint struct { Host string Port int User string } func NewEndpoint(s string) *Endpoint { endpoint := &Endpoint{ Host: s, } if parts := strings.Split(endpoint.Host, "@"); len(parts) > 1 { endpoint.User = parts[0] endpoint.Host = parts[1] } if parts := strings.Split(endpoint.Host, ":"); len(parts) > 1 { endpoint.Host = parts[0] endpoint.Port, _ = strconv.Atoi(parts[1]) } return endpoint } 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 Log *log.Logger } func (tunnel *SSHTunnel) logf(fmt string, args ...interface{}) { if tunnel.Log != nil { tunnel.Log.Printf(fmt, args...) } } func (tunnel *SSHTunnel) Start() error { listener, err := net.Listen("tcp", tunnel.Local.String()) if err != nil { return err } defer listener.Close() tunnel.Local.Port = listener.Addr().(*net.TCPAddr).Port for { conn, err := listener.Accept() if err != nil { return err } tunnel.logf("accepted connection") go tunnel.forward(conn) } } func (tunnel *SSHTunnel) forward(localConn net.Conn) { serverConn, err := ssh.Dial("tcp", tunnel.Server.String(), tunnel.Config) if err != nil { tunnel.logf("server dial error: %s", err) return } tunnel.logf("connected to %s (1 of 2)\n", tunnel.Server.String()) remoteConn, err := serverConn.Dial("tcp", tunnel.Remote.String()) if err != nil { tunnel.logf("remote dial error: %s", err) return } tunnel.logf("connected to %s (2 of 2)\n", tunnel.Remote.String()) copyConn := func(writer, reader net.Conn) { _, err := io.Copy(writer, reader) if err != nil { tunnel.logf("io.Copy error: %s", err) } } go copyConn(localConn, remoteConn) go copyConn(remoteConn, localConn) } func PrivateKeyFile(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) } func NewSSHTunnel(tunnel string, auth ssh.AuthMethod, destination string) *SSHTunnel { // A random port will be chosen for us. localEndpoint := NewEndpoint("localhost:0") server := NewEndpoint(tunnel) if server.Port == 0 { server.Port = 22 } sshTunnel := &SSHTunnel{ Config: &ssh.ClientConfig{ User: server.User, Auth: []ssh.AuthMethod{auth}, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { // Always accept key. return nil }, }, Local: localEndpoint, Server: server, Remote: NewEndpoint(destination), } return sshTunnel }
应用实例:
func main() { // Setup the tunnel, but do not yet start it yet. tunnel := NewSSHTunnel( // User and host of tunnel server, it will default to port 22 // if not specified. "ec2-user@jumpbox.us-east-1.mydomain.com", // Pick ONE of the following authentication methods: PrivateKeyFile("path/to/private/key.pem"), // 1. private key ssh.Password("password"), // 2. password // The destination host and port of the actual server. "dqrsdfdssdfx.us-east-1.redshift.amazonaws.com:5439", ) // You can provide a logger for debugging, or remove this line to // make it silent. tunnel.Log = log.New(os.Stdout, "", log.Ldate | log.Lmicroseconds) // Start the server in the background. You will need to wait a // small amount of time for it to bind to the localhost port // before you can start sending connections. go tunnel.Start() time.Sleep(100 * time.Millisecond) // NewSSHTunnel will bind to a random port so that you can have // multiple SSH tunnels available. The port is available through: // tunnel.Local.Port // You can use any normal Go code to connect to the destination // server through localhost. You may need to use 127.0.0.1 for // some libraries. // // Here is an example of connecting to a PostgreSQL server: conn := fmt.Sprintf("host=127.0.0.1 port=%d username=foo", tunnel.Local.Port) db, err := sql.Open("postgres", conn) // ... }
更方便的包:
https://github.com/elliotchance/sshtunnel