Chapter 4. Managing Zone Data

With traditional DNS servers, such as BIND, administrators usually manage primary zone data as files. More recently, DNS servers have begun to support loading primary zone data from other sources, such as databases.

CoreDNS supports a variety of methods to manage zone data. Some will be very familiar to DNS administrators, like zone data files; others are more modern, such as using Git; whereas some are downright retro (host tables, anyone?). In this chapter, we cover all of them.

Together, these options provide administrators with flexibility and, in some cases, advanced functionality in the mechanism they use to manage zone data. Host tables, for example, provide a simple way to add name-to-address and address-to-name mappings without the overhead of creating and maintaining an entire zone data file. Git, on the other hand, provides distributed version-control capabilities.

Let’s begin with the file plug-in, which supports zone data files. We actually covered this in Chapter 3, but we go through it in more detail here.

The file Plug-in

For an administrator with experience managing zone data files, the file plug-in is probably the most familiar mechanism CoreDNS offers. file configures CoreDNS as the primary DNS server for one or more zones. In its simplest form, the file plug-in takes the syntax shown in Example 4-1.

Example 4-1. Simple file plug-in syntax
file DBFILE [ZONES...]

DBFILE is a zone data file containing resource records. You can specify DBFILE as a full pathname or as a relative pathname; relative pathnames are interpreted relative to any path set with the root directive.

ZONES is an optional list of one or more zones described by the resource records in DBFILE. If ZONES is omitted, CoreDNS uses the zones from the configuration block.

Example 4-2 presents a simple example of a file plug-in, and Example 4-3 shows the zone data file it refers to.

Example 4-2. Simple example of a file plug-in
foo.example {
    file db.foo.example
}
Example 4-3. db.foo.example
@  3600  IN  SOA  ns1.foo.example.  root.foo.example.  (
   2019041900
   3600
   600
   604800
   600 )
   3600  IN  NS  ns1.foo.example.
   3600  IN  NS  ns2.foo.example.

ns1      IN  A  10.0.0.53
         IN  AAAA  2001:db8:42:1::53
ns2      IN  A  10.0.1.53
         IN  AAAA  2001:db8:42:2::53
www      IN  A  10.0.0.1
         IN  AAAA  2001:db8:42:1:1

Because the resource records in the db.foo.example zone data file are all attached to relative domain names (e.g., ns1 and www rather than dot-terminated fully qualified domain names), the file can be loaded to describe multiple zones, as shown in Example 4-4.

Example 4-4. Using a single zone data file for multiple zones
. {
    file db.foo.example foo.example bar.example
}

This assumes that you want the contents of foo.example and bar.example to be identical, of course—for example, that you want both www.foo.example and www.bar.example to map to the IPv4 address 10.0.0.1.

The file plug-in also supports a more extensive syntax that allows you to specify which DNS servers are allowed to transfer the zone(s) and how frequently to check the zone data file for changes, as demonstrated in Example 4-5.

Example 4-5. More detailed syntax of the file plug-in
file DBFILE [ZONES... ] {
    transfer to ADDRESS...
    reload DURATION
    upstream [ADDRESS...]
}

Without a transfer directive, CoreDNS will not allow zone transfers of the zone or zones described by the file plug-in. To send NOTIFY messages to a particular secondary DNS server as well as to allow zone transfers to that secondary, specify the secondary’s IP address. For multiple secondaries, you can list their IP addresses in a single transfer directive or use multiple directives. You can also specify a network in Classless Inter-Domain Routing (CIDR) notation to allow zone transfers  from any IP address on that network, or * to allow zone transfers from anywhere, as presented in Example 4-6.

Example 4-6. Detailed example of the file plug-in
foo.example {
    file db.foo.example {
        transfer to 10.0.1.53
        transfer to *
    }
}

reload allows you to specify how frequently CoreDNS checks the zone data file to determine whether the serial number in the start of authority (SOA) record has changed. When CoreDNS detects a new serial number, it reloads the file (as one or more zones) and sends NOTIFY messages to secondary DNS servers designated in transfer directives.  The default reload is one minute; a setting of 0 disables the periodic check. Valid values are a number followed by “s” for seconds, “m” for minutes, and “h” for hours; for example, 30s for 30 seconds.

The file plug-in is fine if you’re configuring CoreDNS as the primary DNS server for a few zones. But what if you want CoreDNS to be primary for hundreds of zones? In that case, you want the auto plug-in.

The auto Plug-in

The auto plug-in provides a clever way to load a large number of zones from multiple zone data files at once. This minimizes the length and complexity of the Corefile and provides the ability to automatically configure CoreDNS as the primary for new zones. Example 4-7 shows the syntax.

