Posts Tagged ‘howto’

Monitoring PowerDNS via the internal web server

Tuesday, February 16th, 2010

If you want to stay informed about the current status of your PowerDNS server, there is no need to scan the logs or use third party tools like logcheckd. Actually, you might want to do this anyway, but PowerDNS provides an internal web server, which summarizes all the status information (including log messages) in a very nice and compact way for you to view.

By default, the web server will listen on port 8081 on localhost. That means, that you can’t view the status information from the outside network. To view the page, you could use a command line browser like lynx. You could change the interface of the web server to a public IP address, but this is very insecure and not recommended. The PowerDNS web server provides some sensitive information about your DNS server and you should not expose this information to the public. Also, the PowerDNS web server is probably not as thoroughly tested and hardened as e.g. Apache or nginx. Another problem is, that you can only specify a single password and that you are limited to basic authentication, which is not very secure.

There is a secure way to retrieve the information provided by PowerDNS from outside your DNS server host. You could use Apache, nginx or any other web server you like as a proxy server. That way you can use more advanced authentication methods built into that web server to secure your status page. I will now show you how to do this using Apache 2 on Debian. We’ll need mod_proxy, mod_proxy_http and mod_headers enabled on the Apache 2 server. If you do not want to run an instance of Apache 2 on your DNS server, you could use an SSL tunnel or a secure back channel link to a remote Apache server to retrieve the status page. But this is beyond the scope of this post.

First, enable the internal PowerDNS web server by editing the configuration file.

  1. webserver=yes
  2. webserver-address=127.0.0.1
  3. webserver-port=8081
  4. webserver-password=PowerDNS

This tells PowerDNS to run the internal web server on port 8081 of the localhost interface. The user name will be admin and the password will be PowerDNS. Of course you should change the password to something more secure, it’s just an example. After a restart of PowerDNS you can connect to your server from your DNS server host. The password is optional, but it’s safer that way, especially in an environment where you’ve got other users on your DNS server box.

Now install and enable the required modules of Apache 2 by executing the following commands.

  1. apt-get install libapache2-mod-proxy-html
  2. a2enmod proxy
  3. a2enmod proxy_http
  4. a2enmod headers

The last module is only needed, if you set a password for your internal web server as recommended above. I assume that you’ve got some kind of virtual host configuration for your Apache server. You’ll want to add a new virtual host for the DNS status information. If you use a subdirectory, navigation might be a bit odd. Let’s add a new site to the available sites.

  1. vi /etc/apache2/sites-available/status.dns.example.net
  2. <VirtualHost 192.168.0.1:80>
  3. ServerName status.dns.example.net
  4. DocumentRoot /var/www/
  5. ProxyRequests Off
  6. <proxy *>
  7. Order deny,allow
  8. Allow from all
  9. ForceType 'text/html; charset=UTF-8'
  10. </proxy>
  11. ProxyPass / http://localhost:8081
  12. ProxyPassReverse / http://localhost:8081
  13. </VirtualHost>

Alright, so what are we doing here? Basically, we’re adding a new virtual host status.dns.example.net on our main interface of dns.example.net. We’re using a reverse proxy to to send all requests coming from the outside to our internal PowerDNS web server on port 8081. Also, we’re forcing a text/html content type in the proxy request filter, because otherwise we would just get text/plain and we would simply see the source code, which is probably not what you want. Let’s enable the site for a test:

  1. a2ensite status.dns.example.net
  2. /etc/init.d/apache2 reload

If you point your web browser to status.dns.example.net you should now see the internal status page of PowerDNS. If you set a password above, you will see a password dialogue. This is the password dialogue sent by the PowerDNS web server. The user name is admin and the password is your password. Try it out now, because we’ll get rid of this in a minute.

