This article will talk about setting up your own DNS authoritative server (serving your own domain name, this is not a public DNS cache server) using PowerDNS’s GeoIP Backend.

By using PowerDNS, you can build a DNS server that supports partition resolution (fine to country, city, ASN, custom IP segment), EDNS-Client-Subnet, DNSSEC, IPv6 on your own server.

This article is for domain name owners and webmasters, and is about authoritative DNS servers, not DNS caching servers. If you want to know more about DNS, please refer to Detailed Explanation of DNS Domain Name Resolution System - Basics and Introduction to DNSSEC, How Signatures Work.

This article is an update to self-built PowerDNS intelligent resolution server. The advantages and disadvantages of self-built DNS, configuring DNSSEC, and distributed DNS services are described in the previous article, which will be omitted in this article. This article mainly updates the following content:

  1. This article applies to the latest PowerDNS 4.2.0
  2. Use the database in MaxMind GeoIP 2 mmdb format
  3. Describe how to set partition resolution for the root domain name
  4. Use dnsdist to limit IP access rate and defend against DOS attacks.

Install PowerDNS Authoritative Server and dnsdist

Considering the different default versions of operating system software sources, it is recommended to go to PowerDNS repositories reconfigure the software sources for PowerDNS Authoritative Server (please Add 4.2 or above). You can also configure dnsdist’s repositories (please add version 1.2 or above).

After adding the repositories, update the software list and install pdns-server and pdns-backend-geoip. Under Debian/Ubuntu:

sudo apt-get install pdns-server pdns-backend-geoip

