nameserver update key / secret terminology cleanup
key = secret + algorithm
This commit is contained in:
parent
972a411ef4
commit
dd09b6b5af
@ -14,8 +14,8 @@ TEST_HOST2 = 'test2.' + BASEDOMAIN
|
|||||||
TEST_SECRET2 = "somethingelse"
|
TEST_SECRET2 = "somethingelse"
|
||||||
NAMESERVER_IP = "85.10.192.104"
|
NAMESERVER_IP = "85.10.192.104"
|
||||||
NAMESERVER_UPDATE_ALGORITHM = "HMAC_SHA512"
|
NAMESERVER_UPDATE_ALGORITHM = "HMAC_SHA512"
|
||||||
# no problem, you can ONLY update the TEST_HOST with this key, nothing else:
|
# no problem, you can ONLY update the TEST_HOST with this secret, nothing else:
|
||||||
NAMESERVER_UPDATE_KEY = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=="
|
NAMESERVER_UPDATE_SECRET = "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ=="
|
||||||
NAMESERVER_PUBLIC = True
|
NAMESERVER_PUBLIC = True
|
||||||
|
|
||||||
USERNAME = 'test'
|
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!
|
domain=TEST_HOST, # special: single-host update secret!
|
||||||
nameserver_ip=NAMESERVER_IP,
|
nameserver_ip=NAMESERVER_IP,
|
||||||
nameserver_update_algorithm=NAMESERVER_UPDATE_ALGORITHM,
|
nameserver_update_algorithm=NAMESERVER_UPDATE_ALGORITHM,
|
||||||
nameserver_update_key=NAMESERVER_UPDATE_KEY,
|
nameserver_update_secret=NAMESERVER_UPDATE_SECRET,
|
||||||
public=NAMESERVER_PUBLIC,
|
public=NAMESERVER_PUBLIC,
|
||||||
created_by=u,
|
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,
|
domain=BASEDOMAIN,
|
||||||
nameserver_ip=NAMESERVER_IP,
|
nameserver_ip=NAMESERVER_IP,
|
||||||
nameserver_update_algorithm=NAMESERVER_UPDATE_ALGORITHM,
|
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,
|
public=NAMESERVER_PUBLIC,
|
||||||
created_by=u2,
|
created_by=u2,
|
||||||
)
|
)
|
||||||
|
@ -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.
|
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).
|
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
|
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.
|
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
|
ANY record in the respective zone / nameserver directly (without going over
|
||||||
nsupdate.info software / service).
|
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
|
CSRF protection
|
||||||
|
@ -203,12 +203,12 @@ def parse_name(fqdn, origin=None):
|
|||||||
|
|
||||||
def get_ns_info(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.
|
to update the zone and the key algorithm used.
|
||||||
|
|
||||||
:param fqdn: the fully qualified hostname we are dealing with (str)
|
: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)
|
: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
|
:raises: NameServerNotAvailable if ns was flagged unavailable in the db
|
||||||
"""
|
"""
|
||||||
fqdn_str = str(fqdn)
|
fqdn_str = str(fqdn)
|
||||||
@ -238,7 +238,7 @@ def get_ns_info(fqdn, origin=None):
|
|||||||
# retry timeout is over, set it available again
|
# retry timeout is over, set it available again
|
||||||
set_ns_availability(domain, True)
|
set_ns_availability(domain, True)
|
||||||
algorithm = getattr(dns.tsig, d.nameserver_update_algorithm)
|
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):
|
def update_ns(fqdn, rdtype='A', ipaddr=None, origin=None, action='upd', ttl=60):
|
||||||
|
@ -33,4 +33,4 @@ class EditDomainForm(forms.ModelForm):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
model = Domain
|
model = Domain
|
||||||
fields = ['comment', 'nameserver_ip', 'public', 'available',
|
fields = ['comment', 'nameserver_ip', 'public', 'available',
|
||||||
'nameserver_update_algorithm', 'nameserver_update_key']
|
'nameserver_update_algorithm', 'nameserver_update_secret']
|
||||||
|
@ -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']
|
@ -60,8 +60,9 @@ class Domain(models.Model):
|
|||||||
nameserver_ip = models.GenericIPAddressField(
|
nameserver_ip = models.GenericIPAddressField(
|
||||||
max_length=40, # ipv6 = 8 * 4 digits + 7 colons
|
max_length=40, # ipv6 = 8 * 4 digits + 7 colons
|
||||||
help_text="IP where the dynamic DNS updates for this zone will be sent to")
|
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
|
max_length=88, # 512 bits base64 -> 88 bytes
|
||||||
|
default='',
|
||||||
help_text="Shared secret that allows updating this zone (base64 encoded)")
|
help_text="Shared secret that allows updating this zone (base64 encoded)")
|
||||||
nameserver_update_algorithm = models.CharField(
|
nameserver_update_algorithm = models.CharField(
|
||||||
max_length=16, # see elements of UPDATE_ALGORITHM_CHOICES
|
max_length=16, # see elements of UPDATE_ALGORITHM_CHOICES
|
||||||
@ -94,9 +95,9 @@ class Domain(models.Model):
|
|||||||
algorithm = self.nameserver_update_algorithm
|
algorithm = self.nameserver_update_algorithm
|
||||||
bitlength = UPDATE_ALGORITHMS[algorithm].bitlength
|
bitlength = UPDATE_ALGORITHMS[algorithm].bitlength
|
||||||
secret = User.objects.make_random_password(length=bitlength / 8)
|
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()
|
self.save()
|
||||||
return key
|
return secret_base64
|
||||||
|
|
||||||
def get_bind9_algorithm(self):
|
def get_bind9_algorithm(self):
|
||||||
return UPDATE_ALGORITHMS.get(self.nameserver_update_algorithm).bind_name
|
return UPDATE_ALGORITHMS.get(self.nameserver_update_algorithm).bind_name
|
||||||
|
Loading…
x
Reference in New Issue
Block a user