glider/proxy/ssh/ssh.go

143 lines
2.8 KiB
Go
Raw Normal View History

2020-05-04 13:53:59 +08:00
package ssh
import (
"net"
"net/url"
2021-02-06 00:26:58 +08:00
"os"
2021-04-20 20:55:40 +08:00
"sync"
2020-05-04 13:53:59 +08:00
"time"
"golang.org/x/crypto/ssh"
"github.com/nadoo/glider/log"
2020-05-04 13:53:59 +08:00
"github.com/nadoo/glider/proxy"
)
2020-05-06 20:10:18 +08:00
// SSH is a base ssh struct.
2020-05-04 13:53:59 +08:00
type SSH struct {
dialer proxy.Dialer
proxy proxy.Proxy
addr string
2021-12-06 23:53:10 +08:00
mu sync.Mutex
sshCfg *ssh.ClientConfig
sshClient *ssh.Client
2020-05-04 13:53:59 +08:00
}
func init() {
proxy.RegisterDialer("ssh", NewSSHDialer)
}
2020-05-06 20:10:18 +08:00
// NewSSH returns a ssh proxy.
2020-05-04 13:53:59 +08:00
func NewSSH(s string, d proxy.Dialer, p proxy.Proxy) (*SSH, error) {
u, err := url.Parse(s)
if err != nil {
2021-12-05 19:03:57 +08:00
log.F("[ssh] parse err: %s", err)
2020-05-04 13:53:59 +08:00
return nil, err
}
user := u.User.Username()
if user == "" {
user = "root"
}
config := &ssh.ClientConfig{
User: user,
Timeout: time.Second * 3,
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
if pass, _ := u.User.Password(); pass != "" {
config.Auth = []ssh.AuthMethod{ssh.Password(pass)}
}
if key := u.Query().Get("key"); key != "" {
keyAuth, err := privateKeyAuth(key)
if err != nil {
log.F("[ssh] read key file error: %s", err)
return nil, err
}
config.Auth = append(config.Auth, keyAuth)
2020-05-04 13:53:59 +08:00
}
t := &SSH{
2020-05-04 13:53:59 +08:00
dialer: d,
proxy: p,
addr: u.Host,
sshCfg: config,
}
if _, port, _ := net.SplitHostPort(t.addr); port == "" {
t.addr = net.JoinHostPort(t.addr, "22")
2020-05-04 13:53:59 +08:00
}
2021-12-06 23:53:10 +08:00
return t, nil
2020-05-04 13:53:59 +08:00
}
// NewSSHDialer returns a ssh proxy dialer.
func NewSSHDialer(s string, d proxy.Dialer) (proxy.Dialer, error) {
return NewSSH(s, d, nil)
}
// Addr returns forwarder's address.
func (s *SSH) Addr() string {
if s.addr == "" {
return s.dialer.Addr()
}
return s.addr
}
func (s *SSH) initConn() error {
c, err := s.dialer.Dial("tcp", s.addr)
2020-05-04 13:53:59 +08:00
if err != nil {
log.F("[ssh]: dial to %s error: %s", s.addr, err)
return err
2020-05-04 13:53:59 +08:00
}
2021-12-06 23:53:10 +08:00
sshConn, sshChan, sshReq, err := ssh.NewClientConn(c, s.addr, s.sshCfg)
2020-05-04 13:53:59 +08:00
if err != nil {
log.F("[ssh]: initial connection to %s error: %s", s.addr, err)
return err
2020-05-04 13:53:59 +08:00
}
2021-12-06 23:53:10 +08:00
s.sshClient = ssh.NewClient(sshConn, sshChan, sshReq)
return nil
}
// Dial connects to the address addr on the network net via the proxy.
2021-04-20 20:55:40 +08:00
func (s *SSH) Dial(network, addr string) (net.Conn, error) {
2021-12-05 19:03:57 +08:00
s.mu.Lock()
defer s.mu.Unlock()
2021-12-06 23:53:10 +08:00
if s.sshClient != nil {
if c, err := s.sshClient.Dial(network, addr); err == nil {
return c, nil
}
s.sshClient.Conn.Close()
}
2021-07-07 19:05:26 +08:00
if err := s.initConn(); err != nil {
return nil, err
}
2021-12-06 23:53:10 +08:00
return s.sshClient.Dial(network, addr)
2020-05-04 13:53:59 +08:00
}
// DialUDP connects to the given address via the proxy.
func (s *SSH) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
return nil, nil, proxy.ErrNotSupported
2020-05-04 13:53:59 +08:00
}
func privateKeyAuth(file string) (ssh.AuthMethod, error) {
2021-02-06 00:26:58 +08:00
buffer, err := os.ReadFile(file)
2020-05-04 13:53:59 +08:00
if err != nil {
return nil, err
2020-05-04 13:53:59 +08:00
}
key, err := ssh.ParsePrivateKey(buffer)
if err != nil {
return nil, err
2020-05-04 13:53:59 +08:00
}
return ssh.PublicKeys(key), nil
2020-05-04 13:53:59 +08:00
}