It should be noted that the systemd-resolved service may be included by default after Ubuntu 18.04 LTS. This service occupies port 53 of the loopback address, which conflicts with the port (0.0.0.0:53) required by PowerDNS. . You can: 1. Disable this service, 2. Modify PowerDNS’s local-address and [local-ipv6](https: //doc.powerdns.com/md/authoritative/settings/#local-ipv6) Configure to listen to external network IP addresses only, 3. Modify [local-port](https://doc.powerdns.com/md/authoritative/ settings/#local-port) is a non-53 port and forwarded using dnsdist (see below for details).

If you want to configure the user-side IP access rate, install dnsdist:

sudo apt-get install dnsdist

Disable systemd-resolved

If you choose to disable systemd-resolved, you can execute the following code:

sudo systemctl disable systemd-resolved.service
sudo systemctl stop systemd-resolved

Install geoipupdate and download GeoIP2

To implement partition resolution, we need the GeoIP database. The latest version of PowerDNS GeoIP Backend (4.2.0) already supports the mmdb format of GeoIP2, and also supports the dat format. The boss’s database only supports dat format. Since MaxMind has discontinued maintaining a free dat format database, it is highly recommended to use PowerDNS version 4.2 and above and use mmdb format instead IP database**.

First create a configuration file, create /etc/GeoIP.conf with the following content (including IPv4 and IPv6 country, city, and ASN data):

ProductIds GeoLite2-Country GeoLite2-City GeoLite2-ASN
DatabaseDirectory /usr/share/GeoIP

It is recommended to install geoipupdate, which can be installed in Ubuntu as follows:

sudo add-apt-repository ppa:maxmind/ppa
sudo apt update
sudo apt install geoipupdate

Then download/update GeoIP2:

sudo geoipupdate -v

All done!

It is also recommended to configure Crontab to periodically execute the above command to update the database file (and reload the PowerDNS service) to update the GeoIP database.

Configure PowerDNS

The configuration file of PowerDNS (hereinafter referred to as pdns) is located in /etc/powerdns. First delete the original demo configuration of PowerDNS, and then create a folder to store the DNSSEC key file:

sudo rm /etc/powerdns/pdns.d/*
sudo mkdir /etc/powerdns/keys

Then create the file, create /etc/powerdns/pdns.d/geoip.conf with the following content:

launch=geoip
geoip-database-files=/usr/share/GeoIP/GeoLite2-Country.mmdb /usr/share/GeoIP/GeoLite2-City.mmdb /usr/share/GeoIP/GeoLite2-ASN.mmdb
geoip-zones-file=/etc/powerdns/zones.yaml
geoip-dnssec-keydir=/etc/powerdns/key

In the geoip-database option, you can set up one or more GeoIP databases as needed.

Configure DNS records

You can create the file /etc/powerdns/zones.yaml to configure DNS records. For the specific format, see Zonefile format.

It should be noted that if mmdb is used, %re is the two-digit regional abbreviation for ISO3166. If using dat, it is the region code for GeoIP.

Since PowerDNS GeoIP Backend only supports partition resolution by domain name configuration, and does not support partition resolution by record configuration, so to configure root domain name partition resolution, you can refer to the following configuration (YAML format, the same below):

domains:
- domain: example.com
  ttl: 300
  records:
    unknown.cn.example.com:
      - soa: &soa ns1.example.com hostmaster.example.com 2014090125 7200 3600 1209600 3600
      - ns: &ns1
           content: ns1.example.com
           ttl: 600
      - ns: &ns2 ns2.example.com
      - mx: &mx 10 mx.example.com
      - a: 10.1.1.1
    bj.cn.example.com:
      - soa: *soa
      - ns: *ns1
      -ns: *ns2
      - mx: *mx
      - a: 10.1.2.1
    unknown.unknown.example.com:
      - soa: *soa
      - ns: *ns1
      -ns: *ns2
      - mx: *mx
      - a: 10.1.3.1
    ns1.example.com:
      - a: 10.0.1.1
      - aaaa: ::1
    ns2.example.com:
      - a: 10.0.2.1
  services:
    example.com: [ '%ci.%cc.service.geo.example.com', 'unknown.%cc.service.geo.example.com', 'unknown.unknown.service.geo.example.com']

As you can see, in order to configure sub-regional resolution of the domain name, we need to configure all types of records for each region. The root domain name usually has many record types, so it is relatively cumbersome to configure. The above YAML writing uses variables, which can reduce the repeated records of configuration. (&variable sets variables, *variable uses variables)

debug

You can debug by configuring logging as follows:

debug.tlo.xyz:
  - a: "%ip4"
  - aaaa: "%ip6"
  - txt: "co: %co; cc: %cc; cn: %cn; af: %af; re: %re; na: %na; as: %as; ci: %ci; ip: %ip"

Tested on the client through a command similar to the following (replace 10.11.12.13 with the IP address the service is listening on), and the following results are obtained:

$ dig @10.11.12.13 debug.example.com
  
debug.example.com. 3600 IN TXT "co: us; cc: us; cn: na; af: v4; re: ca; na: quadranet enterprises llc; as: 8100; ci: los angeles; ip: 10.11.12.13 "
debug.example.com.3600 IN A 10.11.12.13

This way you can see the specific value of each variable.

Lua records

In PowerDNS 4.2, Lua logging feature. The main function of its Lua record is that it can be configured to automatically switch records when downtime.

Since Lua records can use the Lua programming language to execute arbitrary code, which is relatively dangerous, PowerDNS disables this function by default and needs to be enabled in the configuration file:

enable-lua-records=yes

Configure the following records, which will return dynamically available A records. If the ports of both IPs are available (here is port 443), an IP address will be returned randomly, if one is unavailable, only the address of the available IP will be returned, otherwise both IPs will be returned at the same time:

sub.example.com:
  - lua: A "ifportup(443, {'192.0.2.1', '192.0.2.2'})"

Configure the following records to automatically check the status of the specified URL. By default, the first group of IP addresses will be returned. If the first group of IP addresses is unavailable, the second group will be returned:

sub.example.com:
  - lua: A "ifurlup('https://sub.example.com/', {{'192.0.2.1', '192.0.2.2'}, {'198.51.100.1'}})"

Lua records can be used with GeoIP functionality and can coexist with other types of records. In addition, the status check is not synchronized with the request, but periodically checks whether it is available in the background, so using the status check will not increase the delay of DNS requests.

configure dnsdist

By now, your DNS server should be working. Configuring dnsdist is described below.

dnsdist is equivalent to adding a layer of proxy on top of pdns, which can be configured to limit the access rate and play an anti-DOS role. Under normal circumstances, after the installation of dnsdist is completed, its service will start automatically.

In this example, change the main program listener of pdns to 127.0.0.1:8053. Create or modify the file /etc/dnsdist/dnsdist.conf with the following content:

newServer{address="127.0.0.1:8053",useClientSubnet=true}
setLocal("10.0.1.1")
setACL({'0.0.0.0/0', '::/0'})
setECSSourcePrefixV4(32)
setECSSourcePrefixV6(128)
addAction(MaxQSIPRule(10, 32, 56), TCAction())
addAction(MaxQSIPRule(20, 32, 56), DropAction())
addAction(MaxQSIPRule(40, 24, 48), TCAction())
addAction(MaxQSIPRule(80, 24, 48), DropAction())
addAction(MaxQSIPRule(160, 16, 40), TCAction())
addAction(MaxQSIPRule(320, 16, 40), DropAction())

The newServer statement sets the server proxied by dnsdist, which is the IP address and port number on which pdns listens. setLocal sets the listening ip address of dnsdist. setACL sets the allowed IP addresses, here all IPv6 and IPv4 access is allowed. setECSSourcePrefixV4 and setECSSourcePrefixV6 set the number of bits of the IP address transmitted by the EDNS Client Subnet Client function. Here, 32 bits are set for IPv4, and 128 bits are set for IPv6, that is, the full length of the IP address is reserved. A smaller value than this can also be set in actual production.

The last few lines addAction and MaxQPSIPRule define the access rate limit. Where MaxQSIPRule has three parameters. The first parameter is qps, which is queries per second, the number of requests per second. The second parameter is the CIDR value for IPv4, which defaults to 32, and the third parameter is the CIDR value for IPv6, which defaults to 64. TCAction represents asking the client to use a TCP request, and DropAction represents rejecting the request. When an ordinary client receives a request from the DNS server to use TCP, it will use TCP to make a DNS request, which does not affect the use of ordinary users. DOS generally does not attack the TCP DNS service.

For example, the following statement means:

addAction(MaxQSIPRule(40, 24, 48), TCAction())

Limits the qps of /24 IPv4 addresses (256) and /48 IPv6 addresses (2^80) to 40, if it exceeds, the client is required to use TCP requests.

In addition to TCAction and DropAction, DelayAction (an integer parameter representing the number of milliseconds to delay) and NoRecurseAction (specifically to limit requests marked with recursion ((RD))) can also be used.

Therefore, using dnsdist with pdns can make the DNS service have a certain anti-DOS ability.