diff --git a/cmd/k8s-nameserver/main.go b/cmd/k8s-nameserver/main.go index 53c0fee39..ca4b44935 100644 --- a/cmd/k8s-nameserver/main.go +++ b/cmd/k8s-nameserver/main.go @@ -153,11 +153,36 @@ func (n *nameserver) handleFunc() func(w dns.ResponseWriter, r *dns.Msg) { m.Answer = append(m.Answer, rr) } case dns.TypeAAAA: - // TODO (irbekrm): implement IPv6 support. - // Kubernetes distributions that I am most familiar with - // default to IPv4 for Pod CIDR ranges and often many cases don't - // support IPv6 at all, so this should not be crucial for now. - fallthrough + // TODO (irbekrm): add IPv6 support. + // The nameserver currently does not support IPv6 + // (records are not being created for IPv6 Pod addresses). + // However, we can expect that some callers will + // nevertheless send AAAA queries. + // We have to return NOERROR if a query is received for + // an AAAA record for a DNS name that we have an A + // record for- else the caller might not follow with an + // A record query. + // https://github.com/tailscale/tailscale/issues/12321 + // https://datatracker.ietf.org/doc/html/rfc4074 + q := r.Question[0].Name + fqdn, err := dnsname.ToFQDN(q) + if err != nil { + m = r.SetRcodeFormatError(r) + return + } + // The only supported use of this nameserver is as a + // single source of truth for MagicDNS names by + // non-tailnet Kubernetes workloads. + m.Authoritative = true + ips := n.lookupIP4(fqdn) + if len(ips) == 0 { + // As we are the authoritative nameserver for MagicDNS + // names, if we do not have a record for this MagicDNS + // name, it does not exist. + m = m.SetRcode(r, dns.RcodeNameError) + return + } + m.SetRcode(r, dns.RcodeSuccess) default: log.Printf("[unexpected] nameserver received a query for an unsupported record type: %s", r.Question[0].String()) m.SetRcode(r, dns.RcodeNotImplemented) diff --git a/cmd/k8s-nameserver/main_test.go b/cmd/k8s-nameserver/main_test.go index 2c0367e6e..d9a33c4fa 100644 --- a/cmd/k8s-nameserver/main_test.go +++ b/cmd/k8s-nameserver/main_test.go @@ -79,7 +79,7 @@ func TestNameserver(t *testing.T) { }}, }, { - name: "AAAA record query", + name: "AAAA record query, A record exists", ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, query: &dns.Msg{ Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}}, @@ -88,26 +88,28 @@ func TestNameserver(t *testing.T) { wantResp: &dns.Msg{ Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}}, MsgHdr: dns.MsgHdr{ - Id: 1, - Rcode: dns.RcodeNotImplemented, - Response: true, - Opcode: dns.OpcodeQuery, + Id: 1, + Rcode: dns.RcodeSuccess, + Response: true, + Opcode: dns.OpcodeQuery, + Authoritative: true, }}, }, { - name: "AAAA record query", + name: "AAAA record query, A record does not exist", ip4: map[dnsname.FQDN][]net.IP{dnsname.FQDN("foo.bar.com."): {{1, 2, 3, 4}}}, query: &dns.Msg{ - Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}}, + Question: []dns.Question{{Name: "baz.bar.com", Qtype: dns.TypeAAAA}}, MsgHdr: dns.MsgHdr{Id: 1}, }, wantResp: &dns.Msg{ - Question: []dns.Question{{Name: "foo.bar.com", Qtype: dns.TypeAAAA}}, + Question: []dns.Question{{Name: "baz.bar.com", Qtype: dns.TypeAAAA}}, MsgHdr: dns.MsgHdr{ - Id: 1, - Rcode: dns.RcodeNotImplemented, - Response: true, - Opcode: dns.OpcodeQuery, + Id: 1, + Rcode: dns.RcodeNameError, + Response: true, + Opcode: dns.OpcodeQuery, + Authoritative: true, }}, }, {