跳至主要內容

使用PowerDNS搭建全球分地区DNS解析服务

NaiveTomcat大约 5 分钟随笔网络DNS

介绍

当网络中提供相同服务的服务器分布在不同的地区时,为了提高用户访问速度,需要将用户的请求转发到最近的服务器上。如果不使用开销(资金开销)很大的Anycast技术,就需要一个全球分地区的DNS解析服务,将用户的请求转发到最近的服务器上。

PowerDNS是一个开源的DNS服务器实现,支持权威名称服务器的搭建。配有多种存储后端,如Bind、gmysql等,辅以DNS的AXFR进行Master-Slave同步,可以方便的搭建全球多节点DNS权威服务器。同时配合PowerDNS的Lua Record和geoip后端,可以实现基于请求者IP进行区分的DNS应答。

同时,PowerDNS还有诸如PowerDNS Admin等管理面板,可以实现方便的DNS管理。

全球多节点DNS权威服务器搭建

安装PowerDNS

PowerDNS的安装较为简便,提供了各大发行版的安装包,可以直接使用包管理器进行安装。需要注意,发行版官方仓库的PowerDNS版本通常较老,建议使用PowerDNS仓库进行安装。具体安装流程可在这里open in new window查看。

此处需要安装pdns-serverpdns-backend-geoip和对应所需的存储后端。

安装GeoIP数据库

PowerDNS的geoip后端需要使用GeoIP数据库,建议使用geoipupdate进行更新。GeoIP数据库的下载地址为这里open in new window,需要注册账号后才能下载。

配置PowerDNS

在主服务器上配置Master支持:

#################################
# master        Act as a master
#
master=yes

#################################
# allow-axfr-ips        Allow zonetransfers only to these subnets
#
allow-axfr-ips=[从机IP]

从服务器使用Bind后端。

在从服务器上配置Slave支持:

#################################
# slave Act as a slave
#
slave=yes

创建和主服务器同步的zone:

# named.conf
zone "example.net" {
    file "/var/lib/powerdns/zones.slave.d/example.net.zone";
    type slave;
    masters { 192.0.2.1; [2001:db8::1]; };
};

在所有服务器上配置GeoIP后端:

launch+=geoip
geoip-database-files=/path/to/GeoLite2-City.mmdb

在所有服务器上配置Lua Record:

如对所有DNS Zone启用Lua Record则:

#################################
# enable-lua-records    Process LUA records for all zones (metadata overrides this)
#
enable-lua-records=yes

否则对指定的DNS Zone修改其metadata启用Lua Record。

Lua记录设置

Lua记录基本语法

PowerDNS使用LUA作为record type标志该条记录为LUA记录。记录的record data为:[TYPE] [Lua Script],其中TYPE为该条记录的类型,Lua Script为LUA脚本。为简化多数情况的脚本编写,PowerDNS默认会在[Lua Script]前添加return,因此可以仅编写返回的表达式。如果要进行复杂的内容编写可在脚本前方添加;来禁用默认的return。注意返回的表达式的类型必须为字符串。

简单的Lua记录

本例中,我们设置一个TXT记录,使datetime.nteren.netTXT记录返回请求时间。

使用Lua的os.date函数格式化并返回当前时间字符串。

相对应的bind zone file为:

datetime        60      LUA     TXT "os.date('!%a %b %d %X %Y UTC')"

基于请求IP的Lua记录

通过bestwho获得请求IP

PowerDNS提供了bestwho变量来获取客户的IP地址信息。如果递归解析器使用了EDNS Client Subnet功能,bestwho将返回客户端的IP地址,否则返回递归解析器的IP地址。

下面的zone file配置whoami.nteren.netA记录返回请求IP的IPv4地址,AAAA记录返回请求IP的IPv6地址,TXT记录返回请求的地址和端口。

whoami  60      LUA     A ";if(bestwho:isIPv4()) then return bestwho:toString() else return '0.0.0.0' end"
whoami  60      LUA     AAAA ";if(bestwho:isIPv6()) then return bestwho:toString() else return '::' end"
whoami  60      LUA     TXT "bestwho:toStringWithPort()"

通过GeoIP获得客户端的地理位置

PowerDNS的GeoIP后端为Lua Record提供了查询客户端地理位置的相关函数。latlon()返回客户端的经纬度,latlonloc()以DNS LOC记录的格式返回客户端位置信息,continentCode()返回客户端所在的大陆代码,countryCode()返回客户端的国家代码,regionCode()返回客户端地区代码的地区部分。

下面的zone file配置location.nteren.net的TXT记录返回客户端请求IP和地理位置信息,whoami.nteren.net的LOC记录返回客户端的位置信息。

location        60      LUA     TXT "bestwho:toString() .. ' ' .. continentCode() .. '-' .. countryCode() .. '-' .. regionCode()"
whoami  60      LUA     LOC "latlonloc()"

通过客户端位置进行条件判断

PowerDNS的GeoIP后端同样提供了基于客户端地理位置进行条件判断的相关函数。如:country('CN')返回客户端是否来自中国,region('SN')返回客户端是否来自陕西省,continent('AS')返回客户端是否来自亚洲。类似的,也可以接受由字符串构成的数组,此时当客户位于任何一个区域内即返回true

下面的zone file是一个简单的展示该特性的demo,配置了test.nteren.net的TXT记录,当请求来自中国大陆时返回CN,否则返回IDK

test    60      LUA     TXT ";if(country('CN')) then return 'CN' else return 'IDK' end"

基于地理距离优选IP

PowerDNS的GeoIP后端也提供了基于客户端IP和候选IP距离返回最优结果的函数pickclosest()。该函数接受一个数组,返回与bestwho在地理位置上最接近的地址。

同时,PowerDNS也提供了ifportup(portnum, addresses[, options])函数。该函数接受需测试的端口、候选IP地址数组和可选的参数。可选参数中可指定选择器,如pickclosest。如果指定了选择器,ifportup将返回选择器返回的结果,否则返回随机一个可用的IP地址。

该配置也可结合上述的客户端位置判断特性,实现基于地理位置的负载均衡。下面即为一个简单的配置案例

blog.naivetomcat        60      LUA     A ";if country('CN') then return 'ip1' else return ifportup(443, {'ip2', 'ip3', 'ip4'}, {selector='pickclosest'}) end"
blog.naivetomcat        60      LUA     AAAA "if country('CN') return 'ip61' else return ifportup(443, {'ip62', 'ip63', 'ip64'}, {selector='pickclosest'}) end"

参考资料

更多资料以及PowerDNS Lua Record参考可以在PowerDNS的官方文档中找到。