Add implementation of wildcard DNS lookup for container aliases

This allows use of wildcard aliases for container names via
--network-alias command line.

This is useful for running multi-tenant web applications which expect to
expose tenant interfaces on sub-domains.

Fixes #43442

Signed-off-by: Martin Ashby <martin@ashbysoft.com>
This commit is contained in:
Martin Ashby 2022-03-31 21:49:24 +01:00
parent 9e7ed25c64
commit 465ec963c8
No known key found for this signature in database
GPG key ID: 7C2A95403085B982
2 changed files with 90 additions and 2 deletions

View file

@ -559,6 +559,80 @@ func TestServiceVIPReuse(t *testing.T) {
}
}
func TestWildcardLookup(t *testing.T) {
skip.If(t, runtime.GOOS == "windows", "test only works on linux")
c, err := New()
if err != nil {
t.Fatal(err)
}
defer c.Stop()
n, err := c.NewNetwork("bridge", "net1", "", nil)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := n.Delete(); err != nil {
t.Fatal(err)
}
}()
ep, err := n.CreateEndpoint("testep")
if err != nil {
t.Fatal(err)
}
sb, err := c.NewSandbox("c1")
if err != nil {
t.Fatal(err)
}
defer func() {
if err := sb.Delete(); err != nil {
t.Fatal(err)
}
}()
err = ep.Join(sb)
if err != nil {
t.Fatal(err)
}
// Add aliases including a wildcard for one service
n.(*network).addSvcRecords("ep1", "foo.local", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, true, "test")
n.(*network).addSvcRecords("ep1", "*.bar.local", "serviceID1", net.ParseIP("192.168.0.1"), net.IP{}, false, "test")
// exact match
ipList, _ := n.(*network).ResolveName("foo.local", types.IPv4)
if len(ipList) == 0 {
t.Fatal("There must be the VIP")
}
if len(ipList) != 1 {
t.Fatal("It must return only 1 VIP")
}
if ipList[0].String() != "192.168.0.1" {
t.Fatal("The service VIP is 192.168.0.1")
}
// wildcard match
ipList, _ = n.(*network).ResolveName("my.bar.local", types.IPv4)
if len(ipList) == 0 {
t.Fatal("There must be the VIP")
}
if len(ipList) != 1 {
t.Fatal("It must return only 1 VIP")
}
if ipList[0].String() != "192.168.0.1" {
t.Fatal("The service VIP is 192.168.0.1")
}
// no match
ipList, _ = n.(*network).ResolveName("baz.local", types.IPv4)
if len(ipList) != 0 {
t.Fatal("Invalid hostname must not resolve")
}
}
func TestIpamReleaseOnNetDriverFailures(t *testing.T) {
skip.If(t, runtime.GOOS == "windows", "test only works on linux")

View file

@ -1990,7 +1990,21 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
req = strings.TrimSuffix(req, ".")
req = strings.ToLower(req)
ipSet, ok := sr.svcMap.Get(req)
// Support wildcard matching, naïve implementation uses a loop
selectedKey := req
ok = false
var ipSet []interface{}
for _, key := range sr.svcMap.Keys() {
if key == selectedKey ||
(strings.HasPrefix(key, "*.") &&
strings.HasSuffix(req, strings.TrimPrefix(key, "*"))) {
selectedKey = key
ok = true
ipSet, _ = sr.svcMap.Get(selectedKey)
break
}
}
if ipType == types.IPv6 {
// If the name resolved to v4 address then its a valid name in
@ -2000,7 +2014,7 @@ func (n *network) ResolveName(req string, ipType int) ([]net.IP, bool) {
if ok && !n.enableIPv6 {
ipv6Miss = true
}
ipSet, ok = sr.svcIPv6Map.Get(req)
ipSet, ok = sr.svcIPv6Map.Get(selectedKey)
}
if ok && len(ipSet) > 0 {