@@ -5,7 +5,10 @@ import (
5
5
"fmt"
6
6
"net"
7
7
"os"
8
+ "os/user"
9
+ "path/filepath"
8
10
11
+ "github.com/src-d/crypto/ssh/knownhosts"
9
12
"golang.org/x/crypto/ssh"
10
13
"golang.org/x/crypto/ssh/agent"
11
14
)
@@ -17,6 +20,7 @@ var ErrEmptySSHAgentAddr = errors.New("SSH_AUTH_SOCK env variable is required")
17
20
// configuration needed to establish an ssh connection.
18
21
type AuthMethod interface {
19
22
clientConfig () * ssh.ClientConfig
23
+ hostKeyCallback () (ssh.HostKeyCallback , error )
20
24
}
21
25
22
26
// The names of the AuthMethod implementations. To be returned by the
@@ -35,6 +39,7 @@ const (
35
39
type KeyboardInteractive struct {
36
40
User string
37
41
Challenge ssh.KeyboardInteractiveChallenge
42
+ baseAuthMethod
38
43
}
39
44
40
45
func (a * KeyboardInteractive ) Name () string {
@@ -56,6 +61,7 @@ func (a *KeyboardInteractive) clientConfig() *ssh.ClientConfig {
56
61
type Password struct {
57
62
User string
58
63
Pass string
64
+ baseAuthMethod
59
65
}
60
66
61
67
func (a * Password ) Name () string {
@@ -78,6 +84,7 @@ func (a *Password) clientConfig() *ssh.ClientConfig {
78
84
type PasswordCallback struct {
79
85
User string
80
86
Callback func () (pass string , err error )
87
+ baseAuthMethod
81
88
}
82
89
83
90
func (a * PasswordCallback ) Name () string {
@@ -100,6 +107,7 @@ func (a *PasswordCallback) clientConfig() *ssh.ClientConfig {
100
107
type PublicKeys struct {
101
108
User string
102
109
Signer ssh.Signer
110
+ baseAuthMethod
103
111
}
104
112
105
113
func (a * PublicKeys ) Name () string {
@@ -122,6 +130,7 @@ func (a *PublicKeys) clientConfig() *ssh.ClientConfig {
122
130
type PublicKeysCallback struct {
123
131
User string
124
132
Callback func () (signers []ssh.Signer , err error )
133
+ baseAuthMethod
125
134
}
126
135
127
136
func (a * PublicKeysCallback ) Name () string {
@@ -163,3 +172,80 @@ func NewSSHAgentAuth(user string) (*PublicKeysCallback, error) {
163
172
Callback : agent .NewClient (pipe ).Signers ,
164
173
}, nil
165
174
}
175
+
176
+ // NewKnownHostsCallback returns ssh.HostKeyCallback based on a file based on a
177
+ // know_hosts file. http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
178
+ //
179
+ // If files is empty, the list of files will be read from the SSH_KNOWN_HOSTS
180
+ // environment variable, example:
181
+ // /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
182
+ //
183
+ // If SSH_KNOWN_HOSTS is not set the following file locations will be used:
184
+ // ~/.ssh/known_hosts
185
+ // /etc/ssh/ssh_known_hosts
186
+ func NewKnownHostsCallback (files ... string ) (ssh.HostKeyCallback , error ) {
187
+ files , err := getDefaultKnownHostsFiles ()
188
+ if err != nil {
189
+ return nil , err
190
+ }
191
+
192
+ files , err = filterKnownHostsFiles (files ... )
193
+ if err != nil {
194
+ return nil , err
195
+ }
196
+
197
+ return knownhosts .New (files ... )
198
+ }
199
+
200
+ func getDefaultKnownHostsFiles () ([]string , error ) {
201
+ files := filepath .SplitList (os .Getenv ("SSH_KNOWN_HOSTS" ))
202
+ if len (files ) != 0 {
203
+ return files , nil
204
+ }
205
+
206
+ user , err := user .Current ()
207
+ if err != nil {
208
+ return nil , err
209
+ }
210
+
211
+ return []string {
212
+ filepath .Join (user .HomeDir , "/.ssh/known_hosts" ),
213
+ "/etc/ssh/ssh_known_hosts" ,
214
+ }, nil
215
+ }
216
+
217
+ func filterKnownHostsFiles (files ... string ) ([]string , error ) {
218
+ var out []string
219
+ for _ , file := range files {
220
+ _ , err := os .Stat (file )
221
+ if err == nil {
222
+ out = append (out , file )
223
+ continue
224
+ }
225
+
226
+ if ! os .IsNotExist (err ) {
227
+ return nil , err
228
+ }
229
+ }
230
+
231
+ if len (out ) == 0 {
232
+ return nil , fmt .Errorf ("unable to find any valid know_hosts file, set SSH_KNOWN_HOSTS env variable" )
233
+ }
234
+
235
+ return out , nil
236
+ }
237
+
238
+ type baseAuthMethod struct {
239
+ // HostKeyCallback is the function type used for verifying server keys.
240
+ // If nil default callback will be create using NewKnownHostsHostKeyCallback
241
+ // without argument.
242
+ HostKeyCallback ssh.HostKeyCallback
243
+ }
244
+
245
+ func (m * baseAuthMethod ) hostKeyCallback () (ssh.HostKeyCallback , error ) {
246
+ if m .HostKeyCallback == nil {
247
+ return NewKnownHostsCallback ()
248
+ }
249
+
250
+ return m .HostKeyCallback , nil
251
+ }
0 commit comments