Skip to content

Commit 6f41b60

Browse files
sueverndyakovofekshenawa
authored
fix(client): Do not assume that all non-IP hosts are loopbacks (#3085)
* Do not assume that all non-IP hosts are loopbacks * handle localhost and Docker internal hostnames --------- Co-authored-by: Nedyalko Dyakov <[email protected]> Co-authored-by: Nedyalko Dyakov <[email protected]> Co-authored-by: ofekshenawa <[email protected]> Co-authored-by: ofekshenawa <[email protected]>
1 parent f005806 commit 6f41b60

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

internal_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,38 @@ var _ = Describe("ClusterClient", func() {
383383
})
384384
})
385385
})
386+
387+
var _ = Describe("isLoopback", func() {
388+
DescribeTable("should correctly identify loopback addresses",
389+
func(host string, expected bool) {
390+
result := isLoopback(host)
391+
Expect(result).To(Equal(expected))
392+
},
393+
// IP addresses
394+
Entry("IPv4 loopback", "127.0.0.1", true),
395+
Entry("IPv6 loopback", "::1", true),
396+
Entry("IPv4 non-loopback", "192.168.1.1", false),
397+
Entry("IPv6 non-loopback", "2001:db8::1", false),
398+
399+
// Well-known loopback hostnames
400+
Entry("localhost lowercase", "localhost", true),
401+
Entry("localhost uppercase", "LOCALHOST", true),
402+
Entry("localhost mixed case", "LocalHost", true),
403+
404+
// Docker-specific loopbacks
405+
Entry("host.docker.internal", "host.docker.internal", true),
406+
Entry("HOST.DOCKER.INTERNAL", "HOST.DOCKER.INTERNAL", true),
407+
Entry("custom.docker.internal", "custom.docker.internal", true),
408+
Entry("app.docker.internal", "app.docker.internal", true),
409+
410+
// Non-loopback hostnames
411+
Entry("redis hostname", "redis-cluster", false),
412+
Entry("FQDN", "redis.example.com", false),
413+
Entry("docker but not internal", "redis.docker.com", false),
414+
415+
// Edge cases
416+
Entry("empty string", "", false),
417+
Entry("invalid IP", "256.256.256.256", false),
418+
Entry("partial docker internal", "docker.internal", false),
419+
)
420+
})

osscluster.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -781,12 +781,25 @@ func replaceLoopbackHost(nodeAddr, originHost string) string {
781781
return net.JoinHostPort(originHost, nodePort)
782782
}
783783

784+
// isLoopback returns true if the host is a loopback address.
785+
// For IP addresses, it uses net.IP.IsLoopback().
786+
// For hostnames, it recognizes well-known loopback hostnames like "localhost"
787+
// and Docker-specific loopback patterns like "*.docker.internal".
784788
func isLoopback(host string) bool {
785789
ip := net.ParseIP(host)
786-
if ip == nil {
790+
if ip != nil {
791+
return ip.IsLoopback()
792+
}
793+
794+
if strings.ToLower(host) == "localhost" {
787795
return true
788796
}
789-
return ip.IsLoopback()
797+
798+
if strings.HasSuffix(strings.ToLower(host), ".docker.internal") {
799+
return true
800+
}
801+
802+
return false
790803
}
791804

792805
func (c *clusterState) slotMasterNode(slot int) (*clusterNode, error) {

0 commit comments

Comments
 (0)