For security reasons, you probably want to use Apache 2 for authentication. E.g. you might want use a SSL connection and authenticate your co-workers using the internal LDAP server of your company intranet. You might even stay with the insecure basic authentication method, but use other user names. This is entirely up to you and beyond the scope of this post. Consult the Apache 2 documentation on how to do this. What you probably don’t want to do, however, is to authenticate twice (first your secure authentication method and then PowerDNS basic authentication. Luckily, we can configure the Apache 2 proxy to do the authentication for us. This is a bit tricky, though.
To authenticate at the PowerDNS server, Apache 2 needs to send an additional Authorization header line to the PowerDNS server with every request it handles. We use the RequestHeader directive to override any existing Authorization header with our own authentication data. Add the following lines just before the end of the virtual host container.

  1. <Location />
  2. RequestHeader Set Authorization "Basic YWRtaW46UG93ZXJETlM="
  3. </Location>

The above example works only for our example password, which is PowerDNS. Try it for a test. This is, because part of the header value is encrypted using the base64 algorithm. You need to change the encrypted part YWRtaW46UG93ZXJETlM=. In plain text the encrypted string would read admin:PowerDNS, where admin is the user name and PowerDNS is the password. To use your own password, you need to encrypt the string admin:yourownpassword using the base64 algorithm and replace our example string. Be sure to keep the Basic and the space. It is crucial for success, that you’ve got the right encrypted string. There are a number of online tools, to encode and decode these strings. To ensure, that you’ve got the correct encryption method, encode the example string and compare it to the string above for a reference. If you’ve got the wrong string, it will not work.

Restart the Apache 2 server. To clear your password cache, restart your browser. Now surf to the site again. You will see, that the password dialogue is gone. Now, don’t forget to secure the page again using Apache 2. Under any circumstances, do not use Directory containers in the configuration. These will not apply to the proxy, because the proxy is not a physical directory on your server. Use Location containers like we did above for setting the RequestHeader directive. Also, you could still use insecure basic authentication to secure the page, if you wanted. It would work regardless of the RequestHeader magic.

A light-weight alternative

For those of you, who think that Apache is too heavy, here is an example for the nginx web server:

  1. vi /etc/nginx/sites-available/status.dns.example.net
  2. server {
  3. listen 192.168.0.1:80;
  4. server_name status.dns.example.net;
  5. root /var/www/nginx-default;
  6. location / {
  7. index index.html
  8. proxy_pass http://localhost:8081;
  9. proxy_redirect off;
  10. proxy_set_header Authorization "Basic YWRtaW46UG93ZXJETlM=";
  11. }
  12. }

Checking mail quota during SMTP transaction in Exim

Monday, August 31st, 2009

Introduction

The Exim mail server comes with out of the box quota support. All you need to do is to set the quota, quota_warn_threshold and quota_warn_message options on your transport and you will be fine (see Chapter 26 of the Exim Specification). Combine this with a few custom database lookups and you’ve got a great way to handle your user’s mailbox quota. By utilizing some additional options and the power of regular expressions you could even define different quotas for each folder of your user’s IMAP mailboxes.

The problem

Using Exim‘s quota management has a major drawback, though: Quota checking takes place after the message has been accepted for delivery. What does that mean in practise?

If the quota limit of a mailbox is exceeded, messages for this mailbox are still accepted for delivery, but deferred temporarily. At each queue run, Exim checks, if the quota limit is still exceeded. If the user has deleted mails from her mailbox since the last queue run, the mail will be delivered. If not, the mail will be deferred again. At some point, Exim will give up and finally bounce the mail back to the sender. How long Exim will try to deliver the message depends on your retry rules. While this is good for your user, because she has some more time to clear up her mailbox before she effectively looses mail, it is bad in a number of ways:

  • The deferred mails add up to the queue and clutter it up.
  • If the user does not delete messages from her mailbox (e.g. she is no longer using it actively), messages will sit in your queue for a long time, before they are finally bounced back to the sender. During this time, the sender does not know about the status of the mail. You need to provide disk space for these mails. If you set up quota limits to better manage the disk space at your disposal, this may hit you hard.
  • If your user gets a lot of spam from faked sender addresses, you may generate a lot of collateral spam by bouncing her messages back to the faked sender addresses.

A possible solution

Exim were not Exim, if there wasn’t some way to get around this limitation and check quota right at RCPT time during the SMTP transaction. Actually, there are a number of ways to implement this. And all have pros and cons. I’d like to show you a way that is relatively easy to implement, but does have some minor issues.

Basically, the idea is to set up a database of those users that are over quota. You’d use an external script to periodically check, if a user exceeded her quota limit, and manage the database accordingly. You could use a flat file to store mailbox names of these users or – if you’re already using a relational database (e.g. MySQL, PostgreSQL) or an LDAP directory for mailbox management – add an additional property (e.g. isOverQuota) to your mailbox object.
Additionally, you need to configure Exim to check our file or database during the SMTP transaction and reject an incoming message, if it is for a mailbox that is over quota. How to do this depends a bit on your current configuration. Chances are, you’re already verifying the recipient of a message before you accept it (check your ACLs for require verify recipient). In this case, the fastest way to implement this in Exim is to add an additional router somewhere before (probably immediately before) the router that handles routing of messages to your user’s mailboxes (the router with the quota option). If you used a MySQL database it might look something like this:

  1. virtual_mailbox_overquota:
  2. driver = redirect
  3. domains = +local_domains
  4. condition = ${if eqi{$local_part} {${lookup mysql {SELECT username FROM exim.mailbox WHERE username='$local_part' AND domainname='$domain' AND enabled='1' AND overquota='1' LIMIT 1}}}{yes}{no}}
  5. data = :fail: Mailbox is full, quota limit exceeded
  6. allow_fail

Note: You’ll probably want to change the domains and condition options before you use this router in your configuration. But you get the idea, don’t you?

By default Exim calculates quota by adding the size of the new message to the size of the mailbox and comparing this to the quota limit of the mailbox (inclusive checking). This means that a user can’t go over quota. The mailbox will always remain below the quota limit. This is great, but it doesn’t work for us. Our script depends on users going over quota, because otherwise it would not detect the mailboxes that are over quota. Again, there are a number of possible solutions to this problem. You could increase the quota limit of every mailbox by 1% and check for mailboxes at 99%. Alternatively, you could disable inclusive quota checking. To do so, add the following option to the router, which handles routing of messages to your user’s mailboxes (the router with the quota option):

  1. quota_is_inclusive = false

Testing the new configuration

To test, if the new configuration works, you might use Exim‘s basic address deliverability checking.

  1. $exim -C exim.new.conf -f sender@example.org -bt not.over.quota@example.org
  2. not.over.quota@example.org
  3. router = virtual_mailbox, transport = appendfile
  4. $exim -C exim.new.conf -f sender@example.org -bt exceeded.quota@example.org
  5. exceeded.quota@example.org is undeliverable: Mailbox is full, mailbox quota exceeded

If you get anything close to the above, you’re probably okay. Of course you need to have a mailbox that exceeded its quota limit for this test to work.

Some issues

There are two minor problems with this solution. Depending on your situation, these might be acceptable or not.

First, there is the checking interval. If a user exceeded her quota limit, mail to her mailbox wouldn’t be blocked at SMTP time until the checking script’s next run. This is not such a big problem, because the message won’t be delivered anyway. Instead it would be deferred and bounced back later (see above). Unfortunately, it’s the same the other way around. If a user deleted messages from her mailbox, this would not be recognized by Exim before the checking script’s next run. Mail would be rejected at SMTP time, although there might actually be enough space in the mailbox. This is a much bigger problem. Generally spoken, the interval between the runs of your checking script should be as low as at all possible. But don’t burn your mail server on the way ;).

