From 4e3e62923b84a3101c215b7b32f0016a2f75554c Mon Sep 17 00:00:00 2001
From: Paulo Gomes <paulo.gomes@weave.works>
Date: Fri, 6 May 2022 17:58:09 +0100
Subject: [PATCH 1/2] git: Add git.HostKeyAlgos Enables the setting of HostKey
 algorithms to be used from a client perspective. This implementation supports
 go-git and libgit2 when in ManagedTransport.

Signed-off-by: Paulo Gomes <paulo.gomes@weave.works>
---
 pkg/git/gogit/transport.go     | 4 ++++
 pkg/git/libgit2/managed/ssh.go | 3 +++
 pkg/git/options.go             | 7 ++++++-
 3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/pkg/git/gogit/transport.go b/pkg/git/gogit/transport.go
index 6be46b0cc..977e8f7fd 100644
--- a/pkg/git/gogit/transport.go
+++ b/pkg/git/gogit/transport.go
@@ -103,5 +103,9 @@ func (a *CustomPublicKeys) ClientConfig() (*gossh.ClientConfig, error) {
 	if len(git.KexAlgos) > 0 {
 		config.Config.KeyExchanges = git.KexAlgos
 	}
+	if len(git.HostKeyAlgos) > 0 {
+		config.HostKeyAlgorithms = git.HostKeyAlgos
+	}
+
 	return config, nil
 }
diff --git a/pkg/git/libgit2/managed/ssh.go b/pkg/git/libgit2/managed/ssh.go
index a36ac1660..d506ee420 100644
--- a/pkg/git/libgit2/managed/ssh.go
+++ b/pkg/git/libgit2/managed/ssh.go
@@ -421,6 +421,9 @@ func cacheKeyAndConfig(remoteAddress string, cred *git2go.Credential) (string, *
 	if len(git.KexAlgos) > 0 {
 		cfg.Config.KeyExchanges = git.KexAlgos
 	}
+	if len(git.HostKeyAlgos) > 0 {
+		cfg.HostKeyAlgorithms = git.HostKeyAlgos
+	}
 
 	return ck, cfg, nil
 }
diff --git a/pkg/git/options.go b/pkg/git/options.go
index 3d8a92611..71ecbe98f 100644
--- a/pkg/git/options.go
+++ b/pkg/git/options.go
@@ -70,9 +70,14 @@ type AuthOptions struct {
 	CAFile     []byte
 }
 
-// List of custom key exchange algorithms to be used for ssh connections.
+// KexAlgos hosts the key exchange algorithms to be used for ssh connections.
+// If empty, golang's default is used instead.
 var KexAlgos []string
 
+// HostKeyAlgos holds the HostKey algorithms that the ssh client will advertise
+// to the server. If empty, golang's default is used instead.
+var HostKeyAlgos []string
+
 // Validate the AuthOptions against the defined Transport.
 func (o AuthOptions) Validate() error {
 	switch o.Transport {

From 2b59150fbe1398003c4fb4a68fb0bd67a7c7a12b Mon Sep 17 00:00:00 2001
From: Paulo Gomes <paulo.gomes@weave.works>
Date: Mon, 9 May 2022 13:31:54 +0100
Subject: [PATCH 2/2] tests: algorithms test coverage for go-git and libgit2
 Assures support for: - Authentication Key Types   - rsa   - ecdsa P256   -
 ecdsa P384   - ecdsa P521   - ed25519 - Key Exchange Algoritms:   -
 diffie-hellman-group14-sha1   - diffie-hellman-group14-sha256   -
 curve25519-sha256   - ecdh-sha2-nistp256   - ecdh-sha2-nistp384   -
 ecdh-sha2-nistp521   - curve25519-sha256@libssh.org - HostKey Algoritms:   -
 ssh-rsa   - rsa-sha2-256   - rsa-sha2-512   - ecdsa-sha2-nistp256   -
 ecdsa-sha2-nistp384   - ecdsa-sha2-nistp521   - ssh-ed25519

Signed-off-by: Paulo Gomes <paulo.gomes@weave.works>
---
 controllers/gitrepository_controller_test.go  |   2 +-
 go.mod                                        |  14 +-
 go.sum                                        |  28 +-
 pkg/git/gogit/checkout_test.go                | 367 +++++++++++++++-
 pkg/git/libgit2/checkout_test.go              |   6 +-
 pkg/git/libgit2/managed/managed_test.go       |   2 +-
 pkg/git/libgit2/managed_test.go               | 401 ++++++++++++++++++
 pkg/git/options.go                            |   8 +-
 pkg/git/strategy/strategy_test.go             |   2 +-
 .../{libgit2 => }/testdata/git/repo/foo.txt   |   0
 10 files changed, 798 insertions(+), 32 deletions(-)
 create mode 100644 pkg/git/libgit2/managed_test.go
 rename pkg/git/{libgit2 => }/testdata/git/repo/foo.txt (100%)

diff --git a/controllers/gitrepository_controller_test.go b/controllers/gitrepository_controller_test.go
index 1ab7d4aa3..92461a039 100644
--- a/controllers/gitrepository_controller_test.go
+++ b/controllers/gitrepository_controller_test.go
@@ -478,7 +478,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
 					u, err := url.Parse(obj.Spec.URL)
 					g.Expect(err).NotTo(HaveOccurred())
 					g.Expect(u.Host).ToNot(BeEmpty())
-					knownHosts, err := ssh.ScanHostKey(u.Host, timeout)
+					knownHosts, err := ssh.ScanHostKey(u.Host, timeout, git.HostKeyAlgos)
 					g.Expect(err).NotTo(HaveOccurred())
 					secret.Data["known_hosts"] = knownHosts
 				}
diff --git a/go.mod b/go.mod
index 0f4c1e30f..b67a8db15 100644
--- a/go.mod
+++ b/go.mod
@@ -19,13 +19,14 @@ require (
 	github.com/darkowlzz/controller-check v0.0.0-20220325122359-11f5827b7981
 	github.com/docker/go-units v0.4.0
 	github.com/elazarl/goproxy v0.0.0-20220417044921-416226498f94
+	github.com/fluxcd/gitkit v0.5.0
 	github.com/fluxcd/pkg/apis/meta v0.13.0
-	github.com/fluxcd/pkg/gittestserver v0.5.2
+	github.com/fluxcd/pkg/gittestserver v0.5.3
 	github.com/fluxcd/pkg/gitutil v0.1.0
 	github.com/fluxcd/pkg/helmtestserver v0.7.2
 	github.com/fluxcd/pkg/lockedfile v0.1.0
 	github.com/fluxcd/pkg/runtime v0.14.2
-	github.com/fluxcd/pkg/ssh v0.3.2
+	github.com/fluxcd/pkg/ssh v0.3.3
 	github.com/fluxcd/pkg/testserver v0.2.0
 	github.com/fluxcd/pkg/untar v0.1.0
 	github.com/fluxcd/pkg/version v0.1.0
@@ -104,7 +105,7 @@ require (
 	github.com/docker/go-metrics v0.0.1 // indirect
 	github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
 	github.com/dustin/go-humanize v1.0.0 // indirect
-	github.com/emirpasic/gods v1.12.0 // indirect
+	github.com/emirpasic/gods v1.18.1 // indirect
 	github.com/evanphx/json-patch v5.6.0+incompatible // indirect
 	github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
 	github.com/fatih/color v1.13.0 // indirect
@@ -142,7 +143,7 @@ require (
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
-	github.com/kevinburke/ssh_config v1.1.0 // indirect
+	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/klauspost/compress v1.13.6 // indirect
 	github.com/klauspost/cpuid v1.3.1 // indirect
 	github.com/kylelemons/godebug v1.1.0 // indirect
@@ -183,7 +184,6 @@ require (
 	github.com/sergi/go-diff v1.2.0 // indirect
 	github.com/shopspring/decimal v1.2.0 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
-	github.com/sosedoff/gitkit v0.3.0 // indirect
 	github.com/spf13/cast v1.4.1 // indirect
 	github.com/spf13/cobra v1.3.0 // indirect
 	github.com/stretchr/testify v1.7.1 // indirect
@@ -200,9 +200,9 @@ require (
 	go.uber.org/atomic v1.7.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.21.0 // indirect
-	golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect
+	golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
 	golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 // indirect
-	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
+	golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
diff --git a/go.sum b/go.sum
index 889189573..7087904f4 100644
--- a/go.sum
+++ b/go.sum
@@ -320,8 +320,9 @@ github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy0
 github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
 github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -347,13 +348,15 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
 github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fluxcd/gitkit v0.5.0 h1:kNSJnWZw3W8f83U5K2nsTEHfosnZ9FU2MipfnK0XfEQ=
+github.com/fluxcd/gitkit v0.5.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihnCIqNo=
 github.com/fluxcd/pkg/apis/acl v0.0.3 h1:Lw0ZHdpnO4G7Zy9KjrzwwBmDZQuy4qEjaU/RvA6k1lc=
 github.com/fluxcd/pkg/apis/acl v0.0.3/go.mod h1:XPts6lRJ9C9fIF9xVWofmQwftvhY25n1ps7W9xw0XLU=
 github.com/fluxcd/pkg/apis/meta v0.11.0-rc.3/go.mod h1:ki5wJE4nuFOZt78q0RSYkrKwINgIBPynuswZhnTOSoI=
 github.com/fluxcd/pkg/apis/meta v0.13.0 h1:0QuNKEExSjk+Rv0I6a85p2H3xOlWhdxZRsh10waEL/c=
 github.com/fluxcd/pkg/apis/meta v0.13.0/go.mod h1:Z26X5uTU5LxAyWETGueRQY7TvdPaGfKU7Wye9bdUlho=
-github.com/fluxcd/pkg/gittestserver v0.5.2 h1:Tt2g1C2b3DB4OM7ZX9hsj6scPdpnkl0xjH85ZkNvIzA=
-github.com/fluxcd/pkg/gittestserver v0.5.2/go.mod h1:QNv2arrHGReWIev8rp3Stg1JMq+xqT/lomSFZ2KfMBI=
+github.com/fluxcd/pkg/gittestserver v0.5.3 h1:2Q2+WqEDPw4lsAzby7xu8hchqpw0WmEAfjWcvCO7CnM=
+github.com/fluxcd/pkg/gittestserver v0.5.3/go.mod h1:s1eTVI7IdS5fSjyrJmvAI5rWR3FXclfFJ1q9vXBvhc4=
 github.com/fluxcd/pkg/gitutil v0.1.0 h1:VO3kJY/CKOCO4ysDNqfdpTg04icAKBOSb3lbR5uE/IE=
 github.com/fluxcd/pkg/gitutil v0.1.0/go.mod h1:Ybz50Ck5gkcnvF0TagaMwtlRy3X3wXuiri1HVsK5id4=
 github.com/fluxcd/pkg/helmtestserver v0.7.2 h1:5BBXlZk/EJKRDWmFRj2IQPy6o+9wH7cUfYUQmrNQU0U=
@@ -363,8 +366,8 @@ github.com/fluxcd/pkg/lockedfile v0.1.0/go.mod h1:EJLan8t9MiOcgTs8+puDjbE6I/KAfH
 github.com/fluxcd/pkg/runtime v0.13.0-rc.6/go.mod h1:4oKUO19TeudXrnCRnxCfMSS7EQTYpYlgfXwlQuDJ/Eg=
 github.com/fluxcd/pkg/runtime v0.14.2 h1:ktyUjcX4pHoC8DRoBmhEP6eMHbmR6+/MYoARe4YulZY=
 github.com/fluxcd/pkg/runtime v0.14.2/go.mod h1:NZr3PRK7xX2M1bl0LdtugvQyWkOmu2NcW3NrZH6U0is=
-github.com/fluxcd/pkg/ssh v0.3.2 h1:HZlDF6Qu4yplsU4Tisv6hxsRIbIOwwr7rKus8/Q/Dn0=
-github.com/fluxcd/pkg/ssh v0.3.2/go.mod h1:OVnuv9y2WCx7AoOIid0sxqe9lLKKfDS4PMl+4ta5DIo=
+github.com/fluxcd/pkg/ssh v0.3.3 h1:/tc7W7LO1VoVUI5jB+p9ZHCA+iQaXTkaSCDZJsxcZ9k=
+github.com/fluxcd/pkg/ssh v0.3.3/go.mod h1:+bKhuv0/pJy3HZwkK54Shz68sNv1uf5aI6wtPaEHaYk=
 github.com/fluxcd/pkg/testserver v0.2.0 h1:Mj0TapmKaywI6Fi5wvt1LAZpakUHmtzWQpJNKQ0Krt4=
 github.com/fluxcd/pkg/testserver v0.2.0/go.mod h1:bgjjydkXsZTeFzjz9Cr4heGANr41uTB1Aj1Q5qzuYVk=
 github.com/fluxcd/pkg/untar v0.1.0 h1:k97V/xV5hFrAkIkVPuv5AVhyxh1ZzzAKba/lbDfGo6o=
@@ -680,8 +683,8 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALr
 github.com/karrick/godirwalk v1.15.8 h1:7+rWAZPn9zuRxaIqqT8Ohs2Q2Ac0msBqwRdxNCr2VVs=
 github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
 github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
-github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
+github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -972,8 +975,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
-github.com/sosedoff/gitkit v0.3.0 h1:TfINVRNUM+GcFa+LGhZ3RcWN86Im1M6i8qs0IsgMy90=
-github.com/sosedoff/gitkit v0.3.0/go.mod h1:V3EpGZ0nvCBhXerPsbDeqtyReNb48cwP9KtkUYTKT5I=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
@@ -1146,11 +1147,9 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@@ -1256,8 +1255,9 @@ golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5 h1:bRb386wvrE+oBNdF1d/Xh9mQrfQ4ecYhW5qJ5GvTGT4=
 golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1400,11 +1400,11 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
diff --git a/pkg/git/gogit/checkout_test.go b/pkg/git/gogit/checkout_test.go
index 019036b0b..f74aaf74e 100644
--- a/pkg/git/gogit/checkout_test.go
+++ b/pkg/git/gogit/checkout_test.go
@@ -19,11 +19,18 @@ package gogit
 import (
 	"context"
 	"errors"
+	"fmt"
+	"net/url"
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 	"time"
 
+	"github.com/fluxcd/gitkit"
+	"github.com/fluxcd/pkg/gittestserver"
+	"github.com/fluxcd/pkg/ssh"
+	"github.com/fluxcd/source-controller/pkg/git"
 	"github.com/go-git/go-billy/v5/memfs"
 	"github.com/go-git/go-billy/v5/osfs"
 	extgogit "github.com/go-git/go-git/v5"
@@ -32,8 +39,13 @@ import (
 	"github.com/go-git/go-git/v5/plumbing/object"
 	"github.com/go-git/go-git/v5/storage/filesystem"
 	. "github.com/onsi/gomega"
+
+	cryptossh "golang.org/x/crypto/ssh"
+	corev1 "k8s.io/api/core/v1"
 )
 
+const testRepositoryPath = "../testdata/git/repo"
+
 func TestCheckoutBranch_Checkout(t *testing.T) {
 	repo, path, err := initRepo(t)
 	if err != nil {
@@ -169,7 +181,7 @@ func TestCheckoutTag_Checkout(t *testing.T) {
 				return
 			}
 
-			g.Expect(err).To(BeNil())
+			g.Expect(err).ToNot(HaveOccurred())
 			g.Expect(cc.String()).To(Equal(tt.expectTag + "/" + h.String()))
 			g.Expect(filepath.Join(tmpDir, "tag")).To(BeARegularFile())
 			g.Expect(os.ReadFile(filepath.Join(tmpDir, "tag"))).To(BeEquivalentTo(tt.tag))
@@ -359,6 +371,359 @@ func TestCheckoutTagSemVer_Checkout(t *testing.T) {
 	}
 }
 
+// Test_KeyTypes assures support for the different types of keys
+// for SSH Authentication supported by Flux.
+func Test_KeyTypes(t *testing.T) {
+	tests := []struct {
+		name       string
+		keyType    ssh.KeyPairType
+		authorized bool
+		wantErr    string
+	}{
+		{name: "RSA 4096", keyType: ssh.RSA_4096, authorized: true},
+		{name: "ECDSA P256", keyType: ssh.ECDSA_P256, authorized: true},
+		{name: "ECDSA P384", keyType: ssh.ECDSA_P384, authorized: true},
+		{name: "ECDSA P521", keyType: ssh.ECDSA_P521, authorized: true},
+		{name: "ED25519", keyType: ssh.ED25519, authorized: true},
+		{name: "unauthorized key", keyType: ssh.RSA_4096, wantErr: "unable to authenticate, attempted methods [none publickey], no supported methods remain"},
+	}
+
+	serverRootDir := t.TempDir()
+	server := gittestserver.NewGitServer(serverRootDir)
+
+	// Auth needs to be called, for authentication to be enabled.
+	server.Auth("", "")
+
+	var authorizedPublicKey string
+	server.PublicKeyLookupFunc(func(content string) (*gitkit.PublicKey, error) {
+		authedKey := strings.TrimSuffix(string(authorizedPublicKey), "\n")
+		if authedKey == content {
+			return &gitkit.PublicKey{Content: content}, nil
+		}
+		return nil, fmt.Errorf("pubkey provided '%s' does not match %s", content, authedKey)
+	})
+
+	g := NewWithT(t)
+	timeout := 5 * time.Second
+
+	server.KeyDir(filepath.Join(server.Root(), "keys"))
+	g.Expect(server.ListenSSH()).To(Succeed())
+
+	go func() {
+		server.StartSSH()
+	}()
+	defer server.StopSSH()
+
+	repoPath := "test.git"
+	err := server.InitRepo(testRepositoryPath, git.DefaultBranch, repoPath)
+	g.Expect(err).NotTo(HaveOccurred())
+
+	sshURL := server.SSHAddress()
+	repoURL := sshURL + "/" + repoPath
+
+	// Fetch host key.
+	u, err := url.Parse(sshURL)
+	g.Expect(err).NotTo(HaveOccurred())
+	g.Expect(u.Host).ToNot(BeEmpty())
+
+	knownHosts, err := ssh.ScanHostKey(u.Host, timeout, git.HostKeyAlgos)
+	g.Expect(err).ToNot(HaveOccurred())
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewWithT(t)
+
+			// Generate ssh keys based on key type.
+			kp, err := ssh.GenerateKeyPair(tt.keyType)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Update authorized key to ensure only the new key is valid on the server.
+			if tt.authorized {
+				authorizedPublicKey = string(kp.PublicKey)
+			}
+
+			secret := corev1.Secret{
+				Data: map[string][]byte{
+					"identity":    kp.PrivateKey,
+					"known_hosts": knownHosts,
+				},
+			}
+
+			authOpts, err := git.AuthOptionsFromSecret(repoURL, &secret)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Prepare for checkout.
+			branchCheckoutStrat := &CheckoutBranch{Branch: git.DefaultBranch}
+			tmpDir := t.TempDir()
+
+			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+			defer cancel()
+
+			// Checkout the repo.
+			commit, err := branchCheckoutStrat.Checkout(ctx, tmpDir, repoURL, authOpts)
+
+			if tt.wantErr == "" {
+				g.Expect(err).ToNot(HaveOccurred())
+				g.Expect(commit).ToNot(BeNil())
+
+				// Confirm checkout actually happened.
+				d, err := os.ReadDir(tmpDir)
+				g.Expect(err).ToNot(HaveOccurred())
+				g.Expect(d).To(HaveLen(2)) // .git and foo.txt
+			} else {
+				g.Expect(err).To(HaveOccurred())
+				g.Expect(err.Error()).Should(ContainSubstring(tt.wantErr))
+			}
+		})
+	}
+}
+
+// Test_KeyExchangeAlgos assures support for the different
+// types of SSH key exchange algorithms supported by Flux.
+func Test_KeyExchangeAlgos(t *testing.T) {
+	tests := []struct {
+		name      string
+		ClientKex []string
+		ServerKex []string
+		wantErr   string
+	}{
+		{
+			name:      "support for kex: diffie-hellman-group14-sha1",
+			ClientKex: []string{"diffie-hellman-group14-sha1"},
+			ServerKex: []string{"diffie-hellman-group14-sha1"},
+		},
+		{
+			name:      "support for kex: diffie-hellman-group14-sha256",
+			ClientKex: []string{"diffie-hellman-group14-sha256"},
+			ServerKex: []string{"diffie-hellman-group14-sha256"},
+		},
+		{
+			name:      "support for kex: curve25519-sha256",
+			ClientKex: []string{"curve25519-sha256"},
+			ServerKex: []string{"curve25519-sha256"},
+		},
+		{
+			name:      "support for kex: ecdh-sha2-nistp256",
+			ClientKex: []string{"ecdh-sha2-nistp256"},
+			ServerKex: []string{"ecdh-sha2-nistp256"},
+		},
+		{
+			name:      "support for kex: ecdh-sha2-nistp384",
+			ClientKex: []string{"ecdh-sha2-nistp384"},
+			ServerKex: []string{"ecdh-sha2-nistp384"},
+		},
+		{
+			name:      "support for kex: ecdh-sha2-nistp521",
+			ClientKex: []string{"ecdh-sha2-nistp521"},
+			ServerKex: []string{"ecdh-sha2-nistp521"},
+		},
+		{
+			name:      "support for kex: curve25519-sha256@libssh.org",
+			ClientKex: []string{"curve25519-sha256@libssh.org"},
+			ServerKex: []string{"curve25519-sha256@libssh.org"},
+		},
+		{
+			name:      "non-matching kex",
+			ClientKex: []string{"ecdh-sha2-nistp521"},
+			ServerKex: []string{"curve25519-sha256@libssh.org"},
+			wantErr:   "ssh: no common algorithm for key exchange; client offered: [ecdh-sha2-nistp521 ext-info-c], server offered: [curve25519-sha256@libssh.org]",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewWithT(t)
+			timeout := 5 * time.Second
+
+			serverRootDir := t.TempDir()
+			server := gittestserver.NewGitServer(serverRootDir).WithSSHConfig(&cryptossh.ServerConfig{
+				Config: cryptossh.Config{
+					KeyExchanges: tt.ServerKex,
+				},
+			})
+
+			// Set what Client Key Exchange Algos to send
+			git.KexAlgos = tt.ClientKex
+
+			server.KeyDir(filepath.Join(server.Root(), "keys"))
+			g.Expect(server.ListenSSH()).To(Succeed())
+
+			go func() {
+				server.StartSSH()
+			}()
+			defer server.StopSSH()
+
+			repoPath := "test.git"
+			err := server.InitRepo(testRepositoryPath, git.DefaultBranch, repoPath)
+			g.Expect(err).NotTo(HaveOccurred())
+
+			sshURL := server.SSHAddress()
+			repoURL := sshURL + "/" + repoPath
+
+			// Fetch host key.
+			u, err := url.Parse(sshURL)
+			g.Expect(err).NotTo(HaveOccurred())
+			g.Expect(u.Host).ToNot(BeEmpty())
+
+			knownHosts, err := ssh.ScanHostKey(u.Host, timeout, git.HostKeyAlgos)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// No authentication is required for this test, but it is
+			// used here to make the Checkout logic happy.
+			kp, err := ssh.GenerateKeyPair(ssh.ED25519)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			secret := corev1.Secret{
+				Data: map[string][]byte{
+					"identity":    kp.PrivateKey,
+					"known_hosts": knownHosts,
+				},
+			}
+
+			authOpts, err := git.AuthOptionsFromSecret(repoURL, &secret)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Prepare for checkout.
+			branchCheckoutStrat := &CheckoutBranch{Branch: git.DefaultBranch}
+			tmpDir := t.TempDir()
+
+			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+			defer cancel()
+
+			// Checkout the repo.
+			_, err = branchCheckoutStrat.Checkout(ctx, tmpDir, repoURL, authOpts)
+			if tt.wantErr != "" {
+				g.Expect(err).Error().Should(HaveOccurred())
+				g.Expect(err.Error()).Should(ContainSubstring(tt.wantErr))
+			} else {
+				g.Expect(err).Error().ShouldNot(HaveOccurred())
+			}
+		})
+	}
+}
+
+// TestHostKeyAlgos assures support for the different
+// types of SSH Host Key algorithms supported by Flux.
+func TestHostKeyAlgos(t *testing.T) {
+	tests := []struct {
+		name               string
+		keyType            ssh.KeyPairType
+		ClientHostKeyAlgos []string
+	}{
+		{
+			name:               "support for hostkey: ssh-rsa",
+			keyType:            ssh.RSA_4096,
+			ClientHostKeyAlgos: []string{"ssh-rsa"},
+		},
+		{
+			name:               "support for hostkey: rsa-sha2-256",
+			keyType:            ssh.RSA_4096,
+			ClientHostKeyAlgos: []string{"rsa-sha2-256"},
+		},
+		{
+			name:               "support for hostkey: rsa-sha2-512",
+			keyType:            ssh.RSA_4096,
+			ClientHostKeyAlgos: []string{"rsa-sha2-512"},
+		},
+		{
+			name:               "support for hostkey: ecdsa-sha2-nistp256",
+			keyType:            ssh.ECDSA_P256,
+			ClientHostKeyAlgos: []string{"ecdsa-sha2-nistp256"},
+		},
+		{
+			name:               "support for hostkey: ecdsa-sha2-nistp384",
+			keyType:            ssh.ECDSA_P384,
+			ClientHostKeyAlgos: []string{"ecdsa-sha2-nistp384"},
+		},
+		{
+			name:               "support for hostkey: ecdsa-sha2-nistp521",
+			keyType:            ssh.ECDSA_P521,
+			ClientHostKeyAlgos: []string{"ecdsa-sha2-nistp521"},
+		},
+		{
+			name:               "support for hostkey: ssh-ed25519",
+			keyType:            ssh.ED25519,
+			ClientHostKeyAlgos: []string{"ssh-ed25519"},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewWithT(t)
+			timeout := 5 * time.Second
+
+			sshConfig := &cryptossh.ServerConfig{}
+
+			// Generate new keypair for the server to use for HostKeys.
+			hkp, err := ssh.GenerateKeyPair(tt.keyType)
+			g.Expect(err).NotTo(HaveOccurred())
+			p, err := cryptossh.ParseRawPrivateKey(hkp.PrivateKey)
+			g.Expect(err).NotTo(HaveOccurred())
+
+			// Add key to server.
+			signer, err := cryptossh.NewSignerFromKey(p)
+			g.Expect(err).NotTo(HaveOccurred())
+			sshConfig.AddHostKey(signer)
+
+			serverRootDir := t.TempDir()
+			server := gittestserver.NewGitServer(serverRootDir).WithSSHConfig(sshConfig)
+
+			// Set what HostKey Algos will be accepted from a client perspective.
+			git.HostKeyAlgos = tt.ClientHostKeyAlgos
+
+			keyDir := filepath.Join(server.Root(), "keys")
+			server.KeyDir(keyDir)
+			g.Expect(server.ListenSSH()).To(Succeed())
+
+			go func() {
+				server.StartSSH()
+			}()
+			defer server.StopSSH()
+
+			repoPath := "test.git"
+			err = server.InitRepo(testRepositoryPath, git.DefaultBranch, repoPath)
+			g.Expect(err).NotTo(HaveOccurred())
+
+			sshURL := server.SSHAddress()
+			repoURL := sshURL + "/" + repoPath
+
+			// Fetch host key.
+			u, err := url.Parse(sshURL)
+			g.Expect(err).NotTo(HaveOccurred())
+			g.Expect(u.Host).ToNot(BeEmpty())
+
+			knownHosts, err := ssh.ScanHostKey(u.Host, timeout, git.HostKeyAlgos)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// No authentication is required for this test, but it is
+			// used here to make the Checkout logic happy.
+			kp, err := ssh.GenerateKeyPair(ssh.ED25519)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			secret := corev1.Secret{
+				Data: map[string][]byte{
+					"identity":    kp.PrivateKey,
+					"known_hosts": knownHosts,
+				},
+			}
+
+			authOpts, err := git.AuthOptionsFromSecret(repoURL, &secret)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Prepare for checkout.
+			branchCheckoutStrat := &CheckoutBranch{Branch: git.DefaultBranch}
+			tmpDir := t.TempDir()
+
+			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+			defer cancel()
+
+			// Checkout the repo.
+			_, err = branchCheckoutStrat.Checkout(ctx, tmpDir, repoURL, authOpts)
+			g.Expect(err).Error().ShouldNot(HaveOccurred())
+		})
+	}
+}
+
 func initRepo(t *testing.T) (*extgogit.Repository, string, error) {
 	tmpDir := t.TempDir()
 	sto := filesystem.NewStorage(osfs.New(tmpDir), cache.NewObjectLRUDefault())
diff --git a/pkg/git/libgit2/checkout_test.go b/pkg/git/libgit2/checkout_test.go
index dadb58820..6b5ef5b39 100644
--- a/pkg/git/libgit2/checkout_test.go
+++ b/pkg/git/libgit2/checkout_test.go
@@ -467,7 +467,7 @@ func TestCheckout_ED25519(t *testing.T) {
 
 	repoPath := "test.git"
 
-	err = server.InitRepo("testdata/git/repo", git.DefaultBranch, repoPath)
+	err = server.InitRepo(testRepositoryPath, git.DefaultBranch, repoPath)
 	g.Expect(err).NotTo(HaveOccurred())
 
 	sshURL := server.SSHAddress()
@@ -477,7 +477,7 @@ func TestCheckout_ED25519(t *testing.T) {
 	u, err := url.Parse(sshURL)
 	g.Expect(err).NotTo(HaveOccurred())
 	g.Expect(u.Host).ToNot(BeEmpty())
-	knownHosts, err := ssh.ScanHostKey(u.Host, timeout)
+	knownHosts, err := ssh.ScanHostKey(u.Host, timeout, git.HostKeyAlgos)
 	g.Expect(err).ToNot(HaveOccurred())
 
 	kp, err := ssh.NewEd25519Generator().Generate()
@@ -504,7 +504,7 @@ func TestCheckout_ED25519(t *testing.T) {
 	// This should always fail because the generated key above isn't present in
 	// the git server.
 	_, err = branchCheckoutStrat.Checkout(ctx, tmpDir, repoURL, authOpts)
-	g.Expect(err).To(BeNil())
+	g.Expect(err).ToNot(HaveOccurred())
 }
 
 func TestSafeClone(t *testing.T) {
diff --git a/pkg/git/libgit2/managed/managed_test.go b/pkg/git/libgit2/managed/managed_test.go
index 63afb6721..7d87b9141 100644
--- a/pkg/git/libgit2/managed/managed_test.go
+++ b/pkg/git/libgit2/managed/managed_test.go
@@ -255,7 +255,7 @@ func TestManagedTransport_E2E(t *testing.T) {
 	InitManagedTransport(logr.Discard())
 
 	repoPath := "test.git"
-	err = server.InitRepo("../testdata/git/repo", git.DefaultBranch, repoPath)
+	err = server.InitRepo("../../testdata/git/repo", git.DefaultBranch, repoPath)
 	g.Expect(err).ToNot(HaveOccurred())
 
 	tmpDir := t.TempDir()
diff --git a/pkg/git/libgit2/managed_test.go b/pkg/git/libgit2/managed_test.go
new file mode 100644
index 000000000..1e923ee8f
--- /dev/null
+++ b/pkg/git/libgit2/managed_test.go
@@ -0,0 +1,401 @@
+/*
+Copyright 2022 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package libgit2
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/fluxcd/gitkit"
+	"github.com/fluxcd/pkg/gittestserver"
+	"github.com/fluxcd/pkg/ssh"
+	"github.com/fluxcd/source-controller/pkg/git"
+	"github.com/fluxcd/source-controller/pkg/git/libgit2/managed"
+
+	"github.com/go-logr/logr"
+	. "github.com/onsi/gomega"
+
+	cryptossh "golang.org/x/crypto/ssh"
+	corev1 "k8s.io/api/core/v1"
+)
+
+const testRepositoryPath = "../testdata/git/repo"
+
+// Test_ManagedSSH_KeyTypes assures support for the different
+// types of keys for SSH Authentication supported by Flux.
+func Test_ManagedSSH_KeyTypes(t *testing.T) {
+	tests := []struct {
+		name       string
+		keyType    ssh.KeyPairType
+		authorized bool
+		wantErr    string
+	}{
+		{name: "RSA 4096", keyType: ssh.RSA_4096, authorized: true},
+		{name: "ECDSA P256", keyType: ssh.ECDSA_P256, authorized: true},
+		{name: "ECDSA P384", keyType: ssh.ECDSA_P384, authorized: true},
+		{name: "ECDSA P521", keyType: ssh.ECDSA_P521, authorized: true},
+		{name: "ED25519", keyType: ssh.ED25519, authorized: true},
+		{name: "unauthorized key", keyType: ssh.RSA_4096, wantErr: "Failed to retrieve list of SSH authentication methods"},
+	}
+
+	serverRootDir := t.TempDir()
+	server := gittestserver.NewGitServer(serverRootDir)
+
+	// Auth needs to be called, for authentication to be enabled.
+	server.Auth("", "")
+
+	var authorizedPublicKey string
+	server.PublicKeyLookupFunc(func(content string) (*gitkit.PublicKey, error) {
+		authedKey := strings.TrimSuffix(string(authorizedPublicKey), "\n")
+		if authedKey == content {
+			return &gitkit.PublicKey{Content: content}, nil
+		}
+		return nil, fmt.Errorf("pubkey provided '%s' does not match %s", content, authedKey)
+	})
+
+	g := NewWithT(t)
+	timeout := 5 * time.Second
+
+	server.KeyDir(filepath.Join(server.Root(), "keys"))
+	g.Expect(server.ListenSSH()).To(Succeed())
+
+	go func() {
+		server.StartSSH()
+	}()
+	defer server.StopSSH()
+
+	repoPath := "test.git"
+	err := server.InitRepo(testRepositoryPath, git.DefaultBranch, repoPath)
+	g.Expect(err).NotTo(HaveOccurred())
+
+	sshURL := server.SSHAddress()
+	repoURL := sshURL + "/" + repoPath
+
+	// Fetch host key.
+	u, err := url.Parse(sshURL)
+	g.Expect(err).NotTo(HaveOccurred())
+	g.Expect(u.Host).ToNot(BeEmpty())
+
+	knownHosts, err := ssh.ScanHostKey(u.Host, timeout, git.HostKeyAlgos)
+	g.Expect(err).ToNot(HaveOccurred())
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewWithT(t)
+
+			// Generate ssh keys based on key type.
+			kp, err := ssh.GenerateKeyPair(tt.keyType)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Update authorized key to ensure only the new key is valid on the server.
+			if tt.authorized {
+				authorizedPublicKey = string(kp.PublicKey)
+			}
+
+			secret := corev1.Secret{
+				Data: map[string][]byte{
+					"identity":    kp.PrivateKey,
+					"known_hosts": knownHosts,
+				},
+			}
+
+			authOpts, err := git.AuthOptionsFromSecret(repoURL, &secret)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Prepare for checkout.
+			branchCheckoutStrat := &CheckoutBranch{Branch: git.DefaultBranch}
+			tmpDir := t.TempDir()
+
+			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+			defer cancel()
+
+			// Checkout the repo.
+			commit, err := branchCheckoutStrat.Checkout(ctx, tmpDir, repoURL, authOpts)
+
+			if tt.wantErr == "" {
+				g.Expect(err).ToNot(HaveOccurred())
+				g.Expect(commit).ToNot(BeNil())
+
+				// Confirm checkout actually happened.
+				d, err := os.ReadDir(tmpDir)
+				g.Expect(err).ToNot(HaveOccurred())
+				g.Expect(d).To(HaveLen(2)) // .git and foo.txt
+			} else {
+				g.Expect(err).To(HaveOccurred())
+				g.Expect(err.Error()).Should(ContainSubstring(tt.wantErr))
+			}
+		})
+	}
+}
+
+// Test_ManagedSSH_KeyExchangeAlgos assures support for the different
+// types of SSH key exchange algorithms supported by Flux.
+func Test_ManagedSSH_KeyExchangeAlgos(t *testing.T) {
+	tests := []struct {
+		name      string
+		ClientKex []string
+		ServerKex []string
+		wantErr   string
+	}{
+		{
+			name:      "support for kex: diffie-hellman-group14-sha1",
+			ClientKex: []string{"diffie-hellman-group14-sha1"},
+			ServerKex: []string{"diffie-hellman-group14-sha1"},
+		},
+		{
+			name:      "support for kex: diffie-hellman-group14-sha256",
+			ClientKex: []string{"diffie-hellman-group14-sha256"},
+			ServerKex: []string{"diffie-hellman-group14-sha256"},
+		},
+		{
+			name:      "support for kex: curve25519-sha256",
+			ClientKex: []string{"curve25519-sha256"},
+			ServerKex: []string{"curve25519-sha256"},
+		},
+		{
+			name:      "support for kex: ecdh-sha2-nistp256",
+			ClientKex: []string{"ecdh-sha2-nistp256"},
+			ServerKex: []string{"ecdh-sha2-nistp256"},
+		},
+		{
+			name:      "support for kex: ecdh-sha2-nistp384",
+			ClientKex: []string{"ecdh-sha2-nistp384"},
+			ServerKex: []string{"ecdh-sha2-nistp384"},
+		},
+		{
+			name:      "support for kex: ecdh-sha2-nistp521",
+			ClientKex: []string{"ecdh-sha2-nistp521"},
+			ServerKex: []string{"ecdh-sha2-nistp521"},
+		},
+		{
+			name:      "support for kex: curve25519-sha256@libssh.org",
+			ClientKex: []string{"curve25519-sha256@libssh.org"},
+			ServerKex: []string{"curve25519-sha256@libssh.org"},
+		},
+		{
+			name:      "non-matching kex",
+			ClientKex: []string{"ecdh-sha2-nistp521"},
+			ServerKex: []string{"curve25519-sha256@libssh.org"},
+			wantErr:   "ssh: no common algorithm for key exchange; client offered: [ecdh-sha2-nistp521 ext-info-c], server offered: [curve25519-sha256@libssh.org]",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewWithT(t)
+			timeout := 5 * time.Second
+
+			serverRootDir := t.TempDir()
+			server := gittestserver.NewGitServer(serverRootDir).WithSSHConfig(&cryptossh.ServerConfig{
+				Config: cryptossh.Config{
+					KeyExchanges: tt.ServerKex,
+				},
+			})
+
+			// Set what Client Key Exchange Algos to send
+			git.KexAlgos = tt.ClientKex
+
+			server.KeyDir(filepath.Join(server.Root(), "keys"))
+			g.Expect(server.ListenSSH()).To(Succeed())
+
+			go func() {
+				server.StartSSH()
+			}()
+			defer server.StopSSH()
+
+			os.Setenv("EXPERIMENTAL_GIT_TRANSPORT", "true")
+			managed.InitManagedTransport(logr.Discard())
+			repoPath := "test.git"
+
+			err := server.InitRepo(testRepositoryPath, git.DefaultBranch, repoPath)
+			g.Expect(err).NotTo(HaveOccurred())
+
+			sshURL := server.SSHAddress()
+			repoURL := sshURL + "/" + repoPath
+
+			// Fetch host key.
+			u, err := url.Parse(sshURL)
+			g.Expect(err).NotTo(HaveOccurred())
+			g.Expect(u.Host).ToNot(BeEmpty())
+
+			knownHosts, err := ssh.ScanHostKey(u.Host, timeout, git.HostKeyAlgos)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// No authentication is required for this test, but it is
+			// used here to make the Checkout logic happy.
+			kp, err := ssh.GenerateKeyPair(ssh.ED25519)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			secret := corev1.Secret{
+				Data: map[string][]byte{
+					"identity":    kp.PrivateKey,
+					"known_hosts": knownHosts,
+				},
+			}
+
+			authOpts, err := git.AuthOptionsFromSecret(repoURL, &secret)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Prepare for checkout.
+			branchCheckoutStrat := &CheckoutBranch{Branch: git.DefaultBranch}
+			tmpDir := t.TempDir()
+
+			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+			defer cancel()
+
+			// Checkout the repo.
+			_, err = branchCheckoutStrat.Checkout(ctx, tmpDir, repoURL, authOpts)
+			if tt.wantErr != "" {
+				g.Expect(err).Error().Should(HaveOccurred())
+				g.Expect(err.Error()).Should(ContainSubstring(tt.wantErr))
+			} else {
+				g.Expect(err).Error().ShouldNot(HaveOccurred())
+			}
+		})
+	}
+}
+
+// Test_ManagedSSH_HostKeyAlgos assures support for the different
+// types of SSH Host Key algorithms supported by Flux.
+func Test_ManagedSSH_HostKeyAlgos(t *testing.T) {
+	tests := []struct {
+		name               string
+		keyType            ssh.KeyPairType
+		ClientHostKeyAlgos []string
+	}{
+		{
+			name:               "support for hostkey: ssh-rsa",
+			keyType:            ssh.RSA_4096,
+			ClientHostKeyAlgos: []string{"ssh-rsa"},
+		},
+		{
+			name:               "support for hostkey: rsa-sha2-256",
+			keyType:            ssh.RSA_4096,
+			ClientHostKeyAlgos: []string{"rsa-sha2-256"},
+		},
+		{
+			name:               "support for hostkey: rsa-sha2-512",
+			keyType:            ssh.RSA_4096,
+			ClientHostKeyAlgos: []string{"rsa-sha2-512"},
+		},
+		{
+			name:               "support for hostkey: ecdsa-sha2-nistp256",
+			keyType:            ssh.ECDSA_P256,
+			ClientHostKeyAlgos: []string{"ecdsa-sha2-nistp256"},
+		},
+		{
+			name:               "support for hostkey: ecdsa-sha2-nistp384",
+			keyType:            ssh.ECDSA_P384,
+			ClientHostKeyAlgos: []string{"ecdsa-sha2-nistp384"},
+		},
+		{
+			name:               "support for hostkey: ecdsa-sha2-nistp521",
+			keyType:            ssh.ECDSA_P521,
+			ClientHostKeyAlgos: []string{"ecdsa-sha2-nistp521"},
+		},
+		{
+			name:               "support for hostkey: ssh-ed25519",
+			keyType:            ssh.ED25519,
+			ClientHostKeyAlgos: []string{"ssh-ed25519"},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			g := NewWithT(t)
+			timeout := 5 * time.Second
+
+			sshConfig := &cryptossh.ServerConfig{}
+
+			// Generate new keypair for the server to use for HostKeys.
+			hkp, err := ssh.GenerateKeyPair(tt.keyType)
+			g.Expect(err).NotTo(HaveOccurred())
+			p, err := cryptossh.ParseRawPrivateKey(hkp.PrivateKey)
+			g.Expect(err).NotTo(HaveOccurred())
+
+			// Add key to server.
+			signer, err := cryptossh.NewSignerFromKey(p)
+			g.Expect(err).NotTo(HaveOccurred())
+			sshConfig.AddHostKey(signer)
+
+			serverRootDir := t.TempDir()
+			server := gittestserver.NewGitServer(serverRootDir).WithSSHConfig(sshConfig)
+
+			// Set what HostKey Algos will be accepted from a client perspective.
+			git.HostKeyAlgos = tt.ClientHostKeyAlgos
+
+			keyDir := filepath.Join(server.Root(), "keys")
+			server.KeyDir(keyDir)
+			g.Expect(server.ListenSSH()).To(Succeed())
+
+			go func() {
+				server.StartSSH()
+			}()
+			defer server.StopSSH()
+
+			os.Setenv("EXPERIMENTAL_GIT_TRANSPORT", "true")
+			managed.InitManagedTransport(logr.Discard())
+			repoPath := "test.git"
+
+			err = server.InitRepo(testRepositoryPath, git.DefaultBranch, repoPath)
+			g.Expect(err).NotTo(HaveOccurred())
+
+			sshURL := server.SSHAddress()
+			repoURL := sshURL + "/" + repoPath
+
+			// Fetch host key.
+			u, err := url.Parse(sshURL)
+			g.Expect(err).NotTo(HaveOccurred())
+			g.Expect(u.Host).ToNot(BeEmpty())
+
+			knownHosts, err := ssh.ScanHostKey(u.Host, timeout, tt.ClientHostKeyAlgos)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// No authentication is required for this test, but it is
+			// used here to make the Checkout logic happy.
+			kp, err := ssh.GenerateKeyPair(ssh.ED25519)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			secret := corev1.Secret{
+				Data: map[string][]byte{
+					"identity":    kp.PrivateKey,
+					"known_hosts": knownHosts,
+				},
+			}
+
+			authOpts, err := git.AuthOptionsFromSecret(repoURL, &secret)
+			g.Expect(err).ToNot(HaveOccurred())
+
+			// Prepare for checkout.
+			branchCheckoutStrat := &CheckoutBranch{Branch: git.DefaultBranch}
+			tmpDir := t.TempDir()
+
+			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
+			defer cancel()
+
+			// Checkout the repo.
+			_, err = branchCheckoutStrat.Checkout(ctx, tmpDir, repoURL, authOpts)
+			g.Expect(err).Error().ShouldNot(HaveOccurred())
+		})
+	}
+}
diff --git a/pkg/git/options.go b/pkg/git/options.go
index 71ecbe98f..bd0b4d7b0 100644
--- a/pkg/git/options.go
+++ b/pkg/git/options.go
@@ -70,12 +70,12 @@ type AuthOptions struct {
 	CAFile     []byte
 }
 
-// KexAlgos hosts the key exchange algorithms to be used for ssh connections.
-// If empty, golang's default is used instead.
+// KexAlgos hosts the key exchange algorithms to be used for SSH connections.
+// If empty, Go's default is used instead.
 var KexAlgos []string
 
-// HostKeyAlgos holds the HostKey algorithms that the ssh client will advertise
-// to the server. If empty, golang's default is used instead.
+// HostKeyAlgos holds the HostKey algorithms that the SSH client will advertise
+// to the server. If empty, Go's default is used instead.
 var HostKeyAlgos []string
 
 // Validate the AuthOptions against the defined Transport.
diff --git a/pkg/git/strategy/strategy_test.go b/pkg/git/strategy/strategy_test.go
index 055c44f63..866aea938 100644
--- a/pkg/git/strategy/strategy_test.go
+++ b/pkg/git/strategy/strategy_test.go
@@ -97,7 +97,7 @@ func TestCheckoutStrategyForImplementation_Auth(t *testing.T) {
 				return getSSHRepoURL(srv.SSHAddress(), repoPath)
 			},
 			authOptsFunc: func(g *WithT, u *url.URL, user, pswd string, ca []byte) *git.AuthOptions {
-				knownhosts, err := ssh.ScanHostKey(u.Host, 5*time.Second)
+				knownhosts, err := ssh.ScanHostKey(u.Host, 5*time.Second, git.HostKeyAlgos)
 				g.Expect(err).ToNot(HaveOccurred())
 
 				keygen := ssh.NewRSAGenerator(2048)
diff --git a/pkg/git/libgit2/testdata/git/repo/foo.txt b/pkg/git/testdata/git/repo/foo.txt
similarity index 100%
rename from pkg/git/libgit2/testdata/git/repo/foo.txt
rename to pkg/git/testdata/git/repo/foo.txt