Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 768fda7

Browse filesBrowse files
authored
Merge pull request #1482 from Javier-varez/add-cert-auth-support-v5.x
plumbing: add cert auth support to `releases/v5.x`
2 parents 863c621 + ba9d693 commit 768fda7
Copy full SHA for 768fda7

File tree

Expand file treeCollapse file tree

4 files changed

+151
-32
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+151
-32
lines changed

‎plumbing/transport/ssh/auth_method.go

Copy file name to clipboardExpand all lines: plumbing/transport/ssh/auth_method.go
+32-22Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func (a *KeyboardInteractive) String() string {
5454
}
5555

5656
func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
57-
return a.SetHostKeyCallback(&ssh.ClientConfig{
57+
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
5858
User: a.User,
5959
Auth: []ssh.AuthMethod{
6060
a.Challenge,
@@ -78,7 +78,7 @@ func (a *Password) String() string {
7878
}
7979

8080
func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
81-
return a.SetHostKeyCallback(&ssh.ClientConfig{
81+
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
8282
User: a.User,
8383
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
8484
})
@@ -101,7 +101,7 @@ func (a *PasswordCallback) String() string {
101101
}
102102

103103
func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
104-
return a.SetHostKeyCallback(&ssh.ClientConfig{
104+
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
105105
User: a.User,
106106
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
107107
})
@@ -150,7 +150,7 @@ func (a *PublicKeys) String() string {
150150
}
151151

152152
func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
153-
return a.SetHostKeyCallback(&ssh.ClientConfig{
153+
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
154154
User: a.User,
155155
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
156156
})
@@ -211,7 +211,7 @@ func (a *PublicKeysCallback) String() string {
211211
}
212212

213213
func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
214-
return a.SetHostKeyCallback(&ssh.ClientConfig{
214+
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
215215
User: a.User,
216216
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
217217
})
@@ -230,11 +230,23 @@ func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
230230
// ~/.ssh/known_hosts
231231
// /etc/ssh/ssh_known_hosts
232232
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
233-
kh, err := newKnownHosts(files...)
234-
return ssh.HostKeyCallback(kh), err
233+
kh, err := NewKnownHostsDb(files...)
234+
return kh.HostKeyCallback(), err
235235
}
236236

237-
func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
237+
// NewKnownHostsDb returns knownhosts.HostKeyDB based on a file based on a
238+
// known_hosts file. http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
239+
//
240+
// If list of files is empty, then it will be read from the SSH_KNOWN_HOSTS
241+
// environment variable, example:
242+
//
243+
// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
244+
//
245+
// If SSH_KNOWN_HOSTS is not set the following file locations will be used:
246+
//
247+
// ~/.ssh/known_hosts
248+
// /etc/ssh/ssh_known_hosts
249+
func NewKnownHostsDb(files ...string) (*knownhosts.HostKeyDB, error) {
238250
var err error
239251

240252
if len(files) == 0 {
@@ -247,7 +259,7 @@ func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
247259
return nil, err
248260
}
249261

250-
return knownhosts.New(files...)
262+
return knownhosts.NewDB(files...)
251263
}
252264

253265
func getDefaultKnownHostsFiles() ([]string, error) {
@@ -289,25 +301,23 @@ func filterKnownHostsFiles(files ...string) ([]string, error) {
289301
}
290302

291303
// HostKeyCallbackHelper is a helper that provides common functionality to
292-
// configure HostKeyCallback into a ssh.ClientConfig.
304+
// configure HostKeyCallback and HostKeyAlgorithms into a ssh.ClientConfig.
293305
type HostKeyCallbackHelper struct {
294306
// HostKeyCallback is the function type used for verifying server keys.
295-
// If nil default callback will be create using NewKnownHostsCallback
307+
// If nil, a default callback will be created using NewKnownHostsDb
296308
// without argument.
297309
HostKeyCallback ssh.HostKeyCallback
298-
}
299310

300-
// SetHostKeyCallback sets the field HostKeyCallback in the given cfg. If
301-
// HostKeyCallback is empty a default callback is created using
302-
// NewKnownHostsCallback.
303-
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
304-
var err error
305-
if m.HostKeyCallback == nil {
306-
if m.HostKeyCallback, err = NewKnownHostsCallback(); err != nil {
307-
return cfg, err
308-
}
309-
}
311+
// HostKeyAlgorithms is a list of supported host key algorithms that will
312+
// be used for host key verification.
313+
HostKeyAlgorithms []string
314+
}
310315

316+
// SetHostKeyCallbackAndAlgorithms sets the field HostKeyCallback and HostKeyAlgorithms in the given cfg.
317+
// If the host key callback or algorithms is empty it is left empty. It will be handled by the dial method,
318+
// falling back to knownhosts.
319+
func (m *HostKeyCallbackHelper) SetHostKeyCallbackAndAlgorithms(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
311320
cfg.HostKeyCallback = m.HostKeyCallback
321+
cfg.HostKeyAlgorithms = m.HostKeyAlgorithms
312322
return cfg, nil
313323
}

‎plumbing/transport/ssh/auth_method_test.go

Copy file name to clipboardExpand all lines: plumbing/transport/ssh/auth_method_test.go
+88-1Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"os"
77
"runtime"
8+
"slices"
89
"strings"
910

1011
"github.com/go-git/go-billy/v5/osfs"
@@ -18,7 +19,8 @@ import (
1819
type (
1920
SuiteCommon struct{}
2021

21-
mockKnownHosts struct{}
22+
mockKnownHosts struct{}
23+
mockKnownHostsWithCert struct{}
2224
)
2325

2426
func (mockKnownHosts) host() string { return "github.com" }
@@ -27,6 +29,19 @@ func (mockKnownHosts) knownHosts() []byte {
2729
}
2830
func (mockKnownHosts) Network() string { return "tcp" }
2931
func (mockKnownHosts) String() string { return "github.com:22" }
32+
func (mockKnownHosts) Algorithms() []string {
33+
return []string{ssh.KeyAlgoRSA, ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512}
34+
}
35+
36+
func (mockKnownHostsWithCert) host() string { return "github.com" }
37+
func (mockKnownHostsWithCert) knownHosts() []byte {
38+
return []byte(`@cert-authority github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==`)
39+
}
40+
func (mockKnownHostsWithCert) Network() string { return "tcp" }
41+
func (mockKnownHostsWithCert) String() string { return "github.com:22" }
42+
func (mockKnownHostsWithCert) Algorithms() []string {
43+
return []string{ssh.CertAlgoRSASHA512v01, ssh.CertAlgoRSASHA256v01, ssh.CertAlgoRSAv01}
44+
}
3045

3146
var _ = Suite(&SuiteCommon{})
3247

@@ -230,3 +245,75 @@ func (*SuiteCommon) TestNewKnownHostsCallback(c *C) {
230245
err = clb(mock.String(), mock, hostKey)
231246
c.Assert(err, IsNil)
232247
}
248+
249+
func (*SuiteCommon) TestNewKnownHostsDbWithoutCert(c *C) {
250+
if runtime.GOOS == "js" {
251+
c.Skip("not available in wasm")
252+
}
253+
254+
var mock = mockKnownHosts{}
255+
256+
f, err := util.TempFile(osfs.Default, "", "known-hosts")
257+
c.Assert(err, IsNil)
258+
259+
_, err = f.Write(mock.knownHosts())
260+
c.Assert(err, IsNil)
261+
262+
err = f.Close()
263+
c.Assert(err, IsNil)
264+
265+
defer util.RemoveAll(osfs.Default, f.Name())
266+
267+
f, err = osfs.Default.Open(f.Name())
268+
c.Assert(err, IsNil)
269+
270+
defer f.Close()
271+
272+
db, err := NewKnownHostsDb(f.Name())
273+
c.Assert(err, IsNil)
274+
275+
algos := db.HostKeyAlgorithms(mock.String())
276+
c.Assert(algos, HasLen, len(mock.Algorithms()))
277+
278+
for _, algorithm := range mock.Algorithms() {
279+
if !slices.Contains(algos, algorithm) {
280+
c.Error("algos does not contain ", algorithm)
281+
}
282+
}
283+
}
284+
285+
func (*SuiteCommon) TestNewKnownHostsDbWithCert(c *C) {
286+
if runtime.GOOS == "js" {
287+
c.Skip("not available in wasm")
288+
}
289+
290+
var mock = mockKnownHostsWithCert{}
291+
292+
f, err := util.TempFile(osfs.Default, "", "known-hosts")
293+
c.Assert(err, IsNil)
294+
295+
_, err = f.Write(mock.knownHosts())
296+
c.Assert(err, IsNil)
297+
298+
err = f.Close()
299+
c.Assert(err, IsNil)
300+
301+
defer util.RemoveAll(osfs.Default, f.Name())
302+
303+
f, err = osfs.Default.Open(f.Name())
304+
c.Assert(err, IsNil)
305+
306+
defer f.Close()
307+
308+
db, err := NewKnownHostsDb(f.Name())
309+
c.Assert(err, IsNil)
310+
311+
algos := db.HostKeyAlgorithms(mock.String())
312+
c.Assert(algos, HasLen, len(mock.Algorithms()))
313+
314+
for _, algorithm := range mock.Algorithms() {
315+
if !slices.Contains(algos, algorithm) {
316+
c.Error("algos does not contain ", algorithm)
317+
}
318+
}
319+
}

‎plumbing/transport/ssh/common.go

Copy file name to clipboardExpand all lines: plumbing/transport/ssh/common.go
+8-9Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
"github.com/go-git/go-git/v5/plumbing/transport"
1313
"github.com/go-git/go-git/v5/plumbing/transport/internal/common"
14-
"github.com/skeema/knownhosts"
1514

1615
"github.com/kevinburke/ssh_config"
1716
"golang.org/x/crypto/ssh"
@@ -127,17 +126,17 @@ func (c *command) connect() error {
127126
}
128127
hostWithPort := c.getHostWithPort()
129128
if config.HostKeyCallback == nil {
130-
kh, err := newKnownHosts()
129+
db, err := NewKnownHostsDb()
131130
if err != nil {
132131
return err
133132
}
134-
config.HostKeyCallback = kh.HostKeyCallback()
135-
config.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
136-
} else if len(config.HostKeyAlgorithms) == 0 {
137-
// Set the HostKeyAlgorithms based on HostKeyCallback.
138-
// For background see https://github.com/go-git/go-git/issues/411 as well as
139-
// https://github.com/golang/go/issues/29286 for root cause.
140-
config.HostKeyAlgorithms = knownhosts.HostKeyAlgorithms(config.HostKeyCallback, hostWithPort)
133+
config.HostKeyCallback = db.HostKeyCallback()
134+
config.HostKeyAlgorithms = db.HostKeyAlgorithms(hostWithPort)
135+
} else {
136+
// If the user gave a custom HostKeyCallback, we do not try to detect host key algorithms
137+
// based on knownhosts functionality, as the user may be requesting a FixedKey or using a
138+
// different key approval strategy. In that case, the user is responsible for populating
139+
// HostKeyAlgorithms appropriately
141140
}
142141

143142
overrideConfig(c.config, config)

‎plumbing/transport/ssh/common_test.go

Copy file name to clipboardExpand all lines: plumbing/transport/ssh/common_test.go
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,35 @@ func (s *SuiteCommon) TestFixedHostKeyCallback(c *C) {
129129
c.Assert(err, IsNil)
130130
c.Assert(auth, NotNil)
131131
auth.HostKeyCallback = stdssh.FixedHostKey(hostKey.PublicKey())
132+
auth.HostKeyAlgorithms = []string{"ssh-ed25519"}
132133
ep := uploadPack.newEndpoint(c, "bar.git")
133134
ps, err := uploadPack.Client.NewUploadPackSession(ep, auth)
134135
c.Assert(err, IsNil)
135136
c.Assert(ps, NotNil)
136137
}
137138

139+
func (s *SuiteCommon) TestFixedHostKeyCallbackUnexpectedAlgorithm(c *C) {
140+
hostKey, err := stdssh.ParsePrivateKey(testdata.PEMBytes["ed25519"])
141+
c.Assert(err, IsNil)
142+
uploadPack := &UploadPackSuite{
143+
opts: []ssh.Option{
144+
ssh.HostKeyPEM(testdata.PEMBytes["rsa"]),
145+
},
146+
}
147+
uploadPack.SetUpSuite(c)
148+
// Use the default client, which does not have a host key callback
149+
uploadPack.Client = DefaultClient
150+
auth, err := NewPublicKeys("foo", testdata.PEMBytes["rsa"], "")
151+
c.Assert(err, IsNil)
152+
c.Assert(auth, NotNil)
153+
auth.HostKeyCallback = stdssh.FixedHostKey(hostKey.PublicKey())
154+
auth.HostKeyAlgorithms = []string{"ssh-ed25519"}
155+
ep := uploadPack.newEndpoint(c, "bar.git")
156+
ps, err := uploadPack.Client.NewUploadPackSession(ep, auth)
157+
c.Assert(err, NotNil)
158+
c.Assert(ps, IsNil)
159+
}
160+
138161
func (s *SuiteCommon) TestFailHostKeyCallback(c *C) {
139162
uploadPack := &UploadPackSuite{
140163
opts: []ssh.Option{

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.