From cc5ea0afa850a408106eda0b6e40932e38b4a009 Mon Sep 17 00:00:00 2001 From: Mike Beaumont Date: Mon, 22 Jan 2024 15:29:51 +0100 Subject: [PATCH] fix(gateway): fix subdomain matching Signed-off-by: Mike Beaumont --- .../runtime/gateway/hostname_match_test.go | 29 ------------ .../gateway/match/hostname_match_test.go | 33 +++++++++++++ .../runtime/gateway/match/hostnames.go | 47 +++++++++---------- 3 files changed, 55 insertions(+), 54 deletions(-) delete mode 100644 pkg/plugins/runtime/gateway/hostname_match_test.go create mode 100644 pkg/plugins/runtime/gateway/match/hostname_match_test.go diff --git a/pkg/plugins/runtime/gateway/hostname_match_test.go b/pkg/plugins/runtime/gateway/hostname_match_test.go deleted file mode 100644 index 7a705cbfade3..000000000000 --- a/pkg/plugins/runtime/gateway/hostname_match_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package gateway_test - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/kumahq/kuma/pkg/plugins/runtime/gateway/match" -) - -var _ = Describe("Hostname matching", func() { - DescribeTable("should match", - func(hostname string, candidate string) { - Expect(match.Hostnames(hostname, candidate)).To(BeTrue()) - }, - Entry("", "foo.example.com", "foo.example.com"), - Entry("", "*.example.com", "foo.example.com"), - Entry("", "foo.example.com", "*.example.com"), - Entry("", "*.example.com", "*.example.com"), - ) - DescribeTable("should not match", - func(hostname string, candidate string) { - Expect(match.Hostnames(hostname, candidate)).To(BeFalse()) - }, - Entry("", "foo.example.com", "bar.example.com"), - Entry("", "*.example.com", "foo.examples.com"), - Entry("", "foo.example.com", "*.examples.com"), - Entry("", "*.example.com", "*.examples.com"), - ) -}) diff --git a/pkg/plugins/runtime/gateway/match/hostname_match_test.go b/pkg/plugins/runtime/gateway/match/hostname_match_test.go new file mode 100644 index 000000000000..d4724890603f --- /dev/null +++ b/pkg/plugins/runtime/gateway/match/hostname_match_test.go @@ -0,0 +1,33 @@ +package match_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/kumahq/kuma/pkg/plugins/runtime/gateway/match" +) + +var _ = Describe("Hostname matching", func() { + DescribeTable("should match", + func(hostname string, candidate string) { + Expect(match.Hostnames(hostname, candidate)).To(BeTrue()) + }, + Entry("equal exact", "foo.example.com", "foo.example.com"), + Entry("wild matches exact", "*.example.com", "foo.example.com"), + Entry("exact matches wild", "foo.example.com", "*.example.com"), + Entry("wild exact", "*.example.com", "*.example.com"), + Entry("larger wild matches smaller wild", "*.com", "*.examples.com"), + Entry("smaller wild matches larger wild", "*.examples.com", "*.com"), + Entry("larger exact matches smaller wild", "foo.example.com", "*.com"), + Entry("smaller wild matches larger exact", "foo.examples.com", "*.com"), + ) + DescribeTable("should not match", + func(hostname string, candidate string) { + Expect(match.Hostnames(hostname, candidate)).To(BeFalse()) + }, + Entry("exact unequal", "foo.example.com", "bar.example.com"), + Entry("first wild with rest unequal", "*.example.com", "foo.examples.com"), + Entry("second wild with rest unequal", "foo.example.com", "*.examples.com"), + Entry("both wild with rest unequal", "*.example.com", "*.examples.com"), + ) +}) diff --git a/pkg/plugins/runtime/gateway/match/hostnames.go b/pkg/plugins/runtime/gateway/match/hostnames.go index 7457f335bba3..9a2ce1d73198 100644 --- a/pkg/plugins/runtime/gateway/match/hostnames.go +++ b/pkg/plugins/runtime/gateway/match/hostnames.go @@ -1,10 +1,12 @@ package match -import "strings" +import ( + "strings" +) type hostname struct { - Host string - Domain string + Host string + DomainParts []string } func (h *hostname) wildcard() bool { @@ -12,37 +14,35 @@ func (h *hostname) wildcard() bool { } func (h *hostname) matches(name string) bool { - if name == "*" { - return true - } n := makeHostname(name) + return h.contains(n) || n.contains(*h) +} + +func (h *hostname) contains(n hostname) bool { + if len(h.DomainParts) > len(n.DomainParts) { + return false + } - if h.wildcard() || n.wildcard() { - return h.Domain == n.Domain + for i := 1; i <= len(h.DomainParts); i++ { + hInd := len(h.DomainParts) - i + nInd := len(n.DomainParts) - i + if n.DomainParts[nInd] != h.DomainParts[hInd] { + return false + } } - return h.Host == n.Host && h.Domain == n.Domain + return h.wildcard() || h.Host == n.Host } func makeHostname(name string) hostname { - parts := strings.SplitN(name, ".", 2) - return hostname{Host: parts[0], Domain: parts[1]} + parts := strings.Split(name, ".") + return hostname{Host: parts[0], DomainParts: parts[1:]} } func Contains(target string, test string) bool { - if target == "*" { - return true - } - if test == "*" { - return false - } targetHost := makeHostname(target) testHost := makeHostname(test) - // TODO domain has to be less - if (targetHost.wildcard() || targetHost.Host == testHost.Host) && targetHost.Domain == testHost.Domain { - return true - } - return false + return targetHost.contains(testHost) } // Hostnames returns true if target is a host or domain name match for @@ -54,9 +54,6 @@ func Contains(target string, test string) bool { // 1. They are exactly equal, OR // 2. One of them is a domain wildcard and the domain part matches. func Hostnames(target string, matches ...string) bool { - if target == "*" { - return true - } targetHost := makeHostname(target) for _, m := range matches {