DNS Anycast Deployment

The following guide outlines the deployment methodology and installation instructions for deploying DNS Anycast Infrastructure. The solution comprises of DNS Load Balancers (dnsdist) which sit in front of the Caching DNS Servers and Load Balance the DNS queries among the backend DNS Servers. For advertising the Anycast IPs BGP routing application is used (FRR). For deployment Two CentOS v7.9 servers have been used as the Front End Load Balancers with DNSDIST v1.5.1 and FRR v7.5.

Configuring Anycast IPv4/v6

The IPs used for the Anycast access are configured as loopback IPs on the Load Balancers.

$ cat /etc/sysconfig/network-scripts/ifcfg-lo:1
DEVICE=lo:1
IPADDR=11.11.11.11
NETMASK=255.255.255.255
ONBOOT=yes
NETWORKING_IPV6=yes
DHCPV6C=no
IPV6INIT=yes
IPV6ADDR=fdcf:5069:ba86:eddd::11/128


$ cat /etc/sysconfig/network-scripts/ifcfg-lo:2
DEVICE=lo:2
IPADDR=12.12.12.12
NETMASK=255.255.255.255
ONBOOT=yes

Make sure IPv6 networking and forwarding is enabled for the networks in file /etc/sysconfig/network

NETWORKING_IPV6=yes
IPV6FORWARDING=yes

Performance Tuning

Change the sysctl parameters to be able to handle high traffic load.

net.ipv4.conf.all.arp_notify = 1
net.ipv4.conf.all.forwarding=1
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.all.forwarding=1
# Disable TCP timestampt
net.ipv4.tcp_timestamps=0
# Change the amount of incoming connections and incoming connections backlog
net.core.somaxconn=512
net.core.netdev_max_backlog=4096
# Increase the default and maximum send/receive buffers
net.core.rmem_default = 16777216
net.core.rmem_max = 16777216
net.core.wmem_default = 16777216
net.core.wmem_max = 16777216
#
net.ipv4.ip_local_port_range=16384    60999
#
net.netfilter.nf_conntrack_max = 524288
net.nf_conntrack_max = 524288

Increase the Connection Tracking Hash Size in file /etc/modprobe.d/nf_conntrack.conf.

options nf_conntrack expect_hashsize=131072 hashsize=131072

Allow the applications on the server to be able to open large number of file. Edit the file /etc/security/limits.conf and make the following entries.

......
......

*               soft    nofile  32768
*               hard    nofile  32768

......
......

Installation of DNSDIST Application

Add the EPEL repository for CentOS 7 and install the dnsdist application. Enable dnsdist to start on boot.

$ sudo yum install epel-release

$ sudo yum install yum-plugin-priorities
$ sudo curl -o /etc/yum.repos.d/powerdns-dnsdist-15.repo \
 https://repo.powerdns.com/repo-files/centos-dnsdist-15.repo
$ sudo yum install dnsdist
$ sudo systemctl enable dnsdist

Configuration of DNSDIST Application

The configurations need to be done in the configuration file /etc/dnsdist/dnsdist.conf. Create the configuration file and restrict the file access to dnsdist process so that the security keys or credentials are not readable by everyone.

$ sudo touch /etc/dnsdist/dnsdist.conf
$ sudo chown dnsdist /etc/dnsdist/dnsdist.conf
$ sudo chmod 640 /etc/dnsdist/dnsdist.conf

The dnsdist application provides a command line console over an encrypted tcp connection for controlling it, debugging DNS issues and retrieving statistics. We need to generate the encryption key by starting the application, make the key and put it into the configuration file.

$ dnsdist -l 127.0.0.1:5300
[..]
> makeKey()
setKey("ENCODED KEY")

To configure DNSDIST as load balancer for backend DNS servers, we need to listen on port 53 on IPv4/v6 and both TCP and UDP. To handle large incoming requests we will increase the dnsdist listeners (equal to number of cores). We will restrict the access to the listener to on specific IPs. To protect against Denial of Service Attacks we will restrict the number of queries per IPv4/v6. To provide better response to end users and to take load of the backend servers DNS query caching will be enabled on the load balancers. The application provides a webconsole to view the running server statistics.

-- /etc/dnsdist/dnsdist.conf

controlSocket('127.0.0.1:5199')				-- Enable control socket
setConsoleACL('127.0.0.1/32')				-- Restrict its access
setKey("ENCODED KEY")

