nameserver update key / secret terminology cleanup

key = secret + algorithm
This commit is contained in:
Thomas Waldmann 2013-11-24 05:04:07 +01:00
parent 972a411ef4
commit dd09b6b5af
6 changed files with 113 additions and 17 deletions

View File

@ -14,8 +14,8 @@ TEST_HOST2 = 'test2.' + BASEDOMAIN
TEST_SECRET2 = "somethingelse"
NAMESERVER_IP = "85.10.192.104"
NAMESERVER_UPDATE_ALGORITHM = "HMAC_SHA512"
# no problem, you can ONLY update the TEST_HOST with this key, nothing else:
NAMESERVER_UPDATE_KEY = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=="
# no problem, you can ONLY update the TEST_HOST with this secret, nothing else:
NAMESERVER_UPDATE_SECRET = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=="
NAMESERVER_PUBLIC = True
USERNAME = 'test'
@ -43,7 +43,7 @@ def db_init(db): # note: db is a predefined fixture and required here to have t
domain=TEST_HOST, # special: single-host update secret!
nameserver_ip=NAMESERVER_IP,
nameserver_update_algorithm=NAMESERVER_UPDATE_ALGORITHM,
nameserver_update_key=NAMESERVER_UPDATE_KEY,
nameserver_update_secret=NAMESERVER_UPDATE_SECRET,
public=NAMESERVER_PUBLIC,
created_by=u,
)
@ -52,7 +52,7 @@ def db_init(db): # note: db is a predefined fixture and required here to have t
domain=BASEDOMAIN,
nameserver_ip=NAMESERVER_IP,
nameserver_update_algorithm=NAMESERVER_UPDATE_ALGORITHM,
nameserver_update_key='invalid=', # we don't send updates there (and the real key is really secret)
nameserver_update_secret='invalid=', # we don't send updates there (and the real key is really secret)
public=NAMESERVER_PUBLIC,
created_by=u2,
)

View File

@ -86,21 +86,21 @@ If you lose the secret, you'll have to generate a new one and change it in your
update client also.
Nameserver Update Key (backend, RFC 2136)
-----------------------------------------
Nameserver Update Secret (backend, RFC 2136)
--------------------------------------------
We currently store this key (which is basically a base64 encoded shared secret,
We currently store this secret (which is basically a base64 encoded shared secret,
one per dynamic zone) "as is" into the database ("Domain" records there).
This is somehow critical, but also hard to do better - encryption would only
help very little here as we would need to decrypt the update key before using it,
help very little here as we would need to decrypt the update secret before using it,
so we would need the unlocked decryption key on the same machine.
Make sure no unauthorized person gets that key or he/she will be able to update
Make sure no unauthorized person gets that secret or he/she will be able to update
ANY record in the respective zone / nameserver directly (without going over
nsupdate.info software / service).
We support creating random update keys, so you don't need an extra tool for this.
We support creating a random update secret, so you don't need an extra tool for this.
CSRF protection

View File

@ -203,12 +203,12 @@ def parse_name(fqdn, origin=None):
def get_ns_info(fqdn, origin=None):
"""
Get the master nameserver for the <origin> zone, the key needed
Get the master nameserver for the <origin> zone, the key secret needed
to update the zone and the key algorithm used.
:param fqdn: the fully qualified hostname we are dealing with (str)
:param origin: zone we are dealing with, must be with trailing dot (default:autodetect) (str)
:return: master nameserver, origin, domain, update keyname, update key, update algo
:return: master nameserver, origin, domain, update keyname, update secret, update algo
:raises: NameServerNotAvailable if ns was flagged unavailable in the db
"""
fqdn_str = str(fqdn)
@ -238,7 +238,7 @@ def get_ns_info(fqdn, origin=None):
# 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, origin, domain, name, keyname, d.nameserver_update_key, algorithm
return d.nameserver_ip, origin, domain, name, keyname, d.nameserver_update_secret, algorithm
def update_ns(fqdn, rdtype='A', ipaddr=None, origin=None, action='upd', ttl=60):

View File

@ -33,4 +33,4 @@ class EditDomainForm(forms.ModelForm):
class Meta(object):
model = Domain
fields = ['comment', 'nameserver_ip', 'public', 'available',
'nameserver_update_algorithm', 'nameserver_update_key']
'nameserver_update_algorithm', 'nameserver_update_secret']

View File

@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# renaming field 'Domain.nameserver_update_key'
db.rename_column(u'main_domain', 'nameserver_update_key', 'nameserver_update_secret')
def backwards(self, orm):
# renaming field 'Domain.nameserver_update_secret'
db.rename_column(u'main_domain', 'nameserver_update_secret', 'nameserver_update_key')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'main.blacklisteddomain': {
'Meta': {'object_name': 'BlacklistedDomain'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'main.domain': {
'Meta': {'object_name': 'Domain'},
'available': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'domain': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'nameserver_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
'nameserver_update_algorithm': ('django.db.models.fields.CharField', [], {'default': "'HMAC_SHA512'", 'max_length': '16'}),
'nameserver_update_secret': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '88'}),
'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
u'main.host': {
'Meta': {'unique_together': "(('subdomain', 'domain'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': u"orm['auth.User']"}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'last_update_ipv4': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_update_ipv6': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'ssl_update_ipv4': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'ssl_update_ipv6': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'subdomain': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'update_secret': ('django.db.models.fields.CharField', [], {'max_length': '64'})
}
}
complete_apps = ['main']

View File

@ -60,8 +60,9 @@ class Domain(models.Model):
nameserver_ip = models.GenericIPAddressField(
max_length=40, # ipv6 = 8 * 4 digits + 7 colons
help_text="IP where the dynamic DNS updates for this zone will be sent to")
nameserver_update_key = models.CharField(
nameserver_update_secret = models.CharField(
max_length=88, # 512 bits base64 -> 88 bytes
default='',
help_text="Shared secret that allows updating this zone (base64 encoded)")
nameserver_update_algorithm = models.CharField(
max_length=16, # see elements of UPDATE_ALGORITHM_CHOICES
@ -94,9 +95,9 @@ class Domain(models.Model):
algorithm = self.nameserver_update_algorithm
bitlength = UPDATE_ALGORITHMS[algorithm].bitlength
secret = User.objects.make_random_password(length=bitlength / 8)
self.nameserver_update_key = key = base64.b64encode(secret)
self.nameserver_update_secret = secret_base64 = base64.b64encode(secret)
self.save()
return key
return secret_base64
def get_bind9_algorithm(self):
return UPDATE_ALGORITHMS.get(self.nameserver_update_algorithm).bind_name