PowerDNS MongoDB
PowerDNS + MongoDB
Требуется - настроить ДНС сервер который будет брать данные из Монго. Почему Монго - так хочет заказчик. Заполнять базу будет отдельное приложение, днс будет частью системы.
Сборка плагина
Ни одна из бинарных сборок которые я попробовал не включала плагина для работы с монго. В gentoo не было ключа для включение плагина при компиляции.
Путь один - собирать сервер руками. (чуть позже - собрать пакет для своей системы, rpm )
Для сборки требуются исходники mongo. Я не нашел как передать путь к хедерам монго - насколько я могу судить путь захардкожен. Потому (знаю что не правильно) просто скопировал все хедеры в папку с плагином (/usr/src/pdns-3.0.1/modules/mongodbbackend/)
Далее традиционно
./configure make
Обратить внимание на описание проблем со сборкой ниже - мне пришлось "патчить" модуль до рабочего состояния. Возможно вам не прийдется, или это особенность моего дистрибутива или сборки.
make install не делаю а беру модуль и подкладываю его там где ищет pdns_server
Ошибка в плагине
Мне пришлось закоментировать использование хинтов - с ними я получал ошибку:
Jun 08 14:15:19 [MONGODBBackend: (1)] (getSOA) Query: { name: "smtp-servers.example.com" } Jun 08 14:15:19 [MONGODBBackend: (1)] (getSOA) Query: { name: "example.com" } Jun 08 14:15:19 We have authority, zone='example.com', id=10 Jun 08 14:15:19 UeberBackend received question for ANY of smtp-servers.example.com Jun 08 14:15:19 [MONGODBBackend: (1)] (lookup) Query: { query: { name: "smtp-servers.example.com" }, $hint: { name: 1 } } Jun 08 14:15:19 Ueber get() was called for a ANY record Jun 08 14:15:19 [MONGODBBackend: (1)] (get) Error: The record '{ $err: "bad hint", code: 10113 }' is missing required element(s) for the query '{ query: { name: "smtp-servers.example.com" }, $hint: { name: 1 } }' Jun 08 14:15:19 UeberBackend reached end of backends Jun 08 14:15:19 After first ANY query for 'smtp-servers.example.com', id=10: weDone=0, weHaveUnauth=0, weRedirected=0 Jun 08 14:15:19 Found nothing in the ANY, but let's try wildcards.. Jun 08 14:15:19 UeberBackend received question for ANY of *.example.com Jun 08 14:15:19 [MONGODBBackend: (1)] (lookup) Query: { query: { name: "*.example.com" }, $hint: { name: 1 } } Jun 08 14:15:19 Ueber get() was called for a ANY record Jun 08 14:15:19 [MONGODBBackend: (1)] (get) Error: The record '{ $err: "bad hint", code: 10113 }' is missing required element(s) for the query '{ query: { name: "*.example.com" }, $hint: { name: 1 } }' Jun 08 14:15:19 UeberBackend reached end of backends Jun 08 14:15:19 Found nothing in the ANY and wildcards, let's try NS referral Jun 08 14:15:19 UeberBackend received question for NS of smtp-servers.example.com Jun 08 14:15:19 [MONGODBBackend: (1)] (lookup) Query: { query: { name: "smtp-servers.example.com", type: "NS" }, $hint: { name: 1, type: 1 } } Jun 08 14:15:19 Ueber get() was called for a NS record Jun 08 14:15:19 [MONGODBBackend: (1)] (get) Error: The record '{ $err: "bad hint", code: 10113 }' is missing required element(s) for the query '{ query: { name: "smtp-servers.example.com", type: "NS" }, $hint: { name: 1, type: 1 } }'
Насколько я могу понять - нужно писать hint { ... } а не $hint. Предположительно это баг.
В файле minimal.cc удалил строку:
mongo_query.hint(q_type == "ANY" ? BSON("name" << 1) : BSON("name" << 1 << "type" << 1));
MongoDB - минимальные записи записи
Для того что бы ДНС заработал нужно внести некоторое минимально число записей и описание зоны. в Монго при попытке вставить данные в несуществующий объект он будет создан что несколько упрощает жизнь.
Имена используемых коллекций я не менял: (определяется в конфиге PowerDNS вероятно).
collection-domains = domains collection-records = records collection-domainmetadata = domainmetadata collection-cryptokeys = cryptokeys collection-tsigkeys = tsigkeys
Работа с монго из консоли - заполнить зону
mongo - утилита для работы из консоли.
Тут короткое описание как вставлять записи. Пример со вставкой полдразумевает что зона уже создана и сервер стартован.
Использовать правильную базу:
use dns
Проверяю что записи нет:
sirmax@sirmax ~ $ host start4.example.com 192.168.15.1 Using domain server: Name: 192.168.15.1 Address: 192.168.15.1#53 Aliases:
Определяю переменную, а потом вствляю ее:
#mongo > test = {"domain_id" : 10, "name" : "start4.example.com", "type" : "A", "ttl" : 120, "content" : [ {"data" : "192.168.2.2"} ] } { "domain_id" : 10, "name" : "start4.example.com", "type" : "A", "ttl" : 120, "content" : [ { "data" : "192.168.2.2" } ] } >db.records.insert(test) >db.records.find(); <skipped> { "_id" : ObjectId("4fd1fb484a6ee59df5031fea"), "domain_id" : 10, "name" : "start4.example.com", "type" : "A", "ttl" : 120, "content" : [ { "data" : "192.168.2.2" } ] }
sirmax@sirmax ~ $ host start4.example.com 192.168.15.1 Using domain server: Name: 192.168.15.1 Address: 192.168.15.1#53 Aliases: start4.example.com has address 192.168.2.2
Я сделал следующие записи:
domains
{"domain_id" : 10, "name" : "example.com", "type" : "NATIVE", "ttl" : 120, "SOA" : {"hostmaster" : "ahu.example.com", "nameserver" : "ns1.example.com", "serial" : 2000081501, "refresh" : 28800, "retry" : 7200, "expire" : 604800, "default_ttl" : 86400 } } {"domain_id" : 10, "name" : "example.com", "type" : "NS", "ttl" : 120, "content" : [ {"data" : "ns1.example.com"}, {"data" : "ns2.example.com"} ] } {"domain_id" : 10, "name" : "example.com", "type" : "MX", "ttl" : 120, "content" : [ {"prio" : 10, "data" : "smtp-servers.example.com"}, {"prio" : 15, "data" : "smtp-servers.test.com"} ] } {"domain_id" : 10, "name" : "ns1.example.com", "type" : "A", "ttl" : 120, "content" : [ {"data" : "192.168.1.1"} ] } {"domain_id" : 10, "name" : "ns2.example.com", "type" : "A", "ttl" : 120, "content" : [ {"data" : "192.168.1.2"} ] } {"domain_id" : 10, "name" : "smtp-servers.example.com", "type" : "A", "ttl" : 120, "content" : [ {"data" : "192.168.0.2"}, {"data" : "192.168.0.3"}, {"data" : "192.168.0.4"} ] }
records
{"domain_id" : 10, "name" : "example.com", "type" : "NS", "ttl" : 120, "content" : [ {"data" : "ns1.example.com"}, {"data" : "ns2.example.com"} ] } {"domain_id" : 10, "name" : "example.com", "type" : "MX", "ttl" : 120, "content" : [ {"prio" : 10, "data" : "smtp-servers.example.com"}, {"prio" : 15, "data" : "smtp-servers.test.com"} ] } {"domain_id" : 10, "name" : "ns1.example.com", "type" : "A", "ttl" : 120, "content" : [ {"data" : "192.168.1.1"} ] } {"domain_id" : 10, "name" : "ns2.example.com", "type" : "A", "ttl" : 120, "content" : [ {"data" : "192.168.1.2"} ] } {"domain_id" : 10, "name" : "smtp-servers.example.com", "type" : "A", "ttl" : 120, "content" : [ {"data" : "192.168.0.2"}, {"data" : "192.168.0.3"}, {"data" : "192.168.0.4"} ] }
db.records.find(); { "_id" : ObjectId("4fd1dae01ac5e9baad14dc49"), "domain_id" : 10, "name" : "smtp-servers.example.com", "type" : "A", "ttl" : 120, "content" : [ { "data" : "192.168.0.2" }, { "data" : "192.168.0.3", "ttl" : 60 }, { "data" : "192.168.0.4" } ] } { "_id" : ObjectId("4fd1dccd1ac5e9baad14dc4a"), "domain_id" : 10, "name" : "ns1.example.com", "type" : "A", "ttl" : 120, "content" : [ { "data" : "192.168.1.1" } ] } { "_id" : ObjectId("4fd1dce71ac5e9baad14dc4b"), "domain_id" : 10, "name" : "example.com", "type" : "NS", "ttl" : 120, "content" : [ { "data" : "ns1.example.com" }, { "data" : "ns2.example.com" } ] } { "_id" : ObjectId("4fd1dd7a1ac5e9baad14dc4c"), "domain_id" : 10, "name" : "ns2.example.com", "type" : "A", "ttl" : 120, "content" : [ { "data" : "192.168.1.2" } ] } { "_id" : ObjectId("4fd1de521ac5e9baad14dc4d"), "domain_id" : 10, "name" : "example.com", "type" : "NS", "ttl" : 120, "content" : [ { "data" : "ns1.example.com" }, { "data" : "ns2.example.com" } ] }
Думаю, вцелом все более-менее ясно. Примеры записей есть тут: http://wiki.powerdns.com/trac/browser/trunk/pdns/modules/mongodbbackend/test Так же есть примеры с работой с json и заливкой данных.
rpm для CentOS
Со сборкой RPM пришлось повозиться. отмечу что для сборки нужен g++ Кроме того - несколько патчей.
pdns.spec
привожу полный spec, сами исходники - брал с оффсайта.
Summary: A modern, advanced and high performance authoritative-only nameserver Name: pdns Version: 3.1 Release: 10%{?dist} Group: System Environment/Daemons License: GPLv2 URL: http://powerdns.com BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Source0: http://downloads.powerdns.com/releases/%{name}-%{version}.tar.gz #Patch0: %{name}-fixinit.patch #Patch1: %{name}-gcc44.patch #Patch2: pdns-fix-postgres-detection.patch #Patch3: pdns-fix-crash-on-sigstop.patch Patch0: pdns-fix-mongo-backend.patch Patch1: pdns-fix-md5sum-bug.patch Patch2: pdns-fix-hint.patch #Patch1: pdns-fix-lua-detection.patch Requires(post): %{_sbindir}/useradd, /sbin/chkconfig Requires(preun): /sbin/service, /sbin/chkconfig BuildRequires: boost-devel, chrpath Provides: powerdns = %{version}-%{release} %description The PowerDNS Nameserver is a modern, advanced and high performance authoritative-only nameserver. It is written from scratch and conforms to all relevant DNS standards documents. Furthermore, PowerDNS interfaces with almost any database. %package backend-mysql Summary: MySQL backend for %{name} Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: mysql-devel %package backend-postgresql Summary: PostgreSQL backend for %{name} Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: postgresql-devel %package backend-pipe Summary: Pipe backend for %{name} Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %package backend-geo Summary: Geo backend for %{name} Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} %package backend-ldap Summary: LDAP backend for %{name} Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: openldap-devel %package backend-sqlite Summary: SQLite backend for %{name} Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} BuildRequires: sqlite-devel %package backend-mongodb Summary: MongoDB backend for %{name} Group: System Environment/Daemons Requires: %{name}%{?_isa} = %{version}-%{release} BuildRequires: mongodb-devel %description backend-mysql This package contains the gmysql backend for %{name} %description backend-postgresql This package contains the gpgsql backend for %{name} %description backend-pipe This package contains the pipe backend for %{name} %description backend-geo This package contains the geo backend for %{name} It allows different answers to DNS queries coming from different IP address ranges or based on the geographic location %description backend-ldap This package contains the ldap backend for %{name} %description backend-sqlite This package contains the SQLite backend for %{name} %description backend-mongodb This package contains the MongoDB backend for %{name} %prep %setup -q %patch0 -p1 -b .fixmongo %patch1 -p1 -b .fixmd5 %patch2 -p1 -b .fixhint #%patch1 -p1 -b .fixlua #%patch0 -p1 -b .fixinit #%patch1 -p1 -b .gcc44 #%patch2 -p1 -b .postgres #%patch3 -p1 -b .sigstop %build export CPPFLAGS="-DLDAP_DEPRECATED %{optflags}" %configure \ --sysconfdir=%{_sysconfdir}/%{name} \ --libdir=%{_libdir}/%{name} \ --disable-static \ --with-modules='' \ --with-dynmodules='pipe gmysql gpgsql geo ldap gsqlite3 mongodb' \ --with-mysql-lib=%{_libdir}/mysql \ --with-sqlite3-lib=%{_libdir} #sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool #sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool make %{?_smp_mflags} %install %{__rm} -rf %{buildroot} make install DESTDIR=%{buildroot} %{__rm} -f %{buildroot}%{_libdir}/%{name}/*.la %{__install} -p -D -m 0755 pdns/pdns %{buildroot}%{_initrddir}/pdns %{__mv} %{buildroot}%{_sysconfdir}/%{name}/pdns.conf{-dist,} # add the pdns user to the config file sed -i '1i\setuid=pdns' %{buildroot}%{_sysconfdir}/%{name}/pdns.conf sed -i '2i\setgid=pdns' %{buildroot}%{_sysconfdir}/%{name}/pdns.conf # strip the static rpath from the binaries chrpath --delete %{buildroot}%{_bindir}/pdns_control chrpath --delete %{buildroot}%{_bindir}/zone2ldap chrpath --delete %{buildroot}%{_bindir}/zone2sql chrpath --delete %{buildroot}%{_sbindir}/pdns_server chrpath --delete %{buildroot}%{_libdir}/%{name}/*.so %post if [ $1 -eq 1 ]; then /sbin/chkconfig --add pdns userid=`id -u pdns 2>/dev/null` if [ x"$userid" = x ]; then %{_sbindir}/useradd -c "PowerDNS user" -s /sbin/nologin -r -d / pdns > /dev/null || : fi fi %preun if [ $1 -eq 0 ]; then /sbin/service pdns stop >/dev/null 2>&1 || : /sbin/chkconfig --del pdns fi %clean %{__rm} -rf %{buildroot} %files %defattr(-,root,root,-) #%doc ChangeLog TODO pdns/COPYING %{_bindir}/pdns_control %{_bindir}/zone2ldap %{_bindir}/zone2sql %{_bindir}/dnsreplay %{_bindir}/pdnssec %{_sbindir}/pdns_server %{_mandir}/man8/pdns_control.8.gz %{_mandir}/man8/pdns_server.8.gz %{_mandir}/man8/zone2sql.8.gz %{_initrddir}/pdns %dir %{_libdir}/%{name}/ %dir %{_sysconfdir}/%{name}/ %config(noreplace) %{_sysconfdir}/%{name}/pdns.conf %files backend-mysql %defattr(-,root,root,-) #%doc pdns/COPYING %{_libdir}/%{name}/libgmysqlbackend.so %files backend-postgresql %defattr(-,root,root,-) #%doc pdns/COPYING %{_libdir}/%{name}/libgpgsqlbackend.so %files backend-pipe %defattr(-,root,root,-) #%doc pdns/COPYING %{_libdir}/%{name}/libpipebackend.so %files backend-geo %defattr(-,root,root,-) #%doc pdns/COPYING modules/geobackend/README %{_libdir}/%{name}/libgeobackend.so %files backend-ldap %defattr(-,root,root,-) #%doc pdns/COPYING %{_libdir}/%{name}/libldapbackend.so %files backend-sqlite %defattr(-,root,root,-) #%doc pdns/COPYING %{_libdir}/%{name}/libgsqlite3backend.so %files backend-mongodb %defattr(-,root,root,-) %{_libdir}/%{name}/libmongodbbackend.so %changelog
Patch0 pdns-fix-mongo-backend.patch
Patch0 исправляет ошибку с путями к монго - конфигурационный скрипт не умеет брать их как параметр.
--- pdns-3.1/modules/mongodbbackend/Makefile.in 2012-05-04 14:15:45.000000000 +0400 +++ pdns-3.1/modules/mongodbbackend/Makefile.in.new 2012-06-11 15:02:14.920495293 +0400 @@ -259,10 +259,11 @@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = @THREADFLAGS@ $(BOOST_CPPFLAGS) EXTRA_DIST = OBJECTFILES OBJECTLIBS -INCLUDES = -I/opt/mongo/include/mongo/ +INCLUDES = -I/usr/include/mongo/ lib_LTLIBRARIES = libmongodbbackend.la libmongodbbackend_la_SOURCES = mongodbbackend.cc mongodbbackend.hh minimal.cc slave.cc master.cc reload.cc private.cc dnssec.cc supermaster.cc crc32.cc -libmongodbbackend_la_LDFLAGS = -module -avoid-version -L/opt/mongo/lib64 $(BOOST_THREAD_LDFLAGS) $(BOOST_FILESYSTEM_LDFLAGS) $(BOOST_SYSTEM_LDFLAGS) -lmongoclient $(BOOST_THREAD_LIBS) $(BOOST_FILESYSTEM_LIBS) $(BOOST_SYSTEM_LIBS) +libmongodbbackend_la_LDFLAGS = -module -avoid-version $(BOOST_THREAD_LDFLAGS) $(BOOST_FILESYSTEM_LDFLAGS) $(BOOST_SYSTEM_LDFLAGS) -lmongoclient $(BOOST_THREAD_LIBS) $(BOOST_FILESYSTEM_LIBS) $(BOOST_SYSTEM_LIBS) + all: all-am .SUFFIXES:
Patch1: pdns-fix-md5sum-bug.patch
Вылез странный глюк с md5
Замечу что пролема спецефична для CentOS, в Gentoo я с ней не столкнулся.
Выглядело это так:
>pdns_server Jun 11 17:54:40 Reading random entropy from '/dev/urandom' MD5 ("") = c3a27e6566d1519e6c44f6d2a2d3c277 **** ERROR, should be: d41d8cd98f00b204e9800998ecf8427e MD5 ("a") = 029e6fbd25744bd424f59ca76b03f53c **** ERROR, should be: 0cc175b9c0f1b6a831c399e269772661 MD5 ("abc") = 9914180f313951b88a123553e6b0c253 **** ERROR, should be: 900150983cd24fb0d6963f7d28e17f72 MD5 ("message digest") = b2f5947dec4505e22325f2cbfb34518f **** ERROR, should be: f96b697d7cb7938d525a2f31aaf161d0 MD5 ("abcdefghijklmnopqrstuvwxyz") = 78b3fa1f48e481ff1e191ad6af1cceb3 **** ERROR, should be: c3fcd3d76192e4007dfb496cca67e13b MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = 2e3602a28d001b0f31abc9d5a52eb7f4 **** ERROR, should be: d174ab98d277d9f5a5611c2c9f419d9f MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 7cd7a7d2780c656c61eb74a68413ee4f **** ERROR, should be: 57edf4a22be3c955ac49da2e2107b67a Mon Jun 11 17:54:40 Assertion: 10354:md5 unit test fails 0x7f6745f21c21 0x7f6745f72c26 /usr/lib64/libmongoclient.so(_ZN5mongo11msgassertedEiPKc+0x131) [0x7f6745f21c21] /usr/lib64/libmongoclient.so(+0x119c26) [0x7f6745f72c26] terminate called after throwing an instance of 'mongo::MsgAssertionException' what(): md5 unit test fails Jun 11 17:54:40 Got a signal 6, attempting to print trace: Jun 11 17:54:40 pdns_server() [0x49afa0] Jun 11 17:54:40 /lib64/libc.so.6() [0x38c3c32900] Jun 11 17:54:40 /lib64/libc.so.6(gsignal+0x35) [0x38c3c32885] Jun 11 17:54:40 /lib64/libc.so.6(abort+0x175) [0x38c3c34065] Jun 11 17:54:40 /usr/lib64/libstdc++.so.6(_ZN9__gnu_cxx27__verbose_terminate_handlerEv+0x12d) [0x38d04bea7d] Jun 11 17:54:40 /usr/lib64/libstdc++.so.6() [0x38d04bcc06] Jun 11 17:54:40 /usr/lib64/libstdc++.so.6() [0x38d04bcc33] Jun 11 17:54:40 /usr/lib64/libstdc++.so.6() [0x38d04bcd2e] Jun 11 17:54:40 /usr/lib64/libmongoclient.so(_ZN5mongo11msgassertedEiPKc+0x34a) [0x7f6745f21e3a] Jun 11 17:54:40 /usr/lib64/libmongoclient.so(+0x119c26) [0x7f6745f72c26] Aborted (core dumped)
нагуглил всего одну ссылку: https://blog.benetasso.com/en/entry/mongodb_backend_2 но похоже этот патч решает проблему.
--- pdns-3.1/pdns/ext/polarssl-1.1.2/library/Makefile 2012-05-04 14:16:01.000000000 +0400 +++ pdns-3.1/pdns/ext/polarssl-1.1.2/library/Makefile.new 2012-06-11 18:16:22.266497782 +0400 @@ -1,7 +1,7 @@ # Also see "include/polarssl/config.h" -CFLAGS += -I../include -D_FILE_OFFSET_BITS=64 -Wall -W -Wdeclaration-after-statement +CFLAGS += -I../include -D_FILE_OFFSET_BITS=64 -Wall -W -Wdeclaration-after-statement -fvisibility=hidden OFLAGS = -O # MicroBlaze specific options:
Patch2: pdns-fix-hint.patch
Есть так же проблема что при использовании хинтов ничего не работает. Я просто отключаю их - не уверен что это 100% верное решение но как минимум делает модуль работоспособным. (описано выше)
--- pdns-3.1/modules/mongodbbackend/dnssec.cc 2012-05-04 14:13:23.000000000 +0400 +++ pdns-3.1/modules/mongodbbackend/dnssec.cc.new 2012-06-11 18:38:48.521532693 +0400 @@ -94,7 +94,7 @@ if(logging) L<<Logger::Info << backend_name << "(getBeforeAndAfterNamesAbsolute) Query after: '"<< mongo_q.toString() << "'" << endl; - mongo_q.hint(BSON("domain_id" << 1 << "auth" << 1 << "ordername" << 1)); +// mongo_q.hint(BSON("domain_id" << 1 << "auth" << 1 << "ordername" << 1)); mongo::BSONObj mongo_r = m_db.findOne(collection_records, mongo_q, &fields ); if (mongo_r.isEmpty() && !afterafter) { @@ -118,7 +118,7 @@ if(logging) L<<Logger::Info << backend_name << "(getBeforeAndAfterNamesAbsolute) Query before: '"<< mongo_q.toString() << "'" << endl; - mongo_q.hint(BSON("domain_id" << 1 << "auth" << 1 << "ordername" << -1)); +// mongo_q.hint(BSON("domain_id" << 1 << "auth" << 1 << "ordername" << -1)); mongo_c = m_db.query(collection_records, mongo_q, 0, 0, &fields); --- pdns-3.1/modules/mongodbbackend/minimal.cc 2012-06-11 18:38:02.767497178 +0400 +++ pdns-3.1/modules/mongodbbackend/minimal.cc.new 2012-06-11 18:38:13.204498110 +0400 @@ -166,7 +166,7 @@ q_name = qname; mongo_query = q_type == "ANY" ? QUERY( "name" << toLower(qname) ) : QUERY( "name" << toLower(qname) << "type" << q_type); - mongo_query.hint(q_type == "ANY" ? BSON("name" << 1) : BSON("name" << 1 << "type" << 1)); +// mongo_query.hint(q_type == "ANY" ? BSON("name" << 1) : BSON("name" << 1 << "type" << 1)); elements = false; default_ttl = 0;