go的ssh tunnel
该链接为讲解
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
浙公网安备 33010602011771号