aboutsummaryrefslogtreecommitdiffstats
path: root/plumbing/transport
diff options
context:
space:
mode:
authorJavier Alvarez Garcia <ja@daedalean.ai>2024-07-11 10:06:25 +0200
committerJavier Alvarez <javier.alvarez@allthingsembedded.net>2024-07-24 21:11:15 +0200
commit20b556be6a825f0255a71376a2bce4c2c8393b89 (patch)
tree25c21ae20d431fec15341b378ce903c259484c01 /plumbing/transport
parentec133065ea1201a4488bbc793ccd9337b6c06877 (diff)
downloadgo-git-20b556be6a825f0255a71376a2bce4c2c8393b89.tar.gz
plumbing: transport/ssh, Add support for SSH @cert-authority.
skeema/knownhosts v1.3.0 introduced a HostKeyDB type that extends the HostKeyCallback functionality to support @cert-authority algorithms. `known_hosts` files may contain lines with @cert-authority markers to indicate that a line corresponds to a certificate instead of a key. If a git remote uses cert authorities as the preferred host identification mechanism, the functionality added in skeema/knownhosts v1.3.0 is needed so that go-git can interact with this remote. See https://github.com/skeema/knownhosts/pull/9 for details.
Diffstat (limited to 'plumbing/transport')
-rw-r--r--plumbing/transport/ssh/auth_method.go13
-rw-r--r--plumbing/transport/ssh/auth_method_test.go106
-rw-r--r--plumbing/transport/ssh/common.go17
3 files changed, 124 insertions, 12 deletions
diff --git a/plumbing/transport/ssh/auth_method.go b/plumbing/transport/ssh/auth_method.go
index ac4e358..f9c598e 100644
--- a/plumbing/transport/ssh/auth_method.go
+++ b/plumbing/transport/ssh/auth_method.go
@@ -230,11 +230,11 @@ func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
// ~/.ssh/known_hosts
// /etc/ssh/ssh_known_hosts
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
- kh, err := newKnownHosts(files...)
- return ssh.HostKeyCallback(kh), err
+ db, err := newKnownHostsDb(files...)
+ return db.HostKeyCallback(), err
}
-func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
+func newKnownHostsDb(files ...string) (*knownhosts.HostKeyDB, error) {
var err error
if len(files) == 0 {
@@ -247,7 +247,7 @@ func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
return nil, err
}
- return knownhosts.New(files...)
+ return knownhosts.NewDB(files...)
}
func getDefaultKnownHostsFiles() ([]string, error) {
@@ -301,11 +301,12 @@ type HostKeyCallbackHelper struct {
// HostKeyCallback is empty a default callback is created using
// NewKnownHostsCallback.
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
- var err error
if m.HostKeyCallback == nil {
- if m.HostKeyCallback, err = NewKnownHostsCallback(); err != nil {
+ db, err := newKnownHostsDb()
+ if err != nil {
return cfg, err
}
+ m.HostKeyCallback = db.HostKeyCallback()
}
cfg.HostKeyCallback = m.HostKeyCallback
diff --git a/plumbing/transport/ssh/auth_method_test.go b/plumbing/transport/ssh/auth_method_test.go
index b275018..e3f652e 100644
--- a/plumbing/transport/ssh/auth_method_test.go
+++ b/plumbing/transport/ssh/auth_method_test.go
@@ -18,7 +18,8 @@ import (
type (
SuiteCommon struct{}
- mockKnownHosts struct{}
+ mockKnownHosts struct{}
+ mockKnownHostsWithCert struct{}
)
func (mockKnownHosts) host() string { return "github.com" }
@@ -27,6 +28,19 @@ func (mockKnownHosts) knownHosts() []byte {
}
func (mockKnownHosts) Network() string { return "tcp" }
func (mockKnownHosts) String() string { return "github.com:22" }
+func (mockKnownHosts) Algorithms() []string {
+ return []string{ssh.KeyAlgoRSA, ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512}
+}
+
+func (mockKnownHostsWithCert) host() string { return "github.com" }
+func (mockKnownHostsWithCert) knownHosts() []byte {
+ return []byte(`@cert-authority github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==`)
+}
+func (mockKnownHostsWithCert) Network() string { return "tcp" }
+func (mockKnownHostsWithCert) String() string { return "github.com:22" }
+func (mockKnownHostsWithCert) Algorithms() []string {
+ return []string{ssh.CertAlgoRSASHA512v01, ssh.CertAlgoRSASHA256v01, ssh.CertAlgoRSAv01}
+}
var _ = Suite(&SuiteCommon{})
@@ -230,3 +244,93 @@ func (*SuiteCommon) TestNewKnownHostsCallback(c *C) {
err = clb(mock.String(), mock, hostKey)
c.Assert(err, IsNil)
}
+
+func (*SuiteCommon) TestNewKnownHostsDbWithoutCert(c *C) {
+ if runtime.GOOS == "js" {
+ c.Skip("not available in wasm")
+ }
+
+ var mock = mockKnownHosts{}
+
+ f, err := util.TempFile(osfs.Default, "", "known-hosts")
+ c.Assert(err, IsNil)
+
+ _, err = f.Write(mock.knownHosts())
+ c.Assert(err, IsNil)
+
+ err = f.Close()
+ c.Assert(err, IsNil)
+
+ defer util.RemoveAll(osfs.Default, f.Name())
+
+ f, err = osfs.Default.Open(f.Name())
+ c.Assert(err, IsNil)
+
+ defer f.Close()
+
+ db, err := newKnownHostsDb(f.Name())
+ c.Assert(err, IsNil)
+
+ algos := db.HostKeyAlgorithms(mock.String())
+ c.Assert(algos, HasLen, len(mock.Algorithms()))
+
+ contains := func(container []string, value string) bool {
+ for _, inner := range container {
+ if inner == value {
+ return true
+ }
+ }
+ return false
+ }
+
+ for _, algorithm := range mock.Algorithms() {
+ if !contains(algos, algorithm) {
+ c.Error("algos does not contain ", algorithm)
+ }
+ }
+}
+
+func (*SuiteCommon) TestNewKnownHostsDbWithCert(c *C) {
+ if runtime.GOOS == "js" {
+ c.Skip("not available in wasm")
+ }
+
+ var mock = mockKnownHostsWithCert{}
+
+ f, err := util.TempFile(osfs.Default, "", "known-hosts")
+ c.Assert(err, IsNil)
+
+ _, err = f.Write(mock.knownHosts())
+ c.Assert(err, IsNil)
+
+ err = f.Close()
+ c.Assert(err, IsNil)
+
+ defer util.RemoveAll(osfs.Default, f.Name())
+
+ f, err = osfs.Default.Open(f.Name())
+ c.Assert(err, IsNil)
+
+ defer f.Close()
+
+ db, err := newKnownHostsDb(f.Name())
+ c.Assert(err, IsNil)
+
+ algos := db.HostKeyAlgorithms(mock.String())
+ c.Assert(algos, HasLen, len(mock.Algorithms()))
+
+ contains := func(container []string, value string) bool {
+ for _, inner := range container {
+ if inner == value {
+ return true
+ }
+ }
+ return false
+ }
+
+ for _, algorithm := range mock.Algorithms() {
+ if !contains(algos, algorithm) {
+ c.Error("algos does not contain ", algorithm)
+ }
+ }
+}
diff --git a/plumbing/transport/ssh/common.go b/plumbing/transport/ssh/common.go
index 05dea44..a37024f 100644
--- a/plumbing/transport/ssh/common.go
+++ b/plumbing/transport/ssh/common.go
@@ -11,7 +11,6 @@ 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"
@@ -127,17 +126,25 @@ func (c *command) connect() error {
}
hostWithPort := c.getHostWithPort()
if config.HostKeyCallback == nil {
- kh, err := newKnownHosts()
+ db, err := newKnownHostsDb()
if err != nil {
return err
}
- config.HostKeyCallback = kh.HostKeyCallback()
- config.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
+
+ config.HostKeyCallback = db.HostKeyCallback()
+ config.HostKeyAlgorithms = db.HostKeyAlgorithms(hostWithPort)
} else if len(config.HostKeyAlgorithms) == 0 {
// 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)
+ db, err := newKnownHostsDb()
+ if err != nil {
+ return err
+ }
+
+ // Note that the knownhost database is used, as it provides additional functionality
+ // to handle ssh cert-authorities.
+ config.HostKeyAlgorithms = db.HostKeyAlgorithms(hostWithPort)
}
overrideConfig(c.config, config)