-- Add Listeners, one entry for each CPU Core for v4 and v6
addLocal("0.0.0.0:53", {reusePort=true})		-- Listen on IPv4, port 53
addLocal("0.0.0.0:53", {reusePort=true})
addLocal("0.0.0.0:53", {reusePort=true})
addLocal("0.0.0.0:53", {reusePort=true})
addLocal("[::]:53", {reusePort=true})		-- Listen to IPv6 port 53
addLocal("[::]:53", {reusePort=true})
addLocal("[::]:53", {reusePort=true})
addLocal("[::]:53", {reusePort=true})

-- Backend Servers with weight and QPS limit
newServer({address="20.20.20.3", maxCheckFailures=3, checkInterval=10, weight=5, qps=5000})
newServer({address="20.20.20.4", maxCheckFailures=3, checkInterval=10, weight=5, qps=5000})
newServer({address="30.30.30.3", maxCheckFailures=3, checkInterval=10, weight=5, qps=5000})
newServer({address="30.30.30.4", maxCheckFailures=3, checkInterval=10, weight=5, qps=5000})

-- Allow clients IPs to connect
addACL("10.0.0.0/8")
addACL("172.16.0.0/12")
addACL("192.168.0.0/16")


-- Limit /32 ipv4 and /128 ipv6 to 50 (burst10) /100 (burst20) QPS
addAction(MaxQPSIPRule(50,32,128,10), DelayAction(500))
addAction(MaxQPSIPRule(100,32,128,20), DropAction())

-- Backend server load balancing policy
setServerPolicy(leastOutstanding)

-- Enable DNS query caching
-- 100K entries with avg 512B would take about 50MB of RAM
pc = newPacketCache(100000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false})
getPool(""):setCache(pc)

-- Enable Webserver
webserver("0.0.0.0:8083", "PASSWORD", "APIKEY", {}, "10.10.10.0/27, 192.168.20.0/27")

Working with DNSDIST Console