Second there is the exclusive quota checking. Because we depend on the mailbox actually going over quota, we need to disable inclusive checking or – alternatively – increase quota limits and flag mailboxes as over quota at 99% or less.
If we do the latter, there is always the possibility, that the quota of a mailbox isn’t above the checking threshold and there are new messages coming in, which are too large for the mailbox. These messages would be queued. Consider an example: A user get’s 30 messages of 1 GB, but his mailbox is only 500 MB. He gets no small messages in between, so our checking script will not flag the mailbox in subsequent runs and the size of the queue will rise a lot. This is especially a problem, because this probably affects mostly messages that are quite large. Also, if you can’t check fast enough and get a lot of small messages, there might be a lot of mailboxes that go way above the checking threshold before you can flag them. If you have a very large user base, this might be a problem. A run of the script probably takes longer, if you have a lot of mailboxes, so this adds to the problem.
If we disable inclusive checking, the last message that brings a mailbox over quota will always be delivered. Depending on the message this might be okay (in case of small messages) or really bad (in case of very large messages). But: The mailbox will be flagged at the next run of our checking script, so the queue will remain relatively clear compared to our scenario above.
If you do not have a lot of users and your resources are limited, you might not like this option. If you’ve got a large user base, exclusive checking is probably better.

Of course, there are other solutions, that do not have these issues. But they are a lot harder to implement and to maintain.

