Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit c68ac9e

Browse files
authored
Merge pull request #346 from mcuadros/ssh-known-hosts
transport: ssh, default HostKeyCallback
2 parents c991d2d + 649de29 commit c68ac9e

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

plumbing/transport/ssh/auth_method.go

+86
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import (
55
"fmt"
66
"net"
77
"os"
8+
"os/user"
9+
"path/filepath"
810

11+
"github.com/src-d/crypto/ssh/knownhosts"
912
"golang.org/x/crypto/ssh"
1013
"golang.org/x/crypto/ssh/agent"
1114
)
@@ -17,6 +20,7 @@ var ErrEmptySSHAgentAddr = errors.New("SSH_AUTH_SOCK env variable is required")
1720
// configuration needed to establish an ssh connection.
1821
type AuthMethod interface {
1922
clientConfig() *ssh.ClientConfig
23+
hostKeyCallback() (ssh.HostKeyCallback, error)
2024
}
2125

2226
// The names of the AuthMethod implementations. To be returned by the
@@ -35,6 +39,7 @@ const (
3539
type KeyboardInteractive struct {
3640
User string
3741
Challenge ssh.KeyboardInteractiveChallenge
42+
baseAuthMethod
3843
}
3944

4045
func (a *KeyboardInteractive) Name() string {
@@ -56,6 +61,7 @@ func (a *KeyboardInteractive) clientConfig() *ssh.ClientConfig {
5661
type Password struct {
5762
User string
5863
Pass string
64+
baseAuthMethod
5965
}
6066

6167
func (a *Password) Name() string {
@@ -78,6 +84,7 @@ func (a *Password) clientConfig() *ssh.ClientConfig {
7884
type PasswordCallback struct {
7985
User string
8086
Callback func() (pass string, err error)
87+
baseAuthMethod
8188
}
8289

8390
func (a *PasswordCallback) Name() string {
@@ -100,6 +107,7 @@ func (a *PasswordCallback) clientConfig() *ssh.ClientConfig {
100107
type PublicKeys struct {
101108
User string
102109
Signer ssh.Signer
110+
baseAuthMethod
103111
}
104112

105113
func (a *PublicKeys) Name() string {
@@ -122,6 +130,7 @@ func (a *PublicKeys) clientConfig() *ssh.ClientConfig {
122130
type PublicKeysCallback struct {
123131
User string
124132
Callback func() (signers []ssh.Signer, err error)
133+
baseAuthMethod
125134
}
126135

127136
func (a *PublicKeysCallback) Name() string {
@@ -163,3 +172,80 @@ func NewSSHAgentAuth(user string) (*PublicKeysCallback, error) {
163172
Callback: agent.NewClient(pipe).Signers,
164173
}, nil
165174
}
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+
}

plumbing/transport/ssh/common.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,13 @@ func (c *command) connect() error {
8282
}
8383

8484
var err error
85-
c.client, err = ssh.Dial("tcp", c.getHostWithPort(), c.auth.clientConfig())
85+
config := c.auth.clientConfig()
86+
config.HostKeyCallback, err = c.auth.hostKeyCallback()
87+
if err != nil {
88+
return err
89+
}
90+
91+
c.client, err = ssh.Dial("tcp", c.getHostWithPort(), config)
8692
if err != nil {
8793
return err
8894
}

plumbing/transport/ssh/upload_pack_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,4 @@ func (s *UploadPackSuite) SetUpSuite(c *C) {
3333
ep, err = transport.NewEndpoint("[email protected]:git-fixtures/non-existent.git")
3434
c.Assert(err, IsNil)
3535
s.UploadPackSuite.NonExistentEndpoint = ep
36-
3736
}

0 commit comments

Comments
 (0)