Example 4-7. Syntax of auto plug-in
auto [ZONES...] {
    directory DIR [REGEXP ORIGIN_TEMPLATE]
    transfer to ADDRESS...
    reload DURATION
}

auto tells CoreDNS to scan the directory for files matching the pattern db.*.  Each file is interpreted as a zone data file whose origin is what follows db..  That origin must be within the zone or zones listed in ZONES, if specified.

Suppose that the directory /etc/coredns/zones contains the zone data files db.foo.example and db.bar.example, which describe the zones foo.example and bar.example, respectively. The auto plug-in shown in Example 4-8 would instruct CoreDNS to read the entire directory and load the foo.example and bar.example zones from those files.

Example 4-8. An auto plug-in
auto example {
    directory /etc/coredns/zones
}

Moreover, you can create another zone (under the example domain, anyway) just by creating a new, appropriately named zone data file in /etc/coredns/zones.

You can also specify a regular expression besides db.* for CoreDNS to look for in the directory, and give CoreDNS instructions on how to create the origin (and domain name of the zone) from the filename. If, for example, you named your zone data files <domain>.zone, you could use the auto plug-in presented in Example 4-9.

Example 4-9. Another auto plug-in
auto example {
    directory /etc/coredns/zones (.*)\.zone {1}
}

The regular expression (regex) is expected to incorporate what is essentially a Perl capture group: the parentheses around “.*” indicate the portion of the filename that contains the origin (in this case, everything before .zone); the {1} is a backreference to that portion of the regex. You can use other backreferences, of course, such as {2} for the second capture group.

Finally, you can use the file directives transferreload, and upstream to control which DNS servers can perform zone transfers of these zones (and receive NOTIFY messages from CoreDNS), how often the directory is scanned for modified zone data files, and which DNS servers to use to resolve external domain names.

Using the auto Plug-in with Git

Avid users of the Git distributed version-control system can easily combine the auto plug-in with a script such as git-sync to periodically pull zone data files from a Git repository into a directory. CoreDNS then scans the directory and loads new and modified zones. Using Git allows multiple administrators to jointly manage a set of version-controlled zone data files so that administrators can track changes to zone data.

git-sync is, not surprisingly, implemented as a container. Here’s an example of using git-sync to periodically scan a GitHub repository for new zone data files, as demonstrated in Example 4-10.

Example 4-10. git-sync in action
docker run -d \
   -v /etc/coredns/zone:/tmp/git \
   registry/git-sync \
     --repo=https://github.com/myzonedata
     --branch=master
     --wait=30

This command uses the git-sync container to synchronize files from the https://github.com/myzonedata repository to /etc/coredns/zone and to check the repository every 30 seconds for changes.

What if you have the opposite problem that auto solves—namely, that you just want to load a few resource records into CoreDNS without the overhead of a zone data file. Well then, the hosts plug-in will come in handy.

The hosts Plug-in

The hosts plug-in is used to configure CoreDNS to generate zone data from a host table (e.g., /etc/hosts). The host table must be in the standard host table format:

<IP address> <canonical name> [aliases...]

The IP address can be either an IPv4 or an IPv6 address. The canonical name and any aliases must be domain names. After the host table is read, hosts generates the following:

  • A records for each entry with an IPv4 address, mapping the canonical name and any aliases to the specified IPv4 address
  • AAAA records for each entry with an IPv6 address, mapping the canonical name and any aliases to the specified IPv6 address
  • A PTR record for either an IPv4 or IPv6 address, mapping the address back to the canonical name

Note that the aliases become A or AAAA records, not canonical name (CNAME) records.

Example 4-11 presents the syntax of the hosts plug-in.

Example 4-11. Syntax of the hosts plug-in
hosts [FILE [ZONES...]] {
    [INLINE]
    ttl SECONDS
    no_reverse
    reload DURATION
    fallthrough [ZONES...]
}

FILE specifies the name of the hosts file to read and parse; by default, CoreDNS reads /etc/hosts. If FILE is a relative pathname, it’s interpreted relative to the directory specified in the ROOT directive.

ZONES is an optional list of one or more zones that are loaded from the host table. If ZONES isn’t specified, CoreDNS uses the zones from the enclosing server block. Domain names will be loaded only into the zones of which they are a part. In other words, if you load the host table shown in Example 4-12 as both foo.example and bar.example, you’ll end up with host1.foo.example in the foo.example zone and host2.bar.example in bar.example.

Example 4-12. Example host table
10.0.0.1 host1.foo.example
10.0.1.1 host2.bar.example

