Commit be7e32df authored by Federico Ceratto's avatar Federico Ceratto Committed by Federico Ceratto

Add djbdns backend

Add docs and basic tests
Update config sample file and support matrix
Change-Id: I709cea4e321f6bbee3b0f9f718fa6a9836af3ca5
parent 6ae19233
#! /bin/bash
### BEGIN INIT INFO
# Provides: tinydns
# Required-Start: $local_fs $remote_fs $network
# Required-Stop: $local_fs $remote_fs $network
# Should-Start: $syslog
# Should-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: tinydns daemon processes
# Description: Start the TinyDNS resolver
### END INIT INFO
# Documentation
# man tinydns
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
. /lib/lsb/init-functions
NAME=tinydns
DAEMON=/usr/bin/$NAME
DAEMON_USER=djbdns
DESC="the tinydns daemon"
ROOTDIR=/var/lib/djbdns
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LAUNCHER=/usr/bin/envuidgid
LAUNCHER_ARGS="$DAEMON_USER envdir ./env softlimit -d300000 $DAEMON"
PIDFILE=/run/$NAME.pid
# Exit if executable is not installed
[ -x "$DAEMON" ] || exit 0
set -x
case "$1" in
start)
if [ ! -d "$ROOTDIR" ]; then
log_action_msg "Not starting $DESC: $ROOTDIR is missing."
exit 0
fi
log_action_begin_msg "Starting $DESC"
if start-stop-daemon --stop --signal 0 --quiet --pidfile $PIDFILE --exec $DAEMON; then
log_action_end_msg 0 "already running"
else
if start-stop-daemon --start --verbose --make-pidfile --chdir $ROOTDIR --pidfile $PIDFILE --exec $LAUNCHER -- $LAUNCHER_ARGS
then
log_action_end_msg 0
else
log_action_end_msg 1
exit 1
fi
fi
;;
stop)
log_action_begin_msg "Stopping $DESC"
pid=$(cat $PIDFILE 2>/dev/null) || true
if test ! -f $PIDFILE -o -z "$pid"; then
log_action_end_msg 0 "not running - there is no $PIDFILE"
exit 0
fi
if start-stop-daemon --stop --signal INT --quiet --pidfile $PIDFILE --exec $DAEMON; then
rm -f $PIDFILE
elif kill -0 $pid 2>/dev/null; then
log_action_end_msg 1 "Is $pid not $NAME? Is $DAEMON a different binary now?"
exit 1
else
log_action_end_msg 1 "$DAEMON died: process $pid not running; or permission denied"
exit 1
fi
;;
reload)
echo "Not implemented, use restart"
exit 1
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
if test ! -r $(dirname $PIDFILE); then
log_failure_msg "cannot read PID file $PIDFILE"
exit 4
fi
pid=$(cat $PIDFILE 2>/dev/null) || true
if test ! -f $PIDFILE -o -z "$pid"; then
log_failure_msg "$NAME is not running"
exit 3
fi
if ps "$pid" >/dev/null 2>&1; then
log_success_msg "$NAME is running"
exit 0
else
log_failure_msg "$NAME is not running"
exit 1
fi
;;
*)
log_action_msg "Usage: $0 {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0
#
# Replace /var/lib/djbdns if needed
#
[Unit]
Description=tinydns DNS resolver
Documentation=man:tinydns
Documentation=https://cr.yp.to/djbdns.html
After=network.target
Requires=network.target
Wants=network.target
ConditionPathExists=/var/lib/djbdns
[Service]
Type=forking
PIDFile=/run/tinydns.pid
Environment="ROOT=/var/lib/djbdns"
ExecStart=/usr/bin/tinydns
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry=TERM/5/KILL/5 --pidfile /run/tinydns.pid
TimeoutStopSec=30
KillMode=mixed
PermissionsStartOnly=true
Restart=on-abnormal
RestartSec=2s
LimitNOFILE=65536
WorkingDirectory=/var/lib/djbdns
User=$ug_name
Group=$ug_name
# Hardening
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_CHOWN CAP_FOWNER
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
# TODO: restrict ReadOnlyDirectories
ReadOnlyDirectories=/
ReadWriteDirectories=-/var/lib/djbdns
[Install]
WantedBy=multi-user.target
......@@ -47,7 +47,7 @@ OPTS = [
cfg.ListOpt('masters', default=[],
help='List of masters for the Agent, format ip:port'),
cfg.StrOpt('backend-driver', default='bind9',
help='The backend driver to use: bind9 or knot2'),
help='The backend driver to use, e.g. bind9, djbdns, knot2'),
cfg.StrOpt('transfer-source',
help='An IP address to be used to fetch zones transferred in'),
cfg.FloatOpt('notify-delay', default=0.0,
......
This diff is collapsed.
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Test the Djbdns agent backend
These tests *do* rely on creating directories and files or running
executables from the djbdns suite
If djbdns is not available some tests are skipped.
"""
import os
import tempfile
import unittest
import fixtures
import mock
from designate import exceptions
from designate.backend.agent_backend.impl_djbdns import DjbdnsBackend
from designate.tests import TestCase
import designate.backend.agent_backend.impl_djbdns
TINYDNSDATA_PATH = '/usr/bin/tinydns-data'
class DjbdnsAgentBackendSimpleTestCase(TestCase):
def test__check_dirs(self):
DjbdnsBackend._check_dirs('/tmp')
def test__check_dirs_not_found(self):
self.assertRaises(
exceptions.Backend,
DjbdnsBackend._check_dirs,
'/nonexistent_dir_name'
)
class DjbdnsAgentBackendTestCase(TestCase):
def setUp(self):
super(DjbdnsAgentBackendTestCase, self).setUp()
self.CONF.set_override('masters', ('127.0.0.1:5354',), 'service:agent')
tmp_datafiles_dir = tempfile.mkdtemp()
os.mkdir(os.path.join(tmp_datafiles_dir, 'datafiles'))
self.CONF.set_override(
'tinydns_datadir',
tmp_datafiles_dir,
designate.backend.agent_backend.impl_djbdns.CFG_GROUP
)
self.useFixture(fixtures.MockPatchObject(
DjbdnsBackend, '_check_dirs'
))
self.backend = DjbdnsBackend('foo')
self.patch_ob(self.backend._resolver, 'query')
def tearDown(self):
super(DjbdnsAgentBackendTestCase, self).tearDown()
def patch_ob(self, *a, **kw):
self.useFixture(fixtures.MockPatchObject(*a, **kw))
@mock.patch('designate.backend.agent_backend.impl_djbdns.os.remove')
@mock.patch('designate.backend.agent_backend.impl_djbdns.execute')
def test__perform_axfr_from_minidns(self, mock_exe, mock_rm):
mock_exe.return_value = (None, None)
self.backend._perform_axfr_from_minidns('foo')
mock_exe.assert_called_once_with(
'tcpclient', '127.0.0.1', '5354', 'axfr-get', 'foo',
os.path.join(self.backend._datafiles_dir, 'foo.zonedata'),
os.path.join(self.backend._datafiles_dir, 'foo.ztmp')
)
def test_delete_zone_no_file(self):
self.patch_ob(self.backend, '_rebuild_data_cdb')
# Should not raise exceptions
self.backend.delete_zone('non_existent_zone_file')
@unittest.skipIf(not os.path.isfile(TINYDNSDATA_PATH),
"tinydns-data not installed")
def test__rebuild_data_cdb_empty(self):
# Check that tinydns-data can be run and the required files are
# generated / renamed as needed
self.CONF.set_override('root_helper', ' ') # disable rootwrap
self.backend._tinydns_cdb_filename = tempfile.mkstemp()[1]
self.backend._rebuild_data_cdb()
assert os.path.isfile(self.backend._tinydns_cdb_filename)
os.remove(self.backend._tinydns_cdb_filename)
@unittest.skipIf(not os.path.isfile(TINYDNSDATA_PATH),
"tinydns-data not installed")
def test__rebuild_data_cdb(self):
# Check that tinydns-data can be run and the required files are
# generated / renamed as needed
self.CONF.set_override('root_helper', ' ') # disable rootwrap
self.backend._tinydns_cdb_filename = tempfile.mkstemp()[1]
fn = os.path.join(self.backend._datafiles_dir, 'example.org.zonedata')
with open(fn, 'w') as f:
f.write(""".example.org::ns1.example.org
+ns1.example.org:127.0.0.1
+www.example.org:127.0.0.1
""")
self.backend._rebuild_data_cdb()
assert os.path.isfile(self.backend._tinydns_cdb_filename)
os.remove(self.backend._tinydns_cdb_filename)
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
#
# Author: Federico Ceratto <federico.ceratto@hpe.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit-test the Djbdns agent backend
These tests do not rely on creating directories and files or running
executables from the djbdns suite
"""
import dns.zone
import fixtures
import mock
from designate import exceptions
from designate.backend.agent_backend.impl_djbdns import DjbdnsBackend
from designate.tests import TestCase
import designate.backend.agent_backend.impl_djbdns # noqa
class DjbdnsAgentBackendUnitTestCase(TestCase):
def setUp(self):
super(DjbdnsAgentBackendUnitTestCase, self).setUp()
self.CONF.set_override('masters', ('127.0.0.1:5354',), 'service:agent')
self.useFixture(fixtures.MockPatchObject(
DjbdnsBackend, '_check_dirs'
))
self.backend = DjbdnsBackend('foo')
self.patch_ob(self.backend._resolver, 'query')
def tearDown(self):
super(DjbdnsAgentBackendUnitTestCase, self).tearDown()
def _create_dnspy_zone(self, name):
zone_text = (
'$ORIGIN %(name)s\n%(name)s 3600 IN SOA %(ns)s '
'email.email.com. 1421777854 3600 600 86400 3600\n%(name)s '
'3600 IN NS %(ns)s\n') % {'name': name, 'ns': 'ns1.designate.com'}
return dns.zone.from_text(zone_text, check_origin=False)
def patch_ob(self, *a, **kw):
self.useFixture(fixtures.MockPatchObject(*a, **kw))
def test_init(self):
self.assertTrue(hasattr(self.backend, '_resolver'))
self.assertEqual(1, self.backend._resolver.timeout)
self.assertEqual(1, self.backend._resolver.lifetime)
self.assertEqual(['127.0.0.1'], self.backend._resolver.nameservers)
self.assertEqual('/var/lib/djbdns/root/data.cdb',
self.backend._tinydns_cdb_filename)
self.assertEqual('/var/lib/djbdns/datafiles',
self.backend._datafiles_dir)
self.assertEqual('/var/lib/djbdns/datafiles/%s.zonedata',
self.backend._datafiles_path_tpl)
self.assertEqual([('127.0.0.1', 5354)], self.backend._masters)
def test_find_zone_serial(self):
class Data(object):
serial = 3
self.backend._resolver.query.return_value = [Data(), ]
serial = self.backend.find_zone_serial('example.com')
self.assertEqual(3, serial)
def test_find_zone_serial_error(self):
self.backend._resolver.query.side_effect = RuntimeError('foo')
serial = self.backend.find_zone_serial('example.com')
self.assertEqual(None, serial)
@mock.patch('designate.backend.agent_backend.impl_djbdns.execute')
def test_create_zone(self, mock_exe):
self.patch_ob(self.backend, '_perform_axfr_from_minidns')
self.patch_ob(self.backend, '_rebuild_data_cdb')
zone = self._create_dnspy_zone('example.org')
self.backend.create_zone(zone)
def test_update_zone(self):
self.patch_ob(self.backend, '_perform_axfr_from_minidns')
self.patch_ob(self.backend, '_rebuild_data_cdb')
zone = self._create_dnspy_zone('example.org')
self.backend.update_zone(zone)
@mock.patch('designate.backend.agent_backend.impl_djbdns.os.remove')
def test_delete_zone(self, mock_rm):
self.patch_ob(self.backend, '_rebuild_data_cdb')
self.backend.delete_zone('foo')
mock_rm.assert_called_once_with(
'/var/lib/djbdns/datafiles/foo.zonedata'
)
@mock.patch('designate.backend.agent_backend.impl_djbdns.os.remove')
def test_exception_filter(self, *mocks):
self.patch_ob(self.backend, '_rebuild_data_cdb')
self.assertRaises(
exceptions.Backend,
self.backend.delete_zone,
None
)
@mock.patch('designate.backend.agent_backend.impl_djbdns.os.remove')
def test_exception_filter_pass_through(self, mock_rm):
self.patch_ob(self.backend, '_rebuild_data_cdb')
mock_rm.side_effect = exceptions.Backend
self.assertRaises(
exceptions.Backend,
self.backend.delete_zone,
'foo'
)
......@@ -87,3 +87,12 @@ Agent Backend KnotDNS
:undoc-members:
:show-inheritance:
Agent Backend Djbdns
====================
.. automodule:: designate.backend.agent_backend.impl_djbdns
:members:
:special-members:
:private-members:
:undoc-members:
:show-inheritance:
..
Copyright 2016 Hewlett Packard Enterprise Development Company LP
Author: Federico Ceratto <federico.ceratto@hpe.com>
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Djbdns Agent backend
********************
User documentation
==================
This page documents the Agent backend for `djbdns <https://cr.yp.to/djbdns.html>`_.
The agent runs on the same host as the `tinydns <https://cr.yp.to/djbdns/tinydns.html>`_ resolver.
It receives DNS messages from Mini DNS using private DNS OPCODEs and classes and creates or deletes
zones in the data.cdb file using `axfr-get <https://cr.yp.to/djbdns/axfr-get.html>`_ and
`tinydns-data <https://cr.yp.to/djbdns/tinydns-data.html>`_
Setting up Djbdns on Ubuntu Trusty
------------------------------------
Assuming no DNS resolver is already installed, run as root:
.. code-block:: bash
set -u
datadir=/var/lib/djbdns
ug_name=djbdns
tinydns_ipaddr=127.0.0.1
[[ -d $datadir ]] && echo "$datadir already exists" && exit 1
set -e
apt-get update
apt-get install dbndns daemontools
if ! getent passwd $ug_name >/dev/null; then
adduser --quiet --system --group --no-create-home --home /nonexistent $ug_name
fi
tinydns-conf $ug_name $ug_name $datadir $tinydns_ipaddr
cd $datadir/root
tinydns-data data
chown -Rv $ug_name:$ug_name $datadir
Setup the a Systemd service or, alternatively, an initfile to start TinyDNS.
In the contrib/djbdns directory there are example files for both.
.. code-block:: bash
systemctl daemon-reload
service tinydns start
service tinydns status
If needed, create the rootwrap filters, as root:
.. code-block:: bash
cat > /etc/designate/rootwrap.d/djbdns.filters <<EOF
# cmd-name: filter-name, raw-command, user, args
[Filters]
tcpclient: CommandFilter, /usr/bin/tcpclient, root
axfr-get: CommandFilter, /usr/bin/axfr-get, root
EOF
# Check the filter:
sudo /usr/local/bin/designate-rootwrap /etc/designate/rootwrap.conf tcpclient -h
sudo /usr/local/bin/designate-rootwrap /etc/designate/rootwrap.conf axfr-get -h
Configure the "service.agent" and "backend.agent.djbdns" sections in /etc/designate/designate.conf
Look in designate.conf.example for examples.
Create an agent pool:
.. code-block:: bash
# Fetch the existing pool(s) if needed or start from scratch
designate-manage pool generate_file --file /tmp/pool.yaml
# Edit the file (see below) and reload it as:
designate-manage pool update --file /tmp/pool.yaml
The "targets" section in pool.yaml should look like:
.. code-block:: ini
targets:
- description: gdnsd agent
masters:
- host: <MiniDNS IP addr>
port: 5354
options: {}
options:
- host: <Agent IP addr>
port: 5358
type: agent
Testing
^^^^^^^
Create new zones and records. Monitor the agent logfile and the contents of the
TinyDNS datadir. The data.cdb file should be receiving updates.
.. code-block:: bash
openstack zone create --email example@example.org example.org.
openstack recordset create example.org. --type A foo --records 1.2.3.4
dig example.org @<tinydns_ipaddr> SOA
dig foo.example.org @<tinydns_ipaddr> A
Developer documentation
=======================
Devstack testbed
----------------
Follow "Setting up Djbdns on Ubuntu Trusty"
Configure Tinydns to do AXFR from MiniDNS on 192.168.121.131
......@@ -54,6 +54,7 @@ backend-impl-agent=Agent
backend-impl-bind9-agent=Bind9 (Agent)
backend-impl-denominator=Denominator
backend-impl-knot2-agent=Knot2 (Agent)
backend-impl-djbdns-agent=Djbdns (Agent)
[backends.backend-impl-bind9]
......@@ -79,6 +80,9 @@ type=agent
[backends.backend-impl-knot2-agent]
type=agent
[backends.backend-impl-djbdns-agent]
type=agent
[backends.backend-impl-infoblox-xfr]
status=release-compatible
maintainers=Infoblox OpenStack Team <openstack-maintainer@infoblox.com>
......
......@@ -465,6 +465,15 @@ debug = False
# knotc command name when rootwrap is used. Location of the knotc executable
# on the resolver host if rootwrap is not used
#knotc_cmd_name = /usr/sbin/knotc
#
[backend:agent:djbdns]
# Command names when rootwrap is used or location of the executables
# on the resolver host when rootwrap is not used
# tcpclient_cmd_name =
# axfr_get_cmd_name =
# tinydns_data_cmd_name =
# tinydns_datadir =
#query_destination = 127.0.0.1
[backend:agent:denominator]
#name = dynect
......
[Filters]
tcpclient: CommandFilter, /usr/bin/tcpclient, root
axfr-get: CommandFilter, /usr/bin/axfr-get, root
tinydns-data: CommandFilter, /usr/bin/tinydns-data, root
---
features:
- An experimental agent backend to support TinyDNS, the DNS resolver
from the djbdns tools.
......@@ -92,6 +92,7 @@ designate.backend =
designate.backend.agent_backend =
bind9 = designate.backend.agent_backend.impl_bind9:Bind9Backend
knot2 = designate.backend.agent_backend.impl_knot2:Knot2Backend
djbdns = designate.backend.agent_backend.impl_djbdns:DjbdnsBackend
denominator = designate.backend.agent_backend.impl_denominator:DenominatorBackend
fake = designate.backend.agent_backend.impl_fake:FakeBackend
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment