Configuring bind to only forward DNS to a specific zone

Posted on 2021-07-25 by ungleich

Introduction

In this article we'll show you an easy solution to host DNS zones on IPv6 only or private DNS servers. The method we use here is DNS forwarding as offered in ISC BIND, but one could also see this as DNS proxying.

Background

Sometimes you might have a DNS server that is authoritative for DNS data, but is not reachable for all clients. This might be the case for instance, if

  • your DNS server is IPv6 only: it won't be directly reachable from the IPv4 Internet
  • your DNS server is running in a private network, either IPv4 or IPv6

In both cases, you need something that is publicly reachable, to enable clients to access the zone, like show in the following picture:

The problem: Forwarding requires recursive queries

ISC Bind allows to forward queries to another name server. However to do so, it need to be configured to allow handling recursive querying. However, if we allow recursive querying by any client, we basically create an Open DNS resolver, which can be quite dangerous.

The solution

ISC Bind by default has a root hints file compiled in, which allows it to function as a resolver without any additional configuration files. That is great, but not if you want to prevent it to work as forwarder as described above. But we can easily fix that problem. Now, let's have a look at a real world use case, step-by-step:

Step 1: Global options

In the first step, we need to set the global to allow recursion from anyone, as follows:

options {
    directory "/var/cache/bind";

    listen-on-v6 { any; };

    allow-recursion { ::/0; 0.0.0.0/0; };
};

However as mentioned above, this would create an open resolver. To prevent this, let's disable the root hints:

Step 2: Disable root hints

The root hints are served in the root zone, also know as ".". To disable it, we give bind an empty file to use:

zone "." {
        type hint;
        file "/dev/null";
};

Note: in case you do want to allow recursive function for some clients, you can create multiple DNS views.

Step 3: The actual DNS file

In our case, we have a lot of IPv6 only kubernetes clusters, which are named xx.k8s.ooo and have a world wide rachable CoreDNS server built in. In this case, we want to allow the domain c1.k8s.ooo to be world reachable, so we configure the dual stack server as follows:

zone "c1.k8s.ooo"  {
   type forward;
   forward only;
   forwarders { 2a0a:e5c0:2:f::a; };
};

Step 4: adjusting the zone file

In case you are running an IPv6 only server, you need to configure the upstream DNS server. In our case this looks as follows:

; The domain: c1.k8s.ooo
c1                          NS      kube-dns.kube-system.svc.c1

; The IPv6 only DNS server
kube-dns.kube-system.svc.c1 AAAA    2a0a:e5c0:2:f::a

; The forwarding IPv4 server
kube-dns.kube-system.svc.c1 A       194.5.220.43

DNS, IPv6, Kubernetes?

If you are curious to learn more about either of these topics, feel free to join us on our chat.