Zones whose data is read using the hosts plug-in aren’t really complete zones; they don’t have SOA records, for example, so they can’t be transferred to another DNS server. Consequently, hosts isn’t usually used for management of an entire zone; rather, you would use it for loading discrete domain names. For example, some people load a host table that includes domain names used in serving ads and that maps those domain names to the IP address 0.0.0.0.

[INLINE] allows you to specify one or more lines in host table format directly in the directive, as shown in Example 4-13.

Example 4-13. Inline host table entries
foo.example {
    hosts {
        10.0.0.1 host1.foo.example
        10.0.1.1 host2.foo.example
    }
}

TTL sets the time-to-live (TTL) value for records synthesized from host table entries; by default, the TTL is set to 3600 seconds, or 1 hour. The value must be specified as an integer (in other words, don’t specify units such as “s” for seconds).

no_reverse inhibits the creation of PTR records from host table entries.

By default, CoreDNS reloads the host table every 5 seconds. reload allows you to change this interval by specifying a scaled value (i.e., a number followed by a unit of time) with the following units:

  • ns for nanoseconds
  • us or µs for microseconds
  • ms for milliseconds
  • s for seconds
  • m for minutes
  • h for hours

I’m not sure you really need the ability to specify a reload interval of 500,000,000 nanoseconds (with 500000000 ns), but with CoreDNS, you can!

Finally, fallthrough controls whether queries for the zones handled by the hosts plug-in fall through to another plug-in if no answer is found. For example, you might want queries for foo.example to fall through from the hosts plug-in to a file plug-in if the answer wasn’t found in the host table. By default, specifying fallthrough instructs CoreDNS to fall through for any queries handled by the hosts plug-in. To fall through only for a subset of those queries, you can specify a list of zones as an argument.

Next, we look at a way to load zone data from Amazon’s Route 53 service.

The route53 plug-in

Many organizations use the Amazon Route 53 service to provide authoritative DNS services from the Amazon Web Services (AWS) cloud. CoreDNS provides a plug-in, route53, that enables it to synchronize zone data from Route 53, much like a secondary DNS server would transfer zone data from a master DNS server. CoreDNS can then respond authoritatively to queries for domain names in the synchronized zone.

Example 4-14. Syntax of the route53 plug-in
route53 [ZONE:HOSTED_ZONE_ID...] {
    [aws_access_key AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY]
    upstream
    credentials PROFILE [FILENAME]
    fallthrough [ZONES...]
}

To synchronize the zone, CoreDNS must provide the domain name of the zone and a special “Hosted Zone ID” used within AWS as well as credentials that authenticate CoreDNS to Route 53.1 To get the zone ID, log in to the AWS Dashboard, go to the Route 53 service, and then click “Hosted zones.” This should bring up a table of zones that includes each zone’s Hosted Zone ID.

The route53 plug-in requires that you specify the domain name of the zone and the Hosted Zone ID in a particular (and particularly inflexible) format: the domain name of the zone, terminated with a dot, followed by a colon, then the Hosted Zone ID. Example 4-15 demonstrates this format.

Example 4-15. The route53 plug-in
route53 foo.example.:Z3CDX6AOCUSMX3 {
    fallthrough
}

You can specify multiple zones if you want CoreDNS to synchronize all of them from Route 53.

By default, CoreDNS determines the AWS credentials to use from environment variables or an AWS credentials file. You can override this behavior by specifying the credentials directly within the route53 plug-in, as illustrated in Example 4-16.

Example 4-16. route53 plug-in with explicit credentials
route53 foo.example.:Z3CDX6AOCUSMX3 {
    aws_access_key AKIAIMSX7F33X4MOVBZA SnA4XxFPx/BDEMbty3EKVze7Xi3DkQ5a8akRO9j9
}

You can also specify an AWS credentials file other than the default using the credentials subdirective shown in Example 4-17.

Example 4-17. route53 plug-in with credentials file
route53 foo.example.:Z3CDX6AOCUSMX3 {
    credentials default .awscredentials
}

default, in this case, specifies a particular profile in the credentials file.

Like hosts, the route53 plug-in supports fallthrough to other plug-ins, for all or a specified set of zones synchronized from Route 53. And, like fileroute53 supports specification of an upstream DNS server to resolve references to external domain names in the Route 53 zone data.

That’s the last of the plug-ins for managing zone data in CoreDNS. Hopefully, among the four options (five if you count using the auto plug-in with Git), you’ll find one that meets your needs.

Although CoreDNS is a flexible and capable primary DNS server, its strength, of course, is supporting DNS-based service discovery. We cover that in Chapter 5.

1 The Hosted Zone ID uniquely identifies the zone to AWS. The domain name of the zone alone isn’t always enough, because AWS might have Public Hosted Zones and Private Hosted Zones with the same domain name.

Get Learning CoreDNS now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.