From 536a5f5d9d8fc54b69387ab806bcf36d70b1487e Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 15 Nov 2014 22:42:13 +0100 Subject: [PATCH] add secondary nameserver, prefer it for queries, add migration, fixes #175 --- conftest.py | 3 +++ nsupdate/main/dnstools.py | 9 ++++--- nsupdate/main/forms.py | 4 +-- .../migrations/0003_auto_20141115_2230.py | 26 +++++++++++++++++++ nsupdate/main/models.py | 7 ++++- 5 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 nsupdate/main/migrations/0003_auto_20141115_2230.py diff --git a/conftest.py b/conftest.py index 0be7adc..995a32c 100644 --- a/conftest.py +++ b/conftest.py @@ -20,6 +20,7 @@ TEST_SECRET2 = "somethingelse" RELATED_HOST_NAME = 'rh' TEST_HOST_RELATED = FQDN(RELATED_HOST_NAME + '.' + TEST_HOST.host, TEST_HOST.domain) NAMESERVER_IP = "85.10.192.104" +NAMESERVER2_IP = NAMESERVER_IP # use same server as tests query shortly after update, too quick for secondary NAMESERVER_UPDATE_ALGORITHM = "HMAC_SHA512" # no problem, you can ONLY update the TESTDOMAIN with this secret, nothing else: NAMESERVER_UPDATE_SECRET = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==" @@ -75,6 +76,7 @@ def db_init(db): # note: db is a predefined fixture and required here to have t dt = Domain.objects.create( name=TESTDOMAIN, # special: test-domain update secret! nameserver_ip=NAMESERVER_IP, + nameserver2_ip=NAMESERVER2_IP, nameserver_update_algorithm=NAMESERVER_UPDATE_ALGORITHM, nameserver_update_secret=NAMESERVER_UPDATE_SECRET, public=NAMESERVER_PUBLIC, @@ -84,6 +86,7 @@ def db_init(db): # note: db is a predefined fixture and required here to have t d = Domain.objects.create( name=BASEDOMAIN, nameserver_ip=NAMESERVER_IP, + nameserver2_ip=NAMESERVER2_IP, nameserver_update_algorithm=NAMESERVER_UPDATE_ALGORITHM, nameserver_update_secret='invalid=', # we don't send updates there (and the real key is really secret) public=NAMESERVER_PUBLIC, diff --git a/nsupdate/main/dnstools.py b/nsupdate/main/dnstools.py index ede1ad4..45b4769 100644 --- a/nsupdate/main/dnstools.py +++ b/nsupdate/main/dnstools.py @@ -197,11 +197,14 @@ def query_ns(fqdn, rdtype): :raises: see dns.resolver.Resolver.query """ assert isinstance(fqdn, FQDN) - nameserver, origin = get_ns_info(fqdn)[0:2] + nameserver, nameserver2, origin = get_ns_info(fqdn)[0:3] resolver = dns.resolver.Resolver(configure=False) # we do not configure it from resolv.conf, but patch in the values we # want into the documented attributes: resolver.nameservers = [nameserver, ] + if nameserver2: + # if we have a secondary ns, prefer it for queries + resolver.nameservers.insert(0, nameserver2) resolver.search = [] resolver.lifetime = RESOLVER_TIMEOUT # as we query directly the (authoritative) master dns, we do not desire @@ -269,7 +272,7 @@ def get_ns_info(fqdn): # retry timeout is over, set it available again set_ns_availability(domain, True) algorithm = getattr(dns.tsig, d.nameserver_update_algorithm) - return d.nameserver_ip, fqdn.domain, domain, fqdn.host, domain, d.nameserver_update_secret, algorithm + return d.nameserver_ip, d.nameserver2_ip, fqdn.domain, domain, fqdn.host, domain, d.nameserver_update_secret, algorithm def update_ns(fqdn, rdtype='A', ipaddr=None, action='upd', ttl=60): @@ -286,7 +289,7 @@ def update_ns(fqdn, rdtype='A', ipaddr=None, action='upd', ttl=60): """ assert isinstance(fqdn, FQDN) assert action in ['add', 'del', 'upd', ] - nameserver, origin, domain, name, keyname, key, algo = get_ns_info(fqdn) + nameserver, nameserver2, origin, domain, name, keyname, key, algo = get_ns_info(fqdn) upd = dns.update.Update(origin, keyring=dns.tsigkeyring.from_text({keyname: key}), keyalgorithm=algo) diff --git a/nsupdate/main/forms.py b/nsupdate/main/forms.py index eb75da2..420c1a2 100644 --- a/nsupdate/main/forms.py +++ b/nsupdate/main/forms.py @@ -44,7 +44,7 @@ class EditRelatedHostForm(forms.ModelForm): class CreateDomainForm(forms.ModelForm): class Meta(object): model = Domain - fields = ['name', 'nameserver_ip', 'nameserver_update_algorithm', + fields = ['name', 'nameserver_ip', 'nameserver2_ip', 'nameserver_update_algorithm', 'public', 'available', 'comment'] widgets = { 'name': forms.widgets.TextInput(attrs=dict(autofocus=None)), @@ -54,7 +54,7 @@ class CreateDomainForm(forms.ModelForm): class EditDomainForm(forms.ModelForm): class Meta(object): model = Domain - fields = ['comment', 'nameserver_ip', 'public', 'available', + fields = ['comment', 'nameserver_ip', 'nameserver2_ip', 'public', 'available', 'nameserver_update_algorithm', 'nameserver_update_secret'] diff --git a/nsupdate/main/migrations/0003_auto_20141115_2230.py b/nsupdate/main/migrations/0003_auto_20141115_2230.py new file mode 100644 index 0000000..f87437e --- /dev/null +++ b/nsupdate/main/migrations/0003_auto_20141115_2230.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0002_auto_20141115_2227'), + ] + + operations = [ + migrations.AddField( + model_name='domain', + name='nameserver2_ip', + field=models.GenericIPAddressField(help_text='IP of additional nameserver (used for queries)', null=True, verbose_name='nameserver IP (secondary)', blank=True), + preserve_default=True, + ), + migrations.AlterField( + model_name='domain', + name='nameserver_ip', + field=models.GenericIPAddressField(help_text='IP where the dynamic DNS updates for this zone will be sent to', verbose_name='nameserver IP (primary)'), + preserve_default=True, + ), + ] diff --git a/nsupdate/main/models.py b/nsupdate/main/models.py index 8c24372..8da8cd3 100644 --- a/nsupdate/main/models.py +++ b/nsupdate/main/models.py @@ -83,9 +83,14 @@ class Domain(models.Model): unique=True, help_text=_("Name of the zone where dynamic hosts may get added")) nameserver_ip = models.GenericIPAddressField( - _("nameserver IP"), + _("nameserver IP (primary)"), max_length=40, # ipv6 = 8 * 4 digits + 7 colons help_text=_("IP where the dynamic DNS updates for this zone will be sent to")) + nameserver2_ip = models.GenericIPAddressField( + _("nameserver IP (secondary)"), + max_length=40, # ipv6 = 8 * 4 digits + 7 colons + blank=True, null=True, + help_text=_("IP of additional nameserver (used for queries)")) nameserver_update_secret = models.CharField( _("nameserver update secret"), max_length=88, # 512 bits base64 -> 88 bytes