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..d4001fa6f 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,374 @@ 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 + }{ + {keyType: ssh.RSA_4096}, + {keyType: ssh.ECDSA_P256}, + {keyType: ssh.ECDSA_P384}, + {keyType: ssh.ECDSA_P521}, + {keyType: ssh.ED25519}, + } + + 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(string(tt.keyType), 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. + 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) + 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 + }) + } + + // lastly, ensure that if the authentication was not + // successful, an error is returned. + kp, err := ssh.GenerateKeyPair(ssh.RSA_4096) + 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).To(HaveOccurred()) +} + +// 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/flag.go b/pkg/git/libgit2/managed/flag.go index 2905c7719..7116ceb79 100644 --- a/pkg/git/libgit2/managed/flag.go +++ b/pkg/git/libgit2/managed/flag.go @@ -21,14 +21,26 @@ import ( "strings" ) +var managedTransport bool + // Enabled defines whether the use of Managed Transport should be enabled. // This is only affects git operations that uses libgit2 implementation. // // True is returned when the environment variable `EXPERIMENTAL_GIT_TRANSPORT` // is detected with the value of `true` or `1`. func Enabled() bool { + if managedTransport { + return true + } + if v, ok := os.LookupEnv("EXPERIMENTAL_GIT_TRANSPORT"); ok { return strings.ToLower(v) == "true" || v == "1" } return false } + +// SetEnabled allows for the managed transport state to be changed on demand. +// This is mainly used to enable testing. +func SetEnabled(enabled bool) { + managedTransport = enabled +} 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..2ff3b51ff --- /dev/null +++ b/pkg/git/libgit2/managed_test.go @@ -0,0 +1,419 @@ +/* +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 + }{ + {keyType: ssh.RSA_4096}, + {keyType: ssh.ECDSA_P256}, + {keyType: ssh.ECDSA_P384}, + {keyType: ssh.ECDSA_P521}, + {keyType: ssh.ED25519}, + } + + 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() + + managed.SetEnabled(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()) + + for _, tt := range tests { + t.Run(string(tt.keyType), 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. + 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) + g.Expect(err).To(BeNil()) + g.Expect(commit).ToNot(BeNil()) + + // Confirm checkout actually happened. + d, err := os.ReadDir(tmpDir) + g.Expect(err).To(BeNil()) + g.Expect(d).To(HaveLen(2)) // .git and foo.txt + }) + } + + // lastly, ensure that if the authentication was not + // successful, an error is returned. + kp, err := ssh.GenerateKeyPair(ssh.RSA_4096) + 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).ToNot(BeNil()) +} + +// 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() + + managed.SetEnabled(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() + + managed.SetEnabled(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/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