The dnsdist application can be accessed via web interface and command line console. The web interface (http://server-ip:8083) displays the running servers statistics. Configuration changes can be done on live server and statistics can be viewed via command console.

$ sudo dnsdist -c

> showVersion()
dnsdist 1.3.3


> showServers()
#   Name                 Address                       State     Qps    Qlim Ord Wt    Queries   Drops Drate   Lat Outstanding Pools
0                        10.10.10.3:53                   up     0.0    5000   1  5     250597    2877   0.0  30.3           0
1                        10.10.10.4:53                   up    14.9    5000   1  5     877556    4295   0.0   9.8           0
2                        20.20.20.3:53                 up     4.0    5000   1  5     376735    3108   0.0  20.0           0
3                        20.20.20.4:53                 up     0.0    5000   1  5      46650     113   0.0 141.0           0
All                                                             17.0                   1551538   10393


> getPool(""):getCache():printStats()
Entries: 3923/10000
Hits: 2671621
Misses: 1273977
Deferred inserts: 35
Deferred lookups: 64
Lookup Collisions: 1
Insert Collisions: 1
TTL Too Shorts: 0


> topClients(20)
   1  1.2.3.4                                   579  5.8%
   2  20.30.40.18                               567  5.7%
   3  30.40.20.150                              353  3.5%
   4  10.10.20.13                               312  3.1%
   5  10.20.30.50                               227  2.3%
  21  Rest                                     5574 55.7%


> topQueries(10)
   1  a.root-servers.net.                      1142 11.4%
   2  graph.facebook.com.                       302  3.0%
   3  www.google.com.                           170  1.7%
   4  connectivitycheck.gstatic.com.            141  1.4%
   5  graph.instagram.com.                      108  1.1%
   6  googleads.g.doubleclick.net.              104  1.0%
   7  www.googleapis.com.                        97  1.0%
   8  video.fisb1-1.fna.fbcdn.net.               96  1.0%
   9  scontent.fisb1-2.fna.fbcdn.net.            95  0.9%
  10  youtubei.googleapis.com.                   94  0.9%
  11  Rest                                     7651 76.5%


> showResponseLatency()
Average response latency: 22.26 msec
   msec
   0.10
   0.20
   0.40 .
   0.80 **********************************************************************
   1.60 ****
   3.20 *
   6.40 .
  12.80 .
  25.60 :
  51.20 ***
 102.40 *
 204.80 ****
 409.60 *
 819.20 :
1638.40 .


> showRules()
#     Matches Rule                                                     Action
0           3 IP (/32, /128) match for QPS over 80 burst 20            drop
1       15743 IP (/32, /128) match for QPS over 40 burst 10            delay by 500 msec
>

Installation of FRR Application

Add the FRR repository for CentOS 7 and install the application.

$ curl -O https://rpm.frrouting.org/repo/frr-stable-repo-1-0.el7.noarch.rpm
$ sudo yum install ./frr-stable-repo-1-0.el7.noarch.rpm
$ sudo yum install frr frr-pythontools

Enable the BGP routing daemon and VTY shell in the file /etc/frr/daemons.

...
...
bgpd=yes
...
...
...
vtysh_enable=yes
...
...

Configure the frr service to be dependent on the dnsdist application. In case the dnsdist process dies the routing of the anycast from that node should also stop. Edit the file /usr/lib/systemd/system/frr.service and enter the highlighted dependency.

...
[Unit]
Description=FRRouting
Documentation=https://frrouting.readthedocs.io/en/latest/setup.html
Wants=network.target
Requires=dnsdist.service
...

Restart the systemctl daemon to enable the change. Enable the frr damon to start on bootup.

$ sudo systemctl daemon-reload
$ sudo systemctl enable frr
$ sudo systemctl start frr

Configure BGP Routing

The Anycast route will be advertised from the Load Balancers using BGP. The Anycast IP will be configured as loopback IP and this will be the only allowed IP to be advertised.

$ vtysh

Hello, this is FRRouting (version 7.5).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

dnslb.domain.com# sh running-config
Building configuration...

Current configuration:
!
frr version 7.5
frr defaults datacenter
hostname dnslb.domain.com
log file /var/log/frr/frr.log informational
service password-encryption

!
password 8 XXXXXXXXXXXXXXXX
enable password 8 XXXXXXXXXXXX
!
router bgp 64520
 bgp router-id machine-ip-address
 neighbor bgp-ipv4-peer-1 remote-as 123456
 neighbor bgp-ipv4-peer-1 ebgp-multihop 255
 neighbor bgp-ipv4-peer-2 remote-as 123456
 neighbor bgp-ipv4-peer-2 ebgp-multihop 255
 neighbor bgp-ipv6-peer-1 remote-as 123456
 neighbor bgp-ipv6-peer-1 interface eth0
 neighbor bgp-ipv6-peer-1 ebgp-multihop 255
 neighbor bgp-ipv6-peer-2 remote-as 123456
 neighbor bgp-ipv6-peer-2 interface eth0
 neighbor bgp-ipv6-peer-2 ebgp-multihop 255
 !
 address-family ipv4 unicast
  redistribute connected
  neighbor bgp-ipv4-peer-1 soft-reconfiguration inbound
  neighbor bgp-ipv4-peer-1 route-map DNS-LB-v4 in
  neighbor bgp-ipv4-peer-1 route-map DNS-LB-v4 out
  neighbor bgp-ipv4-peer-2 soft-reconfiguration inbound
  neighbor bgp-ipv4-peer-2 route-map DNS-LB-v4 in
  neighbor bgp-ipv4-peer-2 route-map DNS-LB-v4 out
 exit-address-family
 !
 address-family ipv6 unicast
  redistribute connected
  neighbor bgp-ipv6-peer-1 activate
  neighbor bgp-ipv6-peer-1 soft-reconfiguration inbound
  neighbor bgp-ipv6-peer-1 route-map ANY-IPv6 in
  neighbor bgp-ipv6-peer-1 route-map DNS-LB-V6 out
  neighbor bgp-ipv6-peer-2 activate
  neighbor bgp-ipv6-peer-2 soft-reconfiguration inbound
  neighbor bgp-ipv6-peer-2 route-map ANY-IPv6 in
  neighbor bgp-ipv6-peer-2 route-map DNS-LB-V6 out
 exit-address-family
!
ip prefix-list ANY seq 5 deny 0.0.0.0/0 le 32
ip prefix-list DNS-LB-v4 seq 30 permit 11.11.11.11/32
ip prefix-list DNS-LB-v4-secondary seq 30 permit 12.12.12.12/32
!
ipv6 prefix-list DNS-LB-v6 seq 10 permit fdcf:5069:ba86:eddd::11/128
ipv6 prefix-list ANY-IPv6 seq 5 permit ::/0 le 128
!
route-map DNS-LB-v4 permit 10
 match ip address prefix-list DNS-LB-v4
!
route-map DNS-LB-v4 permit 20
 match ip address prefix-list DNS-LB-v4-secondary
 set as-path prepend 64520 64520
!
route-map DNS-LB-V6 permit 10
 match ipv6 address prefix-list DNS-LB-v6
!
route-map ANY-IPv6 permit 10
 match ipv6 address prefix-list ANY-IPv6
!
line vty
!
end

References

https://rpm.frrouting.org/
https://making.pusher.com/per-ip-rate-limiting-with-iptables/
https://berthub.eu/tmp/dnsdist-md/dnsdist-diagrams.md.html
https://dnsdist.org/quickstart.html
https://dnsdist.org/reference/config.html
https://ripe74.ripe.net/presentations/122-PLexis-dnsdist-lightning.pdf

Leave a Reply