PowerDNS on Debian Lenny, Sarge or Woody (2)

Thursday, April 23rd, 2009

This post is the second part of a workshop on the PowerDNS DNS server. In the first part of this workshop, we set up the PowerDNS server and it’s MySQL database backend. In this part of the workshop, I will show you how to add some domain data to your database and how to synchronize this data to other PowerDNS servers automatically by using the so-called supermaster concept of PowerDNS. I will also show you how to enable AXFR transfers to ensure compatibility with remote Bind slave servers.

Prerequisites

For the rest of this workshop it is assumed that you’ve got two PowerDNS installations on two separate physical hosts with the IP addresses 192.168.0.1 and 192.168.1.2, respectively. The first server has the host name dns.example.net and the second server has the host name dns2.example.net.

Configure Master and Slave

You probably want one DNS server to act as the master server and a number of additional servers to act as slaves. PowerDNS supports multiple master servers for one domain and it can be slave and master at the same time.

We will now configure dns.example.net as the master and dns2.example.net as the slave. To do this, add master to the configuration file of the first server and slave to the configuration file of the second server.

In a traditional setup, you would configure a domain name both on the master and the slave server. Additionally, you would specify record data for the domain name on the master server. The slave would then query the master server and download the record data. Whenever you change the record data on the master server, it would notify the slave server and the slave server would refresh the record data by downloading the new record data from the master server. In fact, you can do this with PowerDNS and it will work just fine. But PowerDNS can do more: If you tell your slave(s), that your master is a so-called supermaster, you do not need to setup new domain names on the slave. All you need to do is to setup the new domain name and its record data on the master server. If the domain has a SOA record and your slave is listed as one of the authoritative name servers for the domain, the master will notify your slave of the new domain name. Your slave recognizes, that your master is a supermaster server and adds the domain name to its configuration automatically. It works like magic.

To tell your slave, that your master server is a supermaster server, execute the following SQL statement on your slave’s MySQL server.

  1. INSERT INTO pdns.supermasters (`ip`, `nameserver`, `account`) VALUES ('192.168.0.1', 'dns2.example.net', 'test');

This tells your slave, that your PowerDNS server with the IP address 192.168.0.1 is a supermaster server. As you will see later, PowerDNS supports the concept of accounts. These do not have any influence on the operation of PowerDNS. You can specify account information for each domain name, which might be useful, if you want to find all domain names of a certain customer. If PowerDNS sets up a new domain name of a supermaster, it uses account information specified in the supermasters table for that supermaster server. In our case, all domains of the supermaster server dns.example.net will be added with the account information test. Note, that the nameserver column of the supermasters table is not the host name of the supermaster server. It is the host name of the slave server. Before the slave adds a new domain name from our supermaster server, it checks that the host name you specify in the the nameserver column is listed as one of the authoritative DNS servers for that domain name. Thus, we need to add the name of our slave there.

Enable AXFR

To exchange domain name and record data, your PowerDNS servers will use a method that is called AXFR. For security reasons, AXFR is disabled by default. Edit the configuration files of your PowerDNS master server:

  1. allow-axfr-ips=192.168.2.1
  2. disable-axfr=no

This will enable AXFR for the IP address of your slave server. Do not allow AXFR for any server. This would pose a security risk. If you’ve got multiple slave servers, add all the IP addresses of the slave servers separated by a comma. AXFR is compatible with other name server software (e.g. Bind).

Running PowerDNS

