forked from cloudfoundry/bosh-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ssh_args.go
148 lines (119 loc) · 3.66 KB
/
ssh_args.go
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package ssh
import (
"fmt"
"net"
"strings"
boshdir "github.com/cloudfoundry/bosh-cli/director"
boshhttp "github.com/cloudfoundry/bosh-utils/httpclient"
boshsys "github.com/cloudfoundry/bosh-utils/system"
proxy "github.com/cloudfoundry/socks5-proxy"
)
type SSHArgs struct {
ConnOpts ConnectionOpts
Result boshdir.SSHResult
ForceTTY bool
PrivKeyFile boshsys.File
KnownHostsFile boshsys.File
socks5Proxy *proxy.Socks5Proxy
dialer proxy.DialFunc
}
func NewSSHArgs(connOpts ConnectionOpts, result boshdir.SSHResult, forceTTY bool, privKeyFile boshsys.File, knownHostsFile boshsys.File) SSHArgs {
socks5Proxy := proxy.NewSocks5Proxy(proxy.NewHostKey(), nil)
boshhttpDialer := boshhttp.SOCKS5DialFuncFromEnvironment(net.Dial, socks5Proxy)
dialer := func(net, addr string) (net.Conn, error) {
return boshhttpDialer(net, addr)
}
return SSHArgs{
ConnOpts: connOpts,
Result: result,
ForceTTY: forceTTY,
PrivKeyFile: privKeyFile,
KnownHostsFile: knownHostsFile,
socks5Proxy: socks5Proxy,
dialer: dialer,
}
}
func (a SSHArgs) LoginForHost(host boshdir.Host) []string {
return []string{host.Host, "-l", host.Username}
}
func (a SSHArgs) OptsForHost(host boshdir.Host) []string {
// Options are used for both ssh and scp
cmdOpts := []string{}
if a.ForceTTY {
cmdOpts = append(cmdOpts, "-tt")
}
cmdOpts = append(cmdOpts, []string{
"-o", "ServerAliveInterval=30",
"-o", "ForwardAgent=no",
"-o", "PasswordAuthentication=no",
"-o", "IdentitiesOnly=yes",
"-o", "IdentityFile=" + a.PrivKeyFile.Name(),
"-o", "StrictHostKeyChecking=yes",
"-o", "UserKnownHostsFile=" + a.KnownHostsFile.Name(),
}...)
gwUsername, gwHost, gwPrivKeyPath := a.gwOpts()
if len(a.ConnOpts.SOCKS5Proxy) > 0 {
proxyString := a.ConnOpts.SOCKS5Proxy
if strings.HasPrefix(proxyString, "ssh+") {
a.socks5Proxy.StartWithDialer(a.dialer)
proxyString, _ = a.socks5Proxy.Addr()
}
proxyOpt := fmt.Sprintf(
"ProxyCommand=nc -x %s %%h %%p",
strings.TrimPrefix(proxyString, "socks5://"),
)
cmdOpts = append(cmdOpts, "-o", proxyOpt)
} else if len(gwHost) > 0 {
gwCmdOpts := []string{
"-o", "ServerAliveInterval=30",
"-o", "ForwardAgent=no",
"-o", "ClearAllForwardings=yes",
// Strict host key checking for a gateway is not necessary
// since ProxyCommand is only used for forwarding TCP and
// agent forwarding is disabled
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
}
if len(gwPrivKeyPath) > 0 {
gwCmdOpts = append(
gwCmdOpts,
"-o", "PasswordAuthentication=no",
"-o", "IdentitiesOnly=yes",
"-o", "IdentityFile="+gwPrivKeyPath,
)
}
// It appears that when using ssh -W, IPv6 address needs to be put in brackets
// fixes: `Bad stdio forwarding specification 'fd7a:eeed:e696:...'`
proxyHostPortTmpl := "%h:%p"
if strings.Contains(host.Host, ":") {
proxyHostPortTmpl = "[%h]:%p"
}
proxyOpt := fmt.Sprintf(
// Always force TTY for gateway ssh
"ProxyCommand=ssh -tt -W %s -l %s %s %s",
proxyHostPortTmpl,
gwUsername,
gwHost,
strings.Join(gwCmdOpts, " "),
)
cmdOpts = append(cmdOpts, "-o", proxyOpt)
}
cmdOpts = append(cmdOpts, a.ConnOpts.RawOpts...)
return cmdOpts
}
func (a SSHArgs) gwOpts() (string, string, string) {
if a.ConnOpts.GatewayDisable {
return "", "", ""
}
// Take server provided gateway options
username := a.Result.GatewayUsername
host := a.Result.GatewayHost
if len(a.ConnOpts.GatewayUsername) > 0 {
username = a.ConnOpts.GatewayUsername
}
if len(a.ConnOpts.GatewayHost) > 0 {
host = a.ConnOpts.GatewayHost
}
privKeyPath := a.ConnOpts.GatewayPrivateKeyPath
return username, host, privKeyPath
}