140 lines
6.1 KiB
Go
140 lines
6.1 KiB
Go
package managed
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"testing"
|
|
|
|
git2go "github.com/libgit2/git2go/v33"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
// knownHostsFixture is known_hosts fixture in the expected
|
|
// format.
|
|
var knownHostsFixture = `github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
|
|
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
|
|
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
|
|
`
|
|
|
|
// To fetch latest knownhosts for source.developers.google.com run:
|
|
// ssh-keyscan -p 2022 source.developers.google.com
|
|
//
|
|
// Expected hash (used in the cases) can get found with:
|
|
// ssh-keyscan -p 2022 source.developers.google.com | ssh-keygen -l -f -
|
|
var knownHostsFixtureWithPort = `[source.developers.google.com]:2022 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBB5Iy4/cq/gt/fPqe3uyMy4jwv1Alc94yVPxmnwNhBzJqEV5gRPiRk5u4/JJMbbu9QUVAguBABxL7sBZa5PH/xY=`
|
|
|
|
// This is an incorrect known hosts entry, that does not aligned with
|
|
// the normalized format and therefore won't match.
|
|
var knownHostsFixtureUnormalized = `source.developers.google.com:2022 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBB5Iy4/cq/gt/fPqe3uyMy4jwv1Alc94yVPxmnwNhBzJqEV5gRPiRk5u4/JJMbbu9QUVAguBABxL7sBZa5PH/xY=`
|
|
|
|
func TestKnownHostsCallback(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
host string
|
|
expectedHost string
|
|
knownHosts []byte
|
|
hostkey git2go.HostkeyCertificate
|
|
want error
|
|
}{
|
|
{
|
|
name: "Empty",
|
|
host: "source.developers.google.com",
|
|
knownHosts: []byte(""),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("AGvEpqYNMqsRNIviwyk4J4HM0lEylomDBKOWZsBn434")},
|
|
expectedHost: "source.developers.google.com:2022",
|
|
want: fmt.Errorf("hostkey verification aborted: no known_hosts found"),
|
|
},
|
|
{
|
|
name: "Mismatch incorrect known_hosts",
|
|
host: "source.developers.google.com",
|
|
knownHosts: []byte(knownHostsFixtureUnormalized),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("AGvEpqYNMqsRNIviwyk4J4HM0lEylomDBKOWZsBn434")},
|
|
expectedHost: "source.developers.google.com:2022",
|
|
want: fmt.Errorf("no entries in known_hosts match host '[source.developers.google.com]:2022' with fingerprint 'AGvEpqYNMqsRNIviwyk4J4HM0lEylomDBKOWZsBn434'"),
|
|
},
|
|
{
|
|
name: "Match when host has port",
|
|
host: "source.developers.google.com:2022",
|
|
knownHosts: []byte(knownHostsFixtureWithPort),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("AGvEpqYNMqsRNIviwyk4J4HM0lEylomDBKOWZsBn434")},
|
|
expectedHost: "source.developers.google.com:2022",
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "Match even when host does not have port",
|
|
host: "source.developers.google.com",
|
|
knownHosts: []byte(knownHostsFixtureWithPort),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("AGvEpqYNMqsRNIviwyk4J4HM0lEylomDBKOWZsBn434")},
|
|
expectedHost: "source.developers.google.com:2022",
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "Match",
|
|
host: "github.com",
|
|
knownHosts: []byte(knownHostsFixture),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8")},
|
|
expectedHost: "github.com",
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "Match with port",
|
|
host: "github.com",
|
|
knownHosts: []byte(knownHostsFixture),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8")},
|
|
expectedHost: "github.com:22",
|
|
want: nil,
|
|
},
|
|
{
|
|
// Test case to specifically detect a regression introduced in v0.25.0
|
|
// Ref: https://github.com/fluxcd/image-automation-controller/issues/378
|
|
name: "Match regardless of order of known_hosts",
|
|
host: "github.com",
|
|
knownHosts: []byte(knownHostsFixture),
|
|
// Use ecdsa-sha2-nistp256 instead of ssh-rsa
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM")},
|
|
expectedHost: "github.com:22",
|
|
want: nil,
|
|
},
|
|
{
|
|
name: "Hostname mismatch",
|
|
host: "github.com",
|
|
knownHosts: []byte(knownHostsFixture),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8")},
|
|
expectedHost: "example.com",
|
|
want: fmt.Errorf("host mismatch: %q %q", "example.com", "github.com"),
|
|
},
|
|
{
|
|
name: "Hostkey mismatch",
|
|
host: "github.com",
|
|
knownHosts: []byte(knownHostsFixture),
|
|
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ")},
|
|
expectedHost: "github.com",
|
|
want: fmt.Errorf("no entries in known_hosts match host 'github.com' with fingerprint 'ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ'"),
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
g := NewWithT(t)
|
|
|
|
cert := &git2go.Certificate{Hostkey: tt.hostkey}
|
|
callback := KnownHostsCallback(tt.expectedHost, tt.knownHosts)
|
|
result := g.Expect(callback(cert, false, tt.host))
|
|
if tt.want == nil {
|
|
result.To(BeNil())
|
|
} else {
|
|
result.To(Equal(tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func sha256Fingerprint(in string) [32]byte {
|
|
d, err := base64.RawStdEncoding.DecodeString(in)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
var out [32]byte
|
|
copy(out[:], d)
|
|
return out
|
|
}
|