PowerDNS should be in a state now, where it can start and connect to the MySQL database with no problems. For now, we will start PowerDNS in monitoring mode. That way we can follow everything that happens live on the console. To start up PowerDNS in monitoring mode, use the following command.

  1. /etc/init.d/pdns monitor

Later, you may want to run PowerDNS in the background. To do this just run the following command.

  1. /etc/init.d/pdns start

Once you have started up both the master and the slave PowerDNS server we can start adding domain names.

Adding your first domain name

To add a new domain name, you need to add a new entry to the domains table of your database. To do so, execute the following SQL query on the master server:

  1. INSERT INTO `pdns.domains` (`id`, `name`, `type`, `account`) VALUES (1, 'example.com', 'MASTER', 'test');

This will add the example.com domain name to the database. The type MASTER indicates, that our master server will be the master for this domain. The account name is optional, it is for your information only. It is important to note, that the id of our new domain name is 1. We’ll need this in a second.

Next, we need to add some records, i.e. some zone data, to the database for PowerDNS to serve. The records need to be added to the records table of the database. PowerDNS supports a number of different records. Depending on the type of the record, you need to provide different values for the type, content and prio columns. Check the PowerDNS documentation on supported record types and their storage to get an idea of what you need. For your convenience, I’ll give you a few examples of common record types.

The first thing you want to add is a SOA record. A typical SOA record would look like this:

  1. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`) VALUES (1, 'example.com','SOA', 'dns.example.net. hostmaster.example.net. 2009042301 10800 3600 604800 3600', 3600);

Note the domain_id, which is set to the id value of our example.com domain. Then there is the fully qualified domain name, that this record is valid for. You are probably familiar with the values of the type, content and ttl columns. If not, check the documentation.

Next, let’s add a few name servers. It is important that your master and all your slaves are listed here, of course.

  1. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`) VALUES (1, 'example.com','NS', 'dns.example.net', 3600);
  2. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`) VALUES (1, 'example.com','NS', 'dns2.example.net', 3600);

You probably want some A records:

  1. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`) VALUES (1, 'example.com','A', '192.168.0.3', 3600);
  2. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`) VALUES (1, 'sales.example.com','A', '192.168.0.4', 3600);
  3. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`) VALUES (1, '*.example.com','A', '192.168.0.2', 3600);

The first of these records is for the example.com domain name. The second is for a subdomain, i.e. sales.example.com. The third A record is special. It is a wildcard record. It matches any subdomain, that is not already specified, i.e. everything except example.com and sales.example.com.

