-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathproxy_sshagent.go
More file actions
99 lines (84 loc) · 2.01 KB
/
proxy_sshagent.go
File metadata and controls
99 lines (84 loc) · 2.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//go:build !tinygo && proxyclient_ssh
package proxyclient
import (
"context"
"errors"
"io/ioutil"
"net/url"
"sync"
"golang.org/x/crypto/ssh"
)
func init() {
RegisterScheme("SSH", newSSHProxyClient)
}
type sshClientCache struct {
sync.RWMutex
clients map[string]*ssh.Client
}
var (
globalSSHCache = &sshClientCache{
clients: make(map[string]*ssh.Client),
}
)
func (c *sshClientCache) getClient(key string) *ssh.Client {
c.RLock()
defer c.RUnlock()
return c.clients[key]
}
func (c *sshClientCache) setClient(key string, client *ssh.Client) {
c.Lock()
defer c.Unlock()
c.clients[key] = client
}
func newSSHProxyClient(proxy *url.URL, upstreamDial Dial) (dial Dial, err error) {
if proxy.User == nil {
err = errors.New("must set username")
return
}
cacheKey := proxy.String()
if client := globalSSHCache.getClient(cacheKey); client != nil {
return WrapDialerContext(client.Dial).TCPOnly, nil
}
auth, err := sshAuth(proxy)
if err != nil {
return nil, err
}
conf := &ssh.ClientConfig{
User: proxy.User.Username(),
Auth: auth,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
conn, err := upstreamDial(context.Background(), "tcp", proxy.Host)
if err != nil {
return
}
sshConn, sshChans, sshRequests, err := ssh.NewClientConn(conn, proxy.Host, conf)
if err != nil {
return
}
sshClient := ssh.NewClient(sshConn, sshChans, sshRequests)
globalSSHCache.setClient(cacheKey, sshClient)
dial = WrapDialerContext(sshClient.Dial).TCPOnly
return
}
func sshAuth(proxy *url.URL) ([]ssh.AuthMethod, error) {
methods := []ssh.AuthMethod{}
publicKey := proxy.Query().Get("public-key")
if publicKey != "" {
buffer, err := ioutil.ReadFile(publicKey)
if err != nil {
return nil, err
}
key, err := ssh.ParsePrivateKey(buffer)
if err != nil {
return nil, err
}
method := ssh.PublicKeys(key)
methods = append(methods, method)
}
if password, ok := proxy.User.Password(); ok {
method := ssh.Password(password)
methods = append(methods, method)
}
return methods, nil
}