一.原因
自从月初CloudXNS突然翻脸,以及一个星期了,虽然我在最短的时间内知道了这个消息并在V2顺利骗了一波铜币,然后花了一个晚上迁移自己的一堆域名,成功将这个事件的影响降到了最低。但是,针对这种情况还是需要做一些预防,所以把之前攒的自建DNS的相关资料拿出来好好研究了一下,并在这几天中实验了其中一种,基于PowerDNS的GeoDNS方案,发现效果还行,于是打算分享一下,也顺便做个记录。(之后打算研究下兽兽安利过的gdns,不过这玩意好像在Centos上比较蛋疼)
二.安装
由于Centos6平台求稳,所以各种包的版本都比较落后,因此在PowerDNS的官方源中没有支持GeoIP的版本,这特么就很尴尬了,在Github发issue求助被告知因为yaml-cpp版本不够所以只能自己编译后,我就开始了长达两天的修(编)仙(译)过程。
其实吧,一开始没发现官方的Github里有打包脚本,于是只能自己拆Centos6和Centos7的src.rpm包来拼一份spec出来,在折腾半天后发现各种不对劲,于是重新去找,结果在官方的Github里发现了build-scripts/build-auth-rpm这个脚本……蛋疼,好吧,拿来修修改改,加个pdns-backend-geoip进去,然后开始了编译打包大业。接着就碰到了一个简直日狗的问题,成功编译,成功打包,成功安装,然后……查询不出设置的txt记录,提示如下
Exception building answer packet for xx.ip.example.com (All data was not consumed) sending out servfail
这就很绝望,因为查询a记录啥都正常,于是怀疑是不是编译过程中有啥依赖有问题,折腾了一天,最后发现……yaml格式的配置文件中txt记录部分少了俩空格……这,我的内心是崩溃的,大哥,你格式不对提醒下啊,别报这种八竿子打不到一起的错误啊。好吧,总算是成功了,所以用Centos6的各位就不用蛋疼了,用我的rpm包就行。
至于Centos7以及其他系统的各位,请参照官方文档从官方repo或者系统自带的仓库安装——>传送门
Centos6请参照如下,第一个yaml-cpp也是我编译的最新版本,官方的太老了,不支持,另外还会安装GeoIP这个包,请确保添加了epel或者city-fan源
yum install http://down.senra.me/Senra-Build/yaml-cpp/yaml-cpp-0.5.3-1.el6.x86_64.rpm http://down.senra.me/Senra-Build/pdns/pdns-4.1.0-rc1.el6.x86_64.rpm http://down.senra.me/Senra-Build/pdns/pdns-backend-geoip-4.1.0-rc1.el6.x86_64.rpm http://down.senra.me/Senra-Build/pdns/pdns-tools-4.1.0-rc1.el6.x86_64.rpm
之后就可以开始配置了(默认没有开机启动,请使用chkconfig pdns on来设置)
三.配置
配置的话主要有三个,pdns配置、zone配置、dnssec配置(可选)
①. pdns.conf 这个默认在/etc/pdns/pdns.conf 中,可能其他系统有所不同,找不到请使用如下命令
find / -name "pdns.conf*"
当然也可能找到pdns.conf-dist这个文件,这是默认配置样本,因为是GeoIP版本,所以大部分配置其实都没用,可以按照我的修改下
cat >/etc/pdns/pdns.conf<<'EOF' logging-facility=0 resolver=8.8.4.4:53 expand-alias=yes launch=geoip setgid=pdns setuid=pdns geoip-database-files=/data/pdns/geoip/GeoIP.dat /data/pdns/geoip/GeoIPv6.dat /data/pdns/geoip/GeoIPCity.dat /data/pdns/geoip/GeoLiteCityv6.dat /data/pdns/geoip/GeoIPISP.dat /data/pdns/geoip/GeoIPOrg.dat /data/pdns/geoip/GeoLiteASNum.dat /data/pdns/geoip/GeoIPASNumv6.dat geoip-database-cache=memory geoip-zones-file=/data/pdns/zone.yaml geoip-dnssec-keydir=/data/pdns/key EOF
其中如下两行,是使用alias(也叫aname)记录的前提条件,官方文档里有说明——>传送门
resolver=8.8.4.4:53 expand-alias=yes
这个记录和CloudXNS的Link记录以及CloudFlare的CNAME Flattening记录一样都是为了解决主域名无法CNAME而生的,确实是挺好用的。
另外第一行是为了将pdns的日志分离开,如果和你其他软件的日志的冲突,也可以改成其他数字(0-7),这个需要配合如下命令
echo -n "local0.* -/var/log/pdns.log" >/etc/rsyslog.d/pdns.conf service rsyslog restart
另外,请修改geoip-database-files,我这边的不是默认路径,而且额外加入了Maxmind的付费库以便提高精准度,默认路径在/usr/share/GeoIP文件夹,当然我还是建议做软连接到和zone.yaml以及key一个文件夹,方便下面的同步(如果默认GeoIP文件夹空的话,请运行geoipupdate命令更新)
下面添加一个zone.yaml方便参考使用
mkdir -p /data/pdns/key /data/pdns/geoip cat >/data/pdns/zone.yaml<<'EOF' domains: - domain: example.com ttl: 3600 records: example.com: - soa: content: ns.example.net hostmaster.example.net 20171005 7200 3600 86400 1800 ttl: 3600 - ns: content: ns1.example.net ttl: 86400 - ns: content: ns2.example.net ttl: 86400 - a: content: 127.0.0.1 ttl: 600 - txt: content: "senra dns works" ttl: 600 "*.ip.example.com": - txt: content: "IP%af: %ip, Continent: %cn, Country: %co, ASn: %as, Region: %re, Organisation: %na, City: %ci" ttl: 0 kor.as.geo.example.com: - a: 37.252.244.19 jpn.as.geo.example.com: - a: 37.252.228.11 chn.as.geo.example.com: - a: 103.243.255.114 hkg.as.geo.example.com: - a: 37.252.249.20 twn.as.geo.example.com: - a: 37.252.243.19 sgp.as.geo.example.com: - a: 217.146.9.10 tha.as.geo.example.com: - a: 217.146.0.10 vnm.as.geo.example.com: - a: 203.162.172.10 ind.as.geo.example.com: - a: 217.146.11.10 rus.eu.geo.example.com: - a: 37.252.254.10 gbr.eu.geo.example.com: - a: 37.252.230.11 fra.eu.geo.example.com: - a: 37.252.225.11 deu.eu.geo.example.com: - a: 37.252.248.36 nld.eu.geo.example.com: - a: 188.172.219.26 usa.na.geo.example.com: - a: 162.220.221.10 can.na.geo.example.com: - a: 188.172.212.10 bra.sa.geo.example.com: - a: 37.252.238.11 aus.oc.geo.example.com: - a: 37.252.240.19 zaf.af.geo.example.com: - a: 188.172.217.60 unknown.af.geo.example.com: - a: 188.172.217.60 unknown.as.geo.example.com: - a: 37.252.228.11 unknown.eu.geo.example.com: - a: 178.255.152.99 unknown.na.geo.example.com: - a: 162.220.221.10 unknown.oc.geo.example.com: - a: 37.252.240.19 unknown.sa.geo.example.com: - a: 37.252.238.11 unknown.unknown.geo.example.com: - a: 37.252.249.20 services: www.example.com: [ '%co.%cn.geo.example.com', 'unknown.%cn.geo.example.com', 'unknown.unknown.geo.example.com' ] EOF
yaml的写法我个人还是不太熟的,所以如果你熟的话请尽管浪,我这边的IP用的是某机房全球各地的IP,地区多,拿来测试效果极好,配置完成后请启动PowerDNS (还是得把下面的dnssec配置好,起码创建好,不然缺文件不好启动),另外请务必参照yaml写法,不然别想跑起来,可以使用某个js-yaml的网页来校对是否写错——>传送门
service pdns restart
PS.配置文件说明请查看PowerDNS文档——>传送门
参数部分大概是这样
IP%af: %ip, Continent: %cn, Country: %co, ASn: %as, Region: %re, Organisation: %na, City: %ci %af是v4或者v6, %cn是大陆缩写(两位代码), %co是国家缩写(三位代码), %as是请求IP所属ASN, %na是IP所属组织, %ci是城市
大陆缩写不说了,国家缩写是ISO 3166-1 alpha-3,你也可以在pygeoip的const里面找到,城市这个蛋疼,请查看maxmind的region_codes.csv,这玩意你搜一下就有
至于DNSSEC,直接创建一下
pdnsutil secure-zone example.com pdnsutil show-zone example.com
然后把返回结果填到你的域名商的DNSSEC设置那儿就行,挑着填,不用都写,建议sha256那个就够了
本部分参考:
四.同步
要知道,为了保证速度以及可靠性,我们至少有两个NS节点,这也是一个域名NS的最少要求。所以,多个节点同步就是一个问题了,由于PowerDNS的GeoIP版本不存在Master以及Slave的区分,所以我们需要额外的方法来实现同步。我推荐btsync (也叫sync home或者resilio sync) 或者 syncthing 以及rsync。
我之前打折的时候买了俩btsync的key,所以这儿就用它了,安装我博客有文章,我这边不说其他的,直接写命令了 ——>传送门
rpm --import https://linux-packages.resilio.com/resilio-sync/key.asc cat >/etc/yum.repos.d/resilio-sync.repo<<'EOF' [resilio-sync] name=Resilio Sync baseurl=https://linux-packages.resilio.com/resilio-sync/rpm/$basearch enabled=1 gpgcheck=1 EOF yum install resilio-sync -y sed -i 's/127.0.0.1/0.0.0.0/g' /etc/resilio-sync/config.json service resilio-sync restart chkconfig --add resilio-sync
然后你访问http://IP:8888/ 就能看到web界面了,简单设置密码以及节点名字啥的,然后在你本机上以及其他节点上都装好,添加一个文件夹,我这边路径就是/data/pdns了,(其实,你也可以在自己电脑上装一个,这样你直接本地修改就能静默同步到节点上,很是方便)
各个节点设置完成,尝试修改文件,确认是否会自动同步。
最后一个问题,由于PowerDNS不能自动检测zone文件变动,所以需要手动重载,如果你使用rsync的话有post script这个设置,可以在同步后执行如下命令实现重载配置
pdns_control reload
如果你用syncthing,可以使用它的api功能写个agent监听同步事件,但是btsync的api我本以为是直接用的,后来发现还额外收费,贵的可怕,放弃,换个思路。
使用inotify来直接对文件的事件监听然后触发操作,试了下发现效果意外的不错
echo "nohup zone_watcher 2>&1 >>/tmp/watcher.log &" >>/etc/rc.local cat >/usr/bin/zone_watcher<<'EOF' #!/bin/sh cleanlog=`echo -n "" > /tmp/pdns-zone.log` zonefile="/data/pdns/zone.yaml" while inotifywait -q -e modify,move_self $zonefile >/dev/null; do echo -e "`date "+%Y-%m-%d %H:%M:%S"` Zone Changed, Reload: [ `sleep 2 & pdns_control reload || echo -n "Failed"` ]" >>/tmp/pdns-zone.log done EOF chmod +x /usr/bin/zone_watcher nohup zone_watcher 2>&1 >>/tmp/watcher.log &
然后就能在后台监视了,发现修改后会延迟2秒然后重载
到这儿一切都OK了,我这测试效果还行,虽然部分区域分配的ip还是有些问题,大概是因为ip库依旧不够精准以及检测节点使用的DNS向上查询可能被透传了的关心,不过也已经基本够用了。
自建DNS主要是灵活性够高,当然如果不想折腾还是建议选择现成的,毕竟自建一是维护麻烦,二是如果想要效果好成本也是挺大的,特别是DNS建议使用anycast的节点来确保在线率以及查询速度。
文章评论
为啥你对CentOS6的执念这么深..... (这都快2018年了
@reisen 233,主要是懒得换,习惯了
失败+1
咸鱼+1
conoha+1
@神楽坂 白 额,我是不太敢拿conoha搞这种东西,万一被人攻击了就血崩
根域好像不能智能解析
@軍師 这个问题在23天前被修复了,不过如果要用的话可能需要使用官方repo的master branch,4.1版本还没解决这个问题 https://github.com/PowerDNS/pdns/issues/4276
@Senra 修复了好像没说怎么设置
还有博主你的%co和%cn 反了
官方解释: %co = 3-letter country, %cn = continent
看你的解释我测试了好几次都蛋疼了
@軍師 额,设置应该和子域名一样吧,那个确实是反了...sorry咯
hi 哥们 麻烦问下现在GeoIP更新到mmdb格式后,pdns.conf 配置文件应该怎么用呀?我现在用的是4.1的pdns
@joe pdns还未支持mmdb这种geoip2的格式,目前只支持geoip legency也就是dat格式,你需要等官方支持
@Senra 非常感谢,谢谢,是不是你服务器时区设置有问题?现在显示9个小时前我留言的
我直接用ns1.com了,可以自定义AS ip段匹配解析
@雅丶涵 识别的国内地区不准
大佬有成品的powerdns整套吗。或者接受定制
大牛有成品dns系统吗,或者接受powerdns定制
想请问一下有没有公开的国内的支持DNSSEC的服务器?在WINDOWS10系统上如何启用呢