Let’s finish by adding some MX records:

  1. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`, `prio`) VALUES (1, 'example.com', 'MX','mail.example.net', 3600, 10);
  2. INSERT INTO `pdns.records` (`domain_id`, `name`, `type`, `content`, `ttl`, `prio`) VALUES (1, 'example.com', 'MX','mailbackup.example.net', 3600, 20);

Within a short time you will see the following information showing up in the log file of your slave server. Don’t be impatient, it takes some time. You should see something within a minute, though.

  1. Feb 07 21:43:26 Received NOTIFY for example.com from 192.168.0.1 for which we are not authoritative
  2. Feb 07 21:43:26 Created new slave zone 'example.com' from supermaster 192.168.0.1, queued axfr
  3. Feb 07 21:43:27 gmysql Connection succesful
  4. Feb 07 21:43:27 No serial for 'example.com' found - zone is missing?
  5. Feb 07 21:43:27 AXFR started for 'example.com', transaction started
  6. Feb 07 21:43:27 AXFR done for 'example.com', zone committed

And the equivalent on the master server:

  1. Feb 7 21:43:20 1 domain for which we are master needs notifications
  2. Feb 7 21:43:20 Queued notification of domain 'example.com' to 192.168.1.2
  3. Feb 7 21:43:21 AXFR of domain 'example.com' initiated by 192.168.1.2
  4. Feb 7 21:43:21 Removed from notification list: 'example.com' to 192.168.1.2 (was acknowledged)
  5. Feb 7 21:43:21 gmysql Connection succesful
  6. Feb 7 21:43:21 AXFR of domain ‘example.com’ to 192.168.1.2 finished
  7. Feb 7 21:43:23 No master domains need notifications

This looks like a successful AXFR of the example.com domain name. Let’s see, if the domain was really added to the MySQL database on the slave server:

  1. mysql> SELECT * FROM pdns.domains WHERE name='example.com';
  2. +----+--------------+---------------+------------+-------+-----------------+---------+
  3. | id | name | master | last_check | type | notified_serial | account |
  4. +----+--------------+---------------+------------+-------+-----------------+---------+
  5. | 1 | example.com | 192.168.0.2 | 1234039407 | SLAVE | NULL | test |
  6. +----+--------------+---------------+------------+-------+-----------------+---------+
  7. 1 row in set (0.00 sec)

Quite cool, isn’t it? You can also query the records table. All your records will be there. Every time you add a new record and raise the serial number, PowerDNS will notify your slave and the record data will be updated automatically.

Adding slave domains

Sometimes, you might want to add a slave domain to one of your servers manually. Suppose your master server acts as a slave server for the domain example.org. Say, the master server of the example.org domain is a remote Bind server with the IP address 192.168.2.34. First, make sure that AXFR with that server is allowed (see above). Then, execute the following SQL query on your server.

  1. INSERT INTO `pdns.domains` (`id`, `name`, `master`, `type`, `account`) VALUES (2, 'example.org', '192.168.2.34', 'SLAVE', 'test');

VoilĂ . That’s it. PowerDNS will take care of the rest.

Limitations of the supermaster mode

Unfortunately, supermaster mode is not equivalent to database synchronization. Automatic set up of new domain names and record data works like a charm. What doesn’t work, though, is the automatic deletion of domain names. If you delete your domain name on the master server, you need to delete it on all slaves manually.

PowerDNS on Debian Lenny, Sarge or Woody (1) [Update3]

Saturday, June 4th, 2005

PowerDNS is an extremly powerful DNS server. It supports a number of relational database backends, loadbalancing and failover algorithms.

Deploying PowerDNS on all our servers has a few major advantages over our current Bind based setup.

  • PowerDNS has native MySQL database backend support and is therefore easier to integrate with our customer control panel. Also changes to the MySQL database are discovered automatically, eliminating the need for periodical restarts. All changes are applied almost instantly.
  • PowerDNS can act as a so-called supermaster, synchronizing itsself automatically with slave servers. Zone transfers occur even without adding a domain name to all of the slaves beforehand. It is enough if the domain is added to the database of the master server.

I will now describe how to setup PowerDNS on a Linux box running Debian Woody or Debian Sarge or Debian Lenny and utilizing the MySQL database backend.

Initial Setup

Official Debian packages for PowerDNS are availlable since Debian Sarge, but you can download Woody packages from http://www.powerdns.com/downloads/, if you use Woody. There is no apt repository availlable, however.

After downloading the most recent package with wget, you can install it on your system with dpkg.

  1. wget http://downloads.powerdns.com/releases/deb/stable/pdns-static_2.9.17-1_i386.deb
  2. dpkg -i pdns-static_2.9.17-1*

If you’ve got a Sarge or newer installation of Debian just run apt-get as follows and the necessary packages will be installed automatically.

  1. apt-get install pdns-recursor pdns-backend-mysql

You’ll notice that PowerDNS will not start up directly after installing the package. That’s because the standard configuration file does not come preconfigured for a specific backend; we have to configure PowerDNS for a backend of our choice first. See below for a sample output of a fresh PowerDNS installation.

  1. /etc/init.d/pdns monitor
  2. Jun 04 10:13:56 Unable to launch, no backends configured for querying.

Gerneal Settings

Before we will begin to configure the database backend, we’ll have a look at some basic settings first. If you use Sarge, you can skip this part. You will be asked for the information below during configuration of the package. If you use Lenny or Woody, you need to specify an IP address and a port, to which PowerDNS should bind. You should specify an alternate port for non-production use. That allows you to run PowerDNS in parallel to an existing nameserver installation during the initial setup period.

On Woddy and Sarge, edit /etc/powerdns/pdns.conf directly. In Lenny, do edit the file /etc/powerdns/pdns.d/pdns.local file instead. This way it will be easier to update the package later.

  1. vi /etc/powerdns/pdns.d/pdns.local
  2. local-address=192.168.0.2
  3. local-port=5300

If you want to bind PowerDNS to multiple IP addresses change the local-address setting as follows.

  1. local-address=192.168.0.2,192.168.0.3

If your server has multiple IP addresses configured, you should also set the IP address, which PowerDNS will use as the source address when sending out answers to queries from clients or remote servers.

  1. query-local-address=192.168.0.2

MySQL

Before we start to configure PowerDNS to use the MySQL backend, we need to setup the required database and tables.

  1. CREATE DATABASE pdns;
  2. USE pdns;
  3. CREATE TABLE domains (
  4. id INT auto_increment,
  5. name VARCHAR(255) NOT NULL,
  6. master VARCHAR(20) DEFAULT NULL,
  7. last_check INT DEFAULT NULL,
  8. type VARCHAR(6) NOT NULL,
  9. notified_serial INT DEFAULT NULL,
  10. account VARCHAR(40) DEFAULT NULL,
  11. primary key (id)
  12. )type=InnoDB;
  13. CREATE UNIQUE INDEX name_index ON domains(name);
  14. CREATE TABLE records (
  15. id INT auto_increment,
  16. domain_id INT DEFAULT NULL,
  17. name VARCHAR(255) DEFAULT NULL,
  18. type VARCHAR(6) DEFAULT NULL,
  19. content VARCHAR(255) DEFAULT NULL,
  20. ttl INT DEFAULT NULL,
  21. prio INT DEFAULT NULL,
  22. change_date INT DEFAULT NULL,
  23. primary key(id)
  24. )type=InnoDB;
  25. CREATE INDEX rec_name_index ON records(name);
  26. CREATE INDEX nametype_index ON records(name,type);
  27. CREATE INDEX domain_id ON records(domain_id);
  28. CREATE TABLE supermasters (
  29. ip VARCHAR(25) NOT NULL,
  30. nameserver VARCHAR(255) NOT NULL,
  31. account VARCHAR(40) DEFAULT NULL
  32. );

We also have to create a new MySQL user account exclusively for PowerDNS and need to set the rights accordingly.

  1. GRANT USAGE ON *.* TO `pdns`@localhost IDENTIFIED BY "********"
  2. WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0
  3. MAX_UPDATES_PER_HOUR 0;
  4. GRANT ALL PRIVILEGES ON `pdns`.`domains` TO `pdns`@localhost;
  5. GRANT ALL PRIVILEGES ON `pdns`.`records` TO `pdns`@localhost;
  6. GRANT SELECT ON `pdns`.`supermasters`
  7. TO `pdns`@localhost;

The neccessary database is now setup and the empty tables await our data. Because PowerDNS is unable to connect using sockets, we have to modify the MySQL configuration and make it bind itsself to localhost.

  1. vi /etc/mysql/my.cnf
  2. #skip-networking
  3. bind-address = 127.0.0.1

All we need to do now is to make another change to the PowerDNS configuration.

  1. vi /etc/powerdns/pdns.d/pdns.local
  2. launch=gmysql
  3. gmysql-host=127.0.0.1
  4. gmysql-user=pdns
  5. gmysql-password=********
  6. gmysql-dbname=pdns

Let's have a little test of the installation.

  1. /etc/init.d/pdns monitor
  2. Jun 04 16:46:34 gmysql Connection successful

Perfect. PowerDNS is setup successfully. The initial setup is completed. See you later for part two of this workshop, in which we will insert some random domain data into our new database and setup a typical superserver/slave framework.

Update1: Recently Debian 3.1 Sarge was released. I changed this tutorial a bit to accommodate some differences between Woody and Sarge.

Update2: Actually, PowerDNS is perfectly able to use MySQL sockets, if you omit the gmysql-host option. Lesson learned. Post changed.

Update3: Debian Lenny will be released shortly. I changed this tutorial a bit to accommodate some differences between Woody, Sarge and Lenny.