From df76f611f3ea464230cc617a2019e5f881340a09 Mon Sep 17 00:00:00 2001 From: Waldemar Faist Date: Wed, 14 Jul 2021 17:54:06 +0200 Subject: [PATCH 1/6] feat!: allow root zone queries and fall through --- .gitignore | 1 + apex.go | 28 ---------------------------- gateway.go | 52 +++++++++++++++++++++++++++++----------------------- 3 files changed, 30 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index c440e43..ba11a1a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ coredns tilt_modules .helm .vscode +.idea \ No newline at end of file diff --git a/apex.go b/apex.go index d18509c..a19532f 100644 --- a/apex.go +++ b/apex.go @@ -7,34 +7,6 @@ import ( "github.com/miekg/dns" ) -// serveApex serves request that hit the zone' apex. A reply is written back to the client. -func (gw *Gateway) serveApex(state request.Request) (int, error) { - m := new(dns.Msg) - m.SetReply(state.Req) - switch state.QType() { - case dns.TypeSOA: - // Force to true to fix broken behaviour of legacy glibc `getaddrinfo`. - // See https://github.com/coredns/coredns/pull/3573 - m.Authoritative = true - m.Answer = []dns.RR{gw.soa(state)} - case dns.TypeNS: - m.Answer = gw.nameservers(state) - - addr := gw.ExternalAddrFunc(state) - for _, rr := range addr { - rr.Header().Ttl = gw.ttlSOA - m.Extra = append(m.Extra, rr) - } - default: - m.Ns = []dns.RR{gw.soa(state)} - } - - if err := state.W.WriteMsg(m); err != nil { - log.Errorf("Failed to send a response: %s", err) - } - return 0, nil -} - // serveSubApex serves requests that hit the zones fake 'dns' subdomain where our nameservers live. func (gw *Gateway) serveSubApex(state request.Request) (int, error) { base, _ := dnsutil.TrimZone(state.Name(), state.Zone) diff --git a/gateway.go b/gateway.go index ccde7de..9a1c4b5 100644 --- a/gateway.go +++ b/gateway.go @@ -118,10 +118,11 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return dns.RcodeServerFailure, plugin.Error(thisPlugin, fmt.Errorf("Could not sync required resources")) } + var is_root_zone_query bool for _, z := range gw.Zones { if state.Name() == z { // apex query - ret, err := gw.serveApex(state) - return ret, err + is_root_zone_query = true + break } if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { // dns subdomain test for ns. and dns. queries @@ -142,39 +143,44 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms } log.Debugf("Computed response addresses %v", addrs) - m := new(dns.Msg) - m.SetReply(state.Req) - - // If there's no match, fall through or return NXDOMAIN + // Fall through if no host matches if len(addrs) == 0 { if gw.Fall.Through(qname) { return plugin.NextOrFailure(gw.Name(), gw.Next, ctx, w, r) } - - m.Rcode = dns.RcodeNameError - m.Ns = []dns.RR{gw.soa(state)} - if err := w.WriteMsg(m); err != nil { - log.Errorf("Failed to send a response: %s", err) - } - return 0, nil } + m := new(dns.Msg) + m.SetReply(state.Req) + switch state.QType() { case dns.TypeA: - m.Answer = gw.A(state.Name(), addrs) + if len(addrs) == 0 { + // No match, return NXDOMAIN + m.Rcode = dns.RcodeNameError + m.Ns = []dns.RR{gw.soa(state)} + } else { + m.Answer = gw.A(state.Name(), addrs) + // Force to true to fix broken behaviour of legacy glibc `getaddrinfo`. + // See https://github.com/coredns/coredns/pull/3573 + m.Authoritative = true + } + case dns.TypeSOA: // Force to true to fix broken behaviour of legacy glibc `getaddrinfo`. // See https://github.com/coredns/coredns/pull/3573 m.Authoritative = true - default: - m.Ns = []dns.RR{gw.soa(state)} - } - - // If there's no match, fall through or return the SOA - if len(m.Answer) == 0 { - if gw.Fall.Through(qname) { - return plugin.NextOrFailure(gw.Name(), gw.Next, ctx, w, r) + m.Answer = []dns.RR{gw.soa(state)} + case dns.TypeNS: + if is_root_zone_query { + m.Answer = gw.nameservers(state) + + addr := gw.ExternalAddrFunc(state) + for _, rr := range addr { + rr.Header().Ttl = gw.ttlSOA + m.Extra = append(m.Extra, rr) + } } - + default: m.Ns = []dns.RR{gw.soa(state)} } From 4c59cda4bca5ac5d7ccccea5fc72cafd5c7c9fc8 Mon Sep 17 00:00:00 2001 From: Waldemar Faist Date: Thu, 15 Jul 2021 10:34:35 +0200 Subject: [PATCH 2/6] style: camel case --- gateway.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gateway.go b/gateway.go index 9a1c4b5..e89457f 100644 --- a/gateway.go +++ b/gateway.go @@ -118,10 +118,10 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return dns.RcodeServerFailure, plugin.Error(thisPlugin, fmt.Errorf("Could not sync required resources")) } - var is_root_zone_query bool + var isRootZoneQuery bool for _, z := range gw.Zones { if state.Name() == z { // apex query - is_root_zone_query = true + isRootZoneQuery = true break } if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { @@ -171,7 +171,7 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms m.Authoritative = true m.Answer = []dns.RR{gw.soa(state)} case dns.TypeNS: - if is_root_zone_query { + if isRootZoneQuery { m.Answer = gw.nameservers(state) addr := gw.ExternalAddrFunc(state) From 5966d8d6bfa9dcb6a1093b953dfe27e316f3ac8b Mon Sep 17 00:00:00 2001 From: networkop Date: Thu, 15 Jul 2021 22:21:28 +0100 Subject: [PATCH 3/6] fixing up tests --- apex_dual_test.go | 14 +++++++++++++- apex_test.go | 3 ++- gateway.go | 49 +++++++++++++++++++++++++++++------------------ gateway_test.go | 28 +++++++++++++-------------- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/apex_dual_test.go b/apex_dual_test.go index 95dd0ce..9e5d96d 100644 --- a/apex_dual_test.go +++ b/apex_dual_test.go @@ -2,6 +2,7 @@ package gateway import ( "context" + "net" "testing" "github.com/coredns/coredns/plugin/pkg/dnstest" @@ -11,6 +12,16 @@ import ( "github.com/miekg/dns" ) +func setupEmptyLookupFuncs() { + + if resource := lookupResource("Ingress"); resource != nil { + resource.lookup = func(_ []string) []net.IP { return []net.IP{} } + } + if resource := lookupResource("Service"); resource != nil { + resource.lookup = func(_ []string) []net.IP { return []net.IP{} } + } +} + func TestDualNS(t *testing.T) { ctrl := &KubeController{hasSynced: true} @@ -20,6 +31,7 @@ func TestDualNS(t *testing.T) { gw.Controller = ctrl gw.ExternalAddrFunc = selfDualAddressTest gw.secondNS = "dns2.kube-system" + setupEmptyLookupFuncs() ctx := context.TODO() for i, tc := range testsDualNS { @@ -49,7 +61,7 @@ var testsDualNS = []test.Case{ { Qname: "example.com.", Qtype: dns.TypeSOA, Rcode: dns.RcodeSuccess, - Answer: []dns.RR{ + Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, diff --git a/apex_test.go b/apex_test.go index bfb1e70..12eb641 100644 --- a/apex_test.go +++ b/apex_test.go @@ -19,6 +19,7 @@ func TestApex(t *testing.T) { gw.Next = test.NextHandler(dns.RcodeSuccess, nil) gw.Controller = ctrl gw.ExternalAddrFunc = selfAddressTest + setupEmptyLookupFuncs() ctx := context.TODO() for i, tc := range testsApex { @@ -48,7 +49,7 @@ var testsApex = []test.Case{ { Qname: "example.com.", Qtype: dns.TypeSOA, Rcode: dns.RcodeSuccess, - Answer: []dns.RR{ + Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, diff --git a/gateway.go b/gateway.go index e89457f..935fb1a 100644 --- a/gateway.go +++ b/gateway.go @@ -118,19 +118,6 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return dns.RcodeServerFailure, plugin.Error(thisPlugin, fmt.Errorf("Could not sync required resources")) } - var isRootZoneQuery bool - for _, z := range gw.Zones { - if state.Name() == z { // apex query - isRootZoneQuery = true - break - } - if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { - // dns subdomain test for ns. and dns. queries - ret, err := gw.serveSubApex(state) - return ret, err - } - } - var addrs []net.IP // Iterate over supported resources and lookup DNS queries @@ -144,9 +131,20 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms log.Debugf("Computed response addresses %v", addrs) // Fall through if no host matches - if len(addrs) == 0 { - if gw.Fall.Through(qname) { - return plugin.NextOrFailure(gw.Name(), gw.Next, ctx, w, r) + if len(addrs) == 0 && gw.Fall.Through(qname) { + return plugin.NextOrFailure(gw.Name(), gw.Next, ctx, w, r) + } + + var isRootZoneQuery bool + for _, z := range gw.Zones { + if state.Name() == z { // apex query + isRootZoneQuery = true + break + } + if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { + // dns subdomain test for ns. and dns. queries + ret, err := gw.serveSubApex(state) + return ret, err } } @@ -155,22 +153,32 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms switch state.QType() { case dns.TypeA: + if len(addrs) == 0 { - // No match, return NXDOMAIN - m.Rcode = dns.RcodeNameError + + if !isRootZoneQuery { + // No match, return NXDOMAIN + m.Rcode = dns.RcodeNameError + } + m.Ns = []dns.RR{gw.soa(state)} + } else { + m.Answer = gw.A(state.Name(), addrs) // Force to true to fix broken behaviour of legacy glibc `getaddrinfo`. // See https://github.com/coredns/coredns/pull/3573 m.Authoritative = true } case dns.TypeSOA: + // Force to true to fix broken behaviour of legacy glibc `getaddrinfo`. // See https://github.com/coredns/coredns/pull/3573 m.Authoritative = true - m.Answer = []dns.RR{gw.soa(state)} + m.Ns = []dns.RR{gw.soa(state)} + case dns.TypeNS: + if isRootZoneQuery { m.Answer = gw.nameservers(state) @@ -179,7 +187,10 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms rr.Header().Ttl = gw.ttlSOA m.Extra = append(m.Extra, rr) } + } else { + m.Ns = []dns.RR{gw.soa(state)} } + default: m.Ns = []dns.RR{gw.soa(state)} } diff --git a/gateway_test.go b/gateway_test.go index 7c360a0..5809196 100644 --- a/gateway_test.go +++ b/gateway_test.go @@ -65,77 +65,77 @@ func TestGateway(t *testing.T) { } var tests = []test.Case{ - // Existing Service + // Existing Service | Test 0 { Qname: "svc1.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("svc1.ns1.example.com. 60 IN A 192.0.1.1"), }, }, - // Existing Ingress + // Existing Ingress | Test 1 { Qname: "domain.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("domain.example.com. 60 IN A 192.0.0.1"), }, }, - // Ingress takes precedence over services + // Ingress takes precedence over services | Test 2 { Qname: "svc2.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("svc2.ns1.example.com. 60 IN A 192.0.0.2"), }, }, - // Non-existing Service + // Non-existing Service | Test 3 { Qname: "svcX.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeNameError, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Non-existing Ingress + // Non-existing Ingress | Test 4 { Qname: "d0main.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeNameError, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // SOA for the existing domain + // SOA for the existing domain | Test 5 { Qname: "domain.example.com.", Qtype: dns.TypeSOA, Rcode: dns.RcodeSuccess, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Service with no public addresses + // Service with no public addresses | Test 6 { Qname: "svc3.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeNameError, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Real service, wrong query type + // Real service, wrong query type | Test 7 { - Qname: "svc3.ns1.example.com.", Qtype: dns.TypeAAAA, Rcode: dns.RcodeNameError, + Qname: "svc3.ns1.example.com.", Qtype: dns.TypeCNAME, Rcode: dns.RcodeSuccess, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Ingress FQDN == zone + // Ingress FQDN == zone | Test 8 { Qname: "example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, - Ns: []dns.RR{ - test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), + Answer: []dns.RR{ + test.A("example.com. 60 IN A 192.0.0.3"), }, }, - // Existing Ingress with a mix of lower and upper case letters + // Existing Ingress with a mix of lower and upper case letters | Test 9 { Qname: "dOmAiN.eXamPLe.cOm.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("domain.example.com. 60 IN A 192.0.0.1"), }, }, - // Existing Service with a mix of lower and upper case letters + // Existing Service with a mix of lower and upper case letters | Test 10 { Qname: "svC1.Ns1.exAmplE.Com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ From 0ac3dc385b3e30732275858597b354966d8f990e Mon Sep 17 00:00:00 2001 From: networkop Date: Thu, 15 Jul 2021 22:21:28 +0100 Subject: [PATCH 4/6] fixing up tests --- apex_dual_test.go | 14 +++++++++++++- apex_test.go | 3 ++- gateway.go | 49 +++++++++++++++++++++++++++++------------------ gateway_test.go | 28 +++++++++++++-------------- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/apex_dual_test.go b/apex_dual_test.go index 95dd0ce..9e5d96d 100644 --- a/apex_dual_test.go +++ b/apex_dual_test.go @@ -2,6 +2,7 @@ package gateway import ( "context" + "net" "testing" "github.com/coredns/coredns/plugin/pkg/dnstest" @@ -11,6 +12,16 @@ import ( "github.com/miekg/dns" ) +func setupEmptyLookupFuncs() { + + if resource := lookupResource("Ingress"); resource != nil { + resource.lookup = func(_ []string) []net.IP { return []net.IP{} } + } + if resource := lookupResource("Service"); resource != nil { + resource.lookup = func(_ []string) []net.IP { return []net.IP{} } + } +} + func TestDualNS(t *testing.T) { ctrl := &KubeController{hasSynced: true} @@ -20,6 +31,7 @@ func TestDualNS(t *testing.T) { gw.Controller = ctrl gw.ExternalAddrFunc = selfDualAddressTest gw.secondNS = "dns2.kube-system" + setupEmptyLookupFuncs() ctx := context.TODO() for i, tc := range testsDualNS { @@ -49,7 +61,7 @@ var testsDualNS = []test.Case{ { Qname: "example.com.", Qtype: dns.TypeSOA, Rcode: dns.RcodeSuccess, - Answer: []dns.RR{ + Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, diff --git a/apex_test.go b/apex_test.go index bfb1e70..12eb641 100644 --- a/apex_test.go +++ b/apex_test.go @@ -19,6 +19,7 @@ func TestApex(t *testing.T) { gw.Next = test.NextHandler(dns.RcodeSuccess, nil) gw.Controller = ctrl gw.ExternalAddrFunc = selfAddressTest + setupEmptyLookupFuncs() ctx := context.TODO() for i, tc := range testsApex { @@ -48,7 +49,7 @@ var testsApex = []test.Case{ { Qname: "example.com.", Qtype: dns.TypeSOA, Rcode: dns.RcodeSuccess, - Answer: []dns.RR{ + Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, diff --git a/gateway.go b/gateway.go index e89457f..935fb1a 100644 --- a/gateway.go +++ b/gateway.go @@ -118,19 +118,6 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return dns.RcodeServerFailure, plugin.Error(thisPlugin, fmt.Errorf("Could not sync required resources")) } - var isRootZoneQuery bool - for _, z := range gw.Zones { - if state.Name() == z { // apex query - isRootZoneQuery = true - break - } - if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { - // dns subdomain test for ns. and dns. queries - ret, err := gw.serveSubApex(state) - return ret, err - } - } - var addrs []net.IP // Iterate over supported resources and lookup DNS queries @@ -144,9 +131,20 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms log.Debugf("Computed response addresses %v", addrs) // Fall through if no host matches - if len(addrs) == 0 { - if gw.Fall.Through(qname) { - return plugin.NextOrFailure(gw.Name(), gw.Next, ctx, w, r) + if len(addrs) == 0 && gw.Fall.Through(qname) { + return plugin.NextOrFailure(gw.Name(), gw.Next, ctx, w, r) + } + + var isRootZoneQuery bool + for _, z := range gw.Zones { + if state.Name() == z { // apex query + isRootZoneQuery = true + break + } + if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { + // dns subdomain test for ns. and dns. queries + ret, err := gw.serveSubApex(state) + return ret, err } } @@ -155,22 +153,32 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms switch state.QType() { case dns.TypeA: + if len(addrs) == 0 { - // No match, return NXDOMAIN - m.Rcode = dns.RcodeNameError + + if !isRootZoneQuery { + // No match, return NXDOMAIN + m.Rcode = dns.RcodeNameError + } + m.Ns = []dns.RR{gw.soa(state)} + } else { + m.Answer = gw.A(state.Name(), addrs) // Force to true to fix broken behaviour of legacy glibc `getaddrinfo`. // See https://github.com/coredns/coredns/pull/3573 m.Authoritative = true } case dns.TypeSOA: + // Force to true to fix broken behaviour of legacy glibc `getaddrinfo`. // See https://github.com/coredns/coredns/pull/3573 m.Authoritative = true - m.Answer = []dns.RR{gw.soa(state)} + m.Ns = []dns.RR{gw.soa(state)} + case dns.TypeNS: + if isRootZoneQuery { m.Answer = gw.nameservers(state) @@ -179,7 +187,10 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms rr.Header().Ttl = gw.ttlSOA m.Extra = append(m.Extra, rr) } + } else { + m.Ns = []dns.RR{gw.soa(state)} } + default: m.Ns = []dns.RR{gw.soa(state)} } diff --git a/gateway_test.go b/gateway_test.go index 7c360a0..5809196 100644 --- a/gateway_test.go +++ b/gateway_test.go @@ -65,77 +65,77 @@ func TestGateway(t *testing.T) { } var tests = []test.Case{ - // Existing Service + // Existing Service | Test 0 { Qname: "svc1.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("svc1.ns1.example.com. 60 IN A 192.0.1.1"), }, }, - // Existing Ingress + // Existing Ingress | Test 1 { Qname: "domain.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("domain.example.com. 60 IN A 192.0.0.1"), }, }, - // Ingress takes precedence over services + // Ingress takes precedence over services | Test 2 { Qname: "svc2.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("svc2.ns1.example.com. 60 IN A 192.0.0.2"), }, }, - // Non-existing Service + // Non-existing Service | Test 3 { Qname: "svcX.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeNameError, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Non-existing Ingress + // Non-existing Ingress | Test 4 { Qname: "d0main.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeNameError, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // SOA for the existing domain + // SOA for the existing domain | Test 5 { Qname: "domain.example.com.", Qtype: dns.TypeSOA, Rcode: dns.RcodeSuccess, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Service with no public addresses + // Service with no public addresses | Test 6 { Qname: "svc3.ns1.example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeNameError, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Real service, wrong query type + // Real service, wrong query type | Test 7 { - Qname: "svc3.ns1.example.com.", Qtype: dns.TypeAAAA, Rcode: dns.RcodeNameError, + Qname: "svc3.ns1.example.com.", Qtype: dns.TypeCNAME, Rcode: dns.RcodeSuccess, Ns: []dns.RR{ test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), }, }, - // Ingress FQDN == zone + // Ingress FQDN == zone | Test 8 { Qname: "example.com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, - Ns: []dns.RR{ - test.SOA("example.com. 60 IN SOA dns1.kube-system.example.com. hostmaster.example.com. 1499347823 7200 1800 86400 5"), + Answer: []dns.RR{ + test.A("example.com. 60 IN A 192.0.0.3"), }, }, - // Existing Ingress with a mix of lower and upper case letters + // Existing Ingress with a mix of lower and upper case letters | Test 9 { Qname: "dOmAiN.eXamPLe.cOm.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ test.A("domain.example.com. 60 IN A 192.0.0.1"), }, }, - // Existing Service with a mix of lower and upper case letters + // Existing Service with a mix of lower and upper case letters | Test 10 { Qname: "svC1.Ns1.exAmplE.Com.", Qtype: dns.TypeA, Rcode: dns.RcodeSuccess, Answer: []dns.RR{ From 77867eeef082241095bf6eeb14a58c5a2f94c8cd Mon Sep 17 00:00:00 2001 From: Waldemar Faist Date: Mon, 19 Jul 2021 18:53:04 +0200 Subject: [PATCH 5/6] test: added for fallthrough --- gateway_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/gateway_test.go b/gateway_test.go index 5809196..f389ed7 100644 --- a/gateway_test.go +++ b/gateway_test.go @@ -2,6 +2,8 @@ package gateway import ( "context" + "errors" + "github.com/coredns/coredns/plugin/pkg/fall" "net" "strings" "testing" @@ -12,6 +14,16 @@ import ( "github.com/miekg/dns" ) +type FallthroughCase struct { + test.Case + FallthroughZones []string + FallthroughExpected bool +} + +type Fallen struct { + error +} + func TestLookup(t *testing.T) { real := []string{"Ingress", "Service"} fake := []string{"Gateway", "Pod"} @@ -64,6 +76,32 @@ func TestGateway(t *testing.T) { } } +func TestGatewayFallthrough(t *testing.T) { + + ctrl := &KubeController{hasSynced: true} + gw := newGateway() + gw.Zones = []string{"example.com."} + gw.Next = test.NextHandler(dns.RcodeSuccess, Fallen{}) + gw.Controller = ctrl + setupTestLookupFuncs() + + ctx := context.TODO() + for i, tc := range testsFallthrough { + r := tc.Msg() + w := dnstest.NewRecorder(&test.ResponseWriter{}) + + gw.Fall = fall.F{Zones: tc.FallthroughZones} + _, err := gw.ServeDNS(ctx, w, r) + + if errors.As(err, &Fallen{}) && !tc.FallthroughExpected { + t.Fatalf("Test %d query resulted unexpectidly in a fall through instead of a response", i) + } + if err == nil && tc.FallthroughExpected { + t.Fatalf("Test %d query resulted unexpectidly in a response instead of a fall through", i) + } + } +} + var tests = []test.Case{ // Existing Service | Test 0 { @@ -144,6 +182,34 @@ var tests = []test.Case{ }, } +var testsFallthrough = []FallthroughCase{ + // Match found, fallthrough enabled | Test 0 + { + Case: test.Case{Qname: "example.com.", Qtype: dns.TypeA}, + FallthroughZones: []string{"."}, FallthroughExpected: false, + }, + // No match found, fallthrough enabled | Test 1 + { + Case: test.Case{Qname: "non-existent.example.com.", Qtype: dns.TypeA}, + FallthroughZones: []string{"."}, FallthroughExpected: true, + }, + // Match found, fallthrough for different zone | Test 2 + { + Case: test.Case{Qname: "example.com.", Qtype: dns.TypeA}, + FallthroughZones: []string{"not-example.com."}, FallthroughExpected: false, + }, + // No match found, fallthrough for different zone | Test 3 + { + Case: test.Case{Qname: "non-existent.example.com.", Qtype: dns.TypeA}, + FallthroughZones: []string{"not-example.com."}, FallthroughExpected: false, + }, + // No fallthrough on gw apex | Test 4 + { + Case: test.Case{Qname: "dns1.kube-system.example.com.", Qtype: dns.TypeA}, + FallthroughZones: []string{"."}, FallthroughExpected: false, + }, +} + var testServiceIndexes = map[string][]net.IP{ "svc1.ns1": {net.ParseIP("192.0.1.1")}, "svc2.ns1": {net.ParseIP("192.0.1.2")}, From f17f8cdfd3e2bbeb34f4939c7cff8e6c45f1cccd Mon Sep 17 00:00:00 2001 From: networkop Date: Fri, 23 Jul 2021 11:51:36 +0100 Subject: [PATCH 6/6] move apex lookup back to the top --- gateway.go | 26 +++++++++++++------------- gateway_test.go | 4 +++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/gateway.go b/gateway.go index 935fb1a..ec355d8 100644 --- a/gateway.go +++ b/gateway.go @@ -118,6 +118,19 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return dns.RcodeServerFailure, plugin.Error(thisPlugin, fmt.Errorf("Could not sync required resources")) } + var isRootZoneQuery bool + for _, z := range gw.Zones { + if state.Name() == z { // apex query + isRootZoneQuery = true + break + } + if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { + // dns subdomain test for ns. and dns. queries + ret, err := gw.serveSubApex(state) + return ret, err + } + } + var addrs []net.IP // Iterate over supported resources and lookup DNS queries @@ -135,19 +148,6 @@ func (gw *Gateway) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms return plugin.NextOrFailure(gw.Name(), gw.Next, ctx, w, r) } - var isRootZoneQuery bool - for _, z := range gw.Zones { - if state.Name() == z { // apex query - isRootZoneQuery = true - break - } - if dns.IsSubDomain(gw.apex+"."+z, state.Name()) { - // dns subdomain test for ns. and dns. queries - ret, err := gw.serveSubApex(state) - return ret, err - } - } - m := new(dns.Msg) m.SetReply(state.Req) diff --git a/gateway_test.go b/gateway_test.go index f389ed7..5dd5527 100644 --- a/gateway_test.go +++ b/gateway_test.go @@ -3,11 +3,12 @@ package gateway import ( "context" "errors" - "github.com/coredns/coredns/plugin/pkg/fall" "net" "strings" "testing" + "github.com/coredns/coredns/plugin/pkg/fall" + "github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/plugin/test" @@ -82,6 +83,7 @@ func TestGatewayFallthrough(t *testing.T) { gw := newGateway() gw.Zones = []string{"example.com."} gw.Next = test.NextHandler(dns.RcodeSuccess, Fallen{}) + gw.ExternalAddrFunc = selfAddressTest gw.Controller = ctrl setupTestLookupFuncs()