1. Why ?

There are lot of ways of how you can manage you company, home or corporate DNS zones. You can offload this task to any DNS registar, you can use any available DNS server software with any back-end that you like, or … you can use Zabbix and particularly Zabbix database as your trusty backend. Let’s look at the simple fact, that you already installed and configured Zabbix on your network. And you invested considerable time and effort of doing so. And. looking inside Zabbix, you see, that it knows a great deal about your infrastructure. It’s host names and IP addresses. Maybe, you are also running discovery process on your network and keeping this portion of configuration up-to date. Maybe you already integrate Zabbix with your inventory system. And with your ticketing system. If you did not done that already, maybe you should. So, your Zabbix installation already one of the central points of your enterprise management. Any reason, why you still using vi to manage your DNS zones or paying somebody to do this for you, when you have all you needed at your fingertips ?

2. What you will need ?

Aside from Zabbix itself, not much:

Some time and software development skills …

3. Prepare your environment.

I will not be covering on how to install and configure Python on your target hosts. You can install it from rpm/deb repositories or compile yourself from the scratch. Second, download unbound DNS resolver and compile it. I am doing this using command

./configure --with-libevent --with-pyunbound --with-pthreads --with-ssl --with-pythonmodule

Please note, that you shall have development files for libevent, openssl, posix threads and the Python on your host.

Next, compile and install REDIS server. I will leave you with excellent Redis documentation as your guide through this process. All I want to say: “It is not difficult to do”. After you’ve compiled and installed Redis, install Python redis module – redis-py.

4. Overview of the design.

You will have number of components on your Zabbix-DNS infrastructure.

  • REDIS servers. Will be serving as a primary storage for you direct and reverse mappings. Depending on the size of your DNS zones, you may want to scale the memory for the hosts on which you will run your
    REDIS servers. All REDIS servers are configured for persistency.
  • DNS_REDIS_SYNC. Script, which will query SQL table interfaces from zabbix database and populate master REDIS server.
  • resolver.py. Unbound python script, which will provide a proper interfacing between zabbix database, REDIS and UNBOUND resolver

5. Masters and slaves.

I am intentionaly insisiting on more complicated master-slave configuration for your installation. When you will need to scale your DNS cluster, you will appretiate that you’ve done this. Depending on your Zabbix configuration, you may be choosing appropriate location for your master REDIS server and DNS_REDIS_SYNC process.

Depending on the size of your Zabbix and number of NVPS, you may consider to perform “select” operations on SQL table “interface” on the less busy with inserts and updates slave MySQL server.

How to setup master-slave MySQL replication is outside of the scope of this article.

Google it. Slave REDIS node shall be local to a DNS resolver.

6. DNS_REDIS_SYNC

DNS_REDIS_SYNC is a simple Python (or whatever language you choose to use, as long as it can interface with MySQL and REDIS) script, which designed to populate master REDIS storage. In order to get information from table interface, you may issue query

select interfaceid,ip,dns from interface where type = 1

When you’ve got all you Name->IP associations from Zabbix database, start to populate direct and reverse zones in REDIS, like

SET A:%(name) %(ip)

SET PTR:%(ip) %(name)

you do not want keys to stick in you REDIS forever, so I recommend to set conservative expiration for your keys. See Chapter #7

EXPIRE A:%(name) %(expiration_time_in_sec)

EXPIRE PTR:%(ip) %(expiration_time_in_sec)

That’s it. Your REDIS database is ready to be used by resolver.py module.

7. Expire or not to expire.

The easiest and more dangerous way to remove the old info from DNS zones stored in REDIS, is to use REDIS EXPIRE commands and capabilities. This will work great if you never get in the situation like this

Downtime of the Zabbix MySQL server > Key expiration time.

One way on how to deal with that situation is to monitor the downtime of the primary Zabbix MySQL from another Zabbix server, which configured to monitor primary server (you shall have this server already) and when downtime crosses pessimistic threshold, execute Action script, which will extend TTL for the keys in the master REDIS server.

8. Anathomy of the resolver.py

Before you will write your resolver.py, consult Unbund documentation on how to write unbound python modules and how to use unbound module. Also, you shall be aware of “gotcha” for the resolver.py . Since it is executed in “embedded Python”, it does not inherit information about location of the some of the python modules. Be prepared to define path to those modules using sys.path.append(…) calls.

Main callback for the query processing inside resolver.py will be function “operate(id, event, qstate, qdata)”. Parameters are:

  • id, is a module identifier (integer);
  • event, type of the event accepted by module. Look at the documentation of which event types are there. For the resolver, we do need to catch MODULE_EVENT_PASS and MODULE_EVENT_NEW
  • qstate, is a module_qstate data structure
  • qdata , is a query_info data structure

First, qstate.qinfo.qname_str will contain your query. The best way to detect if this is a query of the direct or reverse zones is to issue this call

<em>socket.inet_aton(qstate.qinfo.qname_str[:-1])</em>

and then catch the exceptions. If you have an exception, then it direct zone, of not – reverse.

Second, you will be needed to build a return message, like this:

msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA)

Then, depend on which zone you shall query, you sent a one of the following requests to REDIS:

GET A:%(name)

GET PTR:%(ip)

if REDIS returns None, you shall query Zabbix mysql database with one of the following queries:

select interfaceid,ip,dns from interface where type = 1 and dns = ‘%(name)’;

select interfaceid,ip,dns from interface where type = 1 and ip = ‘%(ip)’;

If MySQL query returned data, you shall populate REDIS as described in Chapter 6, fill return message and invalidate and re-populate UNBOUND cache using following calls:

invalidateQueryInCache(qstate, qstate.return_msg.qinfo)

storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0)

Return message is filled by appending results to a msg.answer

"%(name) 900 IN A %(ip)"
"%(in_addr_arpa) 900 IN PTR %(name)."

for direct and reverse zones.

qstate alse shall be updated with information about return message before you manipulate with UNBOUND cache

msg.set_return_msg(qstate)

9. Summary.

Well, now you know enough on how to integrate information from your Zabbix instance into your enterprise.

Tags: No tags

Comments are closed.