Skip to content

Commit

Permalink
fix(ssh): unable to pass a custom HostKeyCallback func
Browse files Browse the repository at this point in the history
Don't overwrite HostKeyCallback if one is provided.

Fixes: c35b808 ("plumbing: transport/ssh, auto-populate ClientConfig.HostKeyAlgorithms. Fixes go-git#411")
Fixes: go-git#654
Signed-off-by: Ayman Bagabas <ayman.bagabas@gmail.com>
  • Loading branch information
aymanbagabas committed Jan 9, 2023
1 parent 5dabd83 commit b522408
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 33 deletions.
28 changes: 15 additions & 13 deletions plumbing/transport/ssh/auth_method.go
Expand Up @@ -43,6 +43,7 @@ const (
type KeyboardInteractive struct {
User string
Challenge ssh.KeyboardInteractiveChallenge
HostKeyCallbackHelper
}

func (a *KeyboardInteractive) Name() string {
Expand All @@ -54,18 +55,19 @@ func (a *KeyboardInteractive) String() string {
}

func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
return &ssh.ClientConfig{
return a.SetHostKeyCallback(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{
a.Challenge,
},
}, nil
})
}

// Password implements AuthMethod by using the given password.
type Password struct {
User string
Password string
HostKeyCallbackHelper
}

func (a *Password) Name() string {
Expand All @@ -77,17 +79,18 @@ func (a *Password) String() string {
}

func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
return &ssh.ClientConfig{
return a.SetHostKeyCallback(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
}, nil
})
}

// PasswordCallback implements AuthMethod by using a callback
// to fetch the password.
type PasswordCallback struct {
User string
Callback func() (pass string, err error)
HostKeyCallbackHelper
}

func (a *PasswordCallback) Name() string {
Expand All @@ -99,16 +102,17 @@ func (a *PasswordCallback) String() string {
}

func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
return &ssh.ClientConfig{
return a.SetHostKeyCallback(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
}, nil
})
}

// PublicKeys implements AuthMethod by using the given key pairs.
type PublicKeys struct {
User string
Signer ssh.Signer
HostKeyCallbackHelper
}

// NewPublicKeys returns a PublicKeys from a PEM encoded private key. An
Expand Down Expand Up @@ -147,10 +151,10 @@ func (a *PublicKeys) String() string {
}

func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
return &ssh.ClientConfig{
return a.SetHostKeyCallback(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
}, nil
})
}

func username() (string, error) {
Expand All @@ -173,6 +177,7 @@ func username() (string, error) {
type PublicKeysCallback struct {
User string
Callback func() (signers []ssh.Signer, err error)
HostKeyCallbackHelper
}

// NewSSHAgentAuth returns a PublicKeysCallback based on a SSH agent, it opens
Expand Down Expand Up @@ -207,10 +212,10 @@ func (a *PublicKeysCallback) String() string {
}

func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
return &ssh.ClientConfig{
return a.SetHostKeyCallback(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
}, nil
})
}

// NewKnownHostsCallback returns ssh.HostKeyCallback based on a file based on a
Expand Down Expand Up @@ -286,9 +291,6 @@ func filterKnownHostsFiles(files ...string) ([]string, error) {

// HostKeyCallbackHelper is a helper that provides common functionality to
// configure HostKeyCallback into a ssh.ClientConfig.
// Deprecated in favor of SetConfigHostKeyFields (see common.go) which provides
// a mechanism for also setting ClientConfig.HostKeyAlgorithms for a specific
// host.
type HostKeyCallbackHelper struct {
// HostKeyCallback is the function type used for verifying server keys.
// If nil default callback will be create using NewKnownHostsCallback
Expand Down
26 changes: 6 additions & 20 deletions plumbing/transport/ssh/common.go
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/internal/common"
"github.com/skeema/knownhosts"

"github.com/kevinburke/ssh_config"
"golang.org/x/crypto/ssh"
Expand Down Expand Up @@ -122,9 +123,11 @@ func (c *command) connect() error {
return err
}
hostWithPort := c.getHostWithPort()
config, err = SetConfigHostKeyFields(config, hostWithPort)
if err != nil {
return err
if config.HostKeyCallback != nil {
// Set the HostKeyAlgorithms based on HostKeyCallback.
// For background see https://github.com/go-git/go-git/issues/411 as well as
// https://github.com/golang/go/issues/29286 for root cause.
config.HostKeyAlgorithms = knownhosts.HostKeyAlgorithms(config.HostKeyCallback, hostWithPort)
}

overrideConfig(c.config, config)
Expand Down Expand Up @@ -167,23 +170,6 @@ func dial(network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
return ssh.NewClient(c, chans, reqs), nil
}

// SetConfigHostKeyFields sets cfg.HostKeyCallback and cfg.HostKeyAlgorithms
// based on OpenSSH known_hosts. cfg is modified in-place. hostWithPort must be
// supplied, since the algorithms will be set based on the known host keys for
// that specific host. Otherwise, golang.org/x/crypto/ssh can return an error
// upon connecting to a host whose *first* key is not known, even though other
// keys (of different types) are known and match properly.
// For background see https://github.com/go-git/go-git/issues/411 as well as
// https://github.com/golang/go/issues/29286 for root cause.
func SetConfigHostKeyFields(cfg *ssh.ClientConfig, hostWithPort string) (*ssh.ClientConfig, error) {
kh, err := newKnownHosts()
if err == nil {
cfg.HostKeyCallback = kh.HostKeyCallback()
cfg.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
}
return cfg, err
}

func (c *command) getHostWithPort() string {
if addr, found := c.doGetHostWithPortFromSSHConfig(); found {
return addr
Expand Down

0 comments on commit b522408

Please sign in to comment.