End-to-end working version of adding forward/reverse A record from forward zone form.

This commit is contained in:
Jeffrey Forman 2011-11-24 11:57:13 -05:00
parent 77da4b8f82
commit 00f8036991
10 changed files with 157 additions and 78 deletions

View File

@ -2,10 +2,18 @@ from django import forms
from bcommon.models import BindServer, Key from bcommon.models import BindServer, Key
RECORD_TYPE_CHOICES = (("A", "A"), ("AAAA", "AAAA"))
TTL_CHOICES = ((300, "5 minutes"),
(1800, "30 minutes"),
(3600, "1 hour"),
(43200, "12 hours"),
(86400, "1 day"))
class FormAddRecord(forms.Form): class FormAddRecord(forms.Form):
dns_hostname = forms.CharField(max_length=100) dns_server = forms.CharField(max_length=100, label="Hostname of DNS Server", widget=forms.TextInput(attrs={'readonly':'readonly'}))
rr_domain = forms.CharField(max_length=100) name = forms.CharField(max_length=100, label="Record Name (FQDN)")
rr_name = forms.CharField(max_length=256) record_type = forms.ChoiceField(choices=RECORD_TYPE_CHOICES, label="Record Type")
rr_type = forms.ChoiceField(choices=(("A", "A"), ("MX", "MX"), ("CNAME", "CNAME"), ("AAAA", "AAAA"))) ttl = forms.ChoiceField(choices=TTL_CHOICES, label="TTL", initial=86400)
rr_data = forms.CharField(max_length=256) create_reverse = forms.BooleanField(label="Create Reverse Record (PTR)?", required=False)
tsig_key = forms.ModelChoiceField(queryset=Key.objects.all(), empty_label=None) data = forms.CharField(max_length=256, label="Record Data (IP/Hostname)")
key_name = forms.ModelChoiceField(queryset=Key.objects.all(), empty_label=None, label="TSIG Key")

View File

@ -1,12 +1,13 @@
import urllib2 from bcommon.keyutils import create_keyring
from BeautifulSoup import BeautifulStoneSoup as BS
import re
from bcommon.models import Key, BindServer import re
import dns.query import dns.query
import dns.reversename
import dns.update
def list_zone_records(dns_hostname, zone_name): def list_zone_records(dns_hostname, zone_name):
""" Take a DNS zone, and list all the records it contains. """ """Take a DNS server and a zone name,
and return an array of its records."""
# Need to move most of this logic into a helper method. # Need to move most of this logic into a helper method.
try: try:
zone = dns.zone.from_xfr(dns.query.xfr(dns_hostname, zone_name)) zone = dns.zone.from_xfr(dns.query.xfr(dns_hostname, zone_name))
@ -31,16 +32,48 @@ def list_zone_records(dns_hostname, zone_name):
'rr_data' : split_record.split(" ")[4]}) 'rr_data' : split_record.split(" ")[4]})
return record_array return record_array
def add_record(clean_data): def add_forward_record(form_data, zone_keyring):
key_name = Key.objects.get(name=(clean_data['tsig_key'])).name """Take in data from FormAddRecord and a keyring object,
key_data = Key.objects.get(name=(clean_data['tsig_key'])).data return a response from the DNS server about adding the record."""
key_algorithm = Key.objects.get(name=(clean_data['tsig_key'])).algorithm
keyring = dns.tsigkeyring.from_text({ key_name : key_data })
dns_update = dns.update.Update(clean_data['rr_domain'], keyring = keyring, keyalgorithm=key_algorithm)
dns_update.replace(str(clean_data['rr_name']), 86400, str(clean_data['rr_type']), str(clean_data['rr_data']))
try:
response = dns.query.tcp(dns_update, clean_data['dns_hostname'])
except dns.tsig.PeerBadKey:
return {'errors' : "There was a problem adding your record due to a TSIG key issue. Please resolve that first." }
return {'response' : response } re_form_data = re.search(r"(\w+).(.*)", form_data["name"])
hostname = re_form_data.group(1)
domain = re_form_data.group(2)
dns_update = dns.update.Update(domain, keyring = zone_keyring)
dns_update.replace(hostname, int(form_data["ttl"]), str(form_data["record_type"]), str(form_data["data"]))
try:
response = dns.query.tcp(dns_update, form_data["dns_server"])
except dns.tsig.BadPeerKey:
response = "There was a problem adding your forward record due to a TSIG key issue."
return response
def add_reverse_record(form_data, zone_keyring):
reverse_ip_fqdn = str(dns.reversename.from_address(form_data["data"]))
reverse_ip = re.search(r"([0-9]+).(.*).$", reverse_ip_fqdn).group(1)
reverse_domain = re.search(r"([0-9]+).(.*).$", reverse_ip_fqdn).group(2)
dns_update = dns.update.Update(reverse_domain, keyring = zone_keyring)
dns_update.replace(reverse_ip, int(form_data["ttl"]), "PTR", str(form_data["name"]) + ".")
response = dns.query.tcp(dns_update, form_data["dns_server"])
return response
def add_record(form_data, key_dict):
"""Add a DNS record with data from a FormAddRecord object.
If a reverse PTR record is requested, this will be added too."""
keyring = create_keyring(key_dict)
response = {}
forward_response = add_forward_record(form_data, keyring)
response["forward_response"] = forward_response
if form_data["create_reverse"]:
reverse_response = add_reverse_record(form_data, keyring)
response["reverse_response"] = reverse_response
return response

13
binder/bcommon/keyutils.py Executable file
View File

@ -0,0 +1,13 @@
import dns.tsigkeyring
import re
import sys
def create_keyring(key_dict):
"""Accept a TSIG keyfile and a key name to retrieve.
Return a keyring object with the key name and TSIG secret."""
keyring = dns.tsigkeyring.from_text({
key_dict.name : key_dict.data
})
return keyring

View File

@ -3,17 +3,11 @@
from bcommon.models import BindServer, Key from bcommon.models import BindServer, Key
from django.template import Context from django.template import Context
from django.shortcuts import render_to_response, redirect from django.shortcuts import render_to_response, redirect
from bcommon.helpers import list_server_zones, list_zone_records, add_record from bcommon.helpers import list_zone_records, add_record #, delete_record
from bcommon.forms import FormAddRecord from bcommon.forms import FormAddRecord
from django.template import RequestContext from django.template import RequestContext
import dns.update
import dns.tsigkeyring
import socket
def home_index(request): def home_index(request):
return render_to_response('index.htm') return render_to_response('index.htm')
@ -55,37 +49,40 @@ def view_zone_records(request, dns_hostname, zone_name):
'rr_domain' : zone_name}, 'rr_domain' : zone_name},
context_instance=RequestContext(request)) context_instance=RequestContext(request))
def view_add_record(request, dns_hostname, zone_name): def view_add_record(request, dns_server, zone):
""" View to provide form to add a DNS record. """ """ View to provide form to add a DNS record. """
form = FormAddRecord(initial={ 'dns_hostname' : dns_hostname, form = FormAddRecord(initial={ 'dns_server' : dns_server,
'rr_domain' : zone_name }) 'zone' : zone })
return render_to_response('bcommon/add_record_form.htm', return render_to_response('bcommon/add_record_form.htm',
{ 'form' : form }, { 'form' : form },
context_instance=RequestContext(request)) context_instance=RequestContext(request))
def add_record_result(request): def view_add_record_result(request):
""" Process the input given to add a DNS record. """ """ Process the input given to add a DNS record. """
if request.method == "GET": if request.method == "GET":
# Return home. You shouldn't be accessing the result # Return home. You shouldn't be accessing this url via a GET.
# via a GET.
return redirect('/') return redirect('/')
# We got a POST to add the result.
form = FormAddRecord(request.POST) form = FormAddRecord(request.POST)
if form.is_valid(): if form.is_valid():
cd = form.cleaned_data cd = form.cleaned_data
response = add_record(cd)
if 'errors' in response:
return render_to_response('bcommon/add_record_result.htm',
{ 'errors' : response['errors'] },
context_instance=RequestContext(request))
else: else:
return render_to_response('bcommon/add_record_result.htm', form = FormAddRecord(request.POST)
{ 'response' : response['response'] }, return render_to_response('bcommon/add_record_form.htm',
{ 'form' : form },
context_instance=RequestContext(request)) context_instance=RequestContext(request))
def view_delete_record(request):
key_dict = Key.objects.get(name=cd["key_name"])
add_record_response = add_record(cd, key_dict)
return render_to_response('bcommon/add_record_result.htm',
{ 'response' : add_record_response },
context_instance=RequestContext(request))
### WORK ON BELOW
def confirm_delete_record(request):
if request.method == "GET": if request.method == "GET":
# Return home. You shouldn't trying to directly acces # Return home. You shouldn't trying to directly acces
# the url for deleting records. # the url for deleting records.
@ -95,20 +92,33 @@ def view_delete_record(request):
rr_domain = request.POST['rr_domain'] rr_domain = request.POST['rr_domain']
rr_array = request.POST.getlist('rr_array') rr_array = request.POST.getlist('rr_array')
if request.POST['delete_step'] == "initial": ## TODO(jforman): We need to handle the case where the POST data
""" We need to confirm they really want to delete the items. """ ## is somehow bad.
return render_to_response('bcommon/delete_record_initial.htm', return render_to_response('bcommon/delete_record_initial.htm',
{ 'rr_server' : rr_server, { 'rr_server' : rr_server,
'rr_domain' : rr_domain, 'rr_domain' : rr_domain,
'rr_array' : rr_array }, 'rr_array' : rr_array,
'tsig_keys' : Key.objects.all() },
context_instance=RequestContext(request)) context_instance=RequestContext(request))
if request.POST['delete_step'] == "finalize":
# TODO: Instrument
""" Time to actually delete the records requested """
pass
# If we hit a case where we don't know what's going on. # If we hit a case where we don't know what's going on.
return render_to_response('bcommon/index.htm', # return render_to_response('bcommon/index.htm',
{ 'errors' : "We hit an unhandled exception in deleting your requested records." }, # { 'errors' : "We hit an unhandled exception in deleting your requested records." },
context_instance=RequestContext(request)) # context_instance=RequestContext(request))
def delete_result(request):
if request.method == "GET":
# Return home. You shouldn't trying to directly acces
# the url for deleting records.
return redirect('/')
to_delete_array = {}
to_delete_array['rr_server'] = request.POST['rr_server']
to_delete_array['rr_domain'] = request.POST['rr_domain']
to_delete_array['rr_array'] = eval(request.POST.getlist('rr_array')[0])
for current in to_delete_array['rr_array']:
print "current: %s" % current
to_delete_array['key_name'] = request.POST['key_name']
to_delete_array['key_data'] = Key.objects.get(name=(to_delete_array['key_name'])).data
delete_result = delete_record(to_delete_array)

View File

@ -6,10 +6,19 @@
{% endblock pageheader %} {% endblock pageheader %}
{% block body %} {% block body %}
{% if not errors %} {% if response.forward_response %}
Result: Forward Record Creation Output
<pre> <pre>
{{ response }} {{ response.forward_response }}
</pre> </pre>
{% endif %} {% endif %}
<p>
{% if response.reverse_response %}
Reverse Record Creation Output
<pre>
{{ response.reverse_response }}
</pre>
{% endif %}
{% endblock body %} {% endblock body %}

View File

@ -2,18 +2,23 @@
{% block body %} {% block body %}
<form action="/delete_record/" method="POST">{% csrf_token %} <form action="/delete_record/result/" method="POST">{% csrf_token %}
<input type="hidden" name="rr_server" value="{{ rr_server }}"> <input type="hidden" name="rr_server" value="{{ rr_server }}">
<input type="hidden" name="rr_domain" value="{{ rr_domain }}"> <input type="hidden" name="rr_domain" value="{{ rr_domain }}">
<input type="hidden" name="rr_array" value="{{ rr_array }}"> <input type="hidden" name="rr_array" value="{{ rr_array }}">
<input type="hidden" name="delete_step" value="finalize"> <!-- <input type="hidden" name="delete_step" value="finalize"> -->
Do you really want to delete the following records? Do you really want to delete the following records?
<ul> <ul>
<li>Server: {{ rr_server }}</li> <li>Server: {{ rr_server }}</li>
<li>Domain: {{ rr_domain }}</li> <li>Domain: {{ rr_domain }}</li>
<li>Records: {{ rr_array }}</li> <li>Records: {% for current_rr in rr_array %} {{ current_rr}} {% endfor %}</li>
<li>Key: <select name="key_name">
{% for current_key in tsig_keys %}
<option value="{{current_key}}">{{current_key}}</option>
{% endfor %}
</select> </li>
</ul> </ul>
<input type="submit" value="Yes, I really want to really them."/> <input type="submit" value="Yes, I really want to delete them."/>
</form> </form>
{% endblock body %} {% endblock body %}

View File

@ -2,7 +2,7 @@
{% block body %} {% block body %}
{% if not errors %} {% if not errors %}
DNS Server Zone List: DNS Server Zone List for {{ dns_hostname }}:
<ul> <ul>
{% for current_zone in zone_array %} {% for current_zone in zone_array %}

View File

@ -15,7 +15,6 @@
<form action="/delete_record/" method="post">{% csrf_token %} <form action="/delete_record/" method="post">{% csrf_token %}
<input type="hidden" name="rr_server" value="{{ rr_server }}"> <input type="hidden" name="rr_server" value="{{ rr_server }}">
<input type="hidden" name="rr_domain" value="{{ rr_domain }}"> <input type="hidden" name="rr_domain" value="{{ rr_domain }}">
<input type="hidden" name="delete_step" value="initial">
{% for current_record in record_array %} {% for current_record in record_array %}
<tr> <tr>
<td><input type="checkbox" name="rr_array" value="{{ current_record.rr_name }}"></td> <td><input type="checkbox" name="rr_array" value="{{ current_record.rr_name }}"></td>

View File

@ -2,8 +2,6 @@
{% block body %} {% block body %}
<ul> <a href="/info/">Server List</a>
<li><a href="/info/">Server List</a></li>
</ul>
{% endblock body %} {% endblock body %}

View File

@ -9,11 +9,15 @@ urlpatterns = patterns('',
(r'^admin/', include(admin.site.urls)), (r'^admin/', include(admin.site.urls)),
(r'^$', 'bcommon.views.home_index'), (r'^$', 'bcommon.views.home_index'),
(r'^info/$', 'bcommon.views.list_servers'), (r'^info/$', 'bcommon.views.list_servers'),
(r'^info/(?P<dns_hostname>[a-zA-Z0-9.-]+)/$', 'bcommon.views.view_server_zones'), (r'^info/(?P<dns_hostname>[a-zA-Z0-9.-]+)/$', 'bcommon.views.view_server_zones'),
(r'^info/(?P<dns_hostname>[a-zA-Z0-9.-]+)/(?P<zone_name>[a-zA-Z0-9.-]+)/$', 'bcommon.views.view_zone_records'), (r'^info/(?P<dns_hostname>[a-zA-Z0-9.-]+)/(?P<zone_name>[a-zA-Z0-9.-]+)/$', 'bcommon.views.view_zone_records'),
(r'^add_record/(?P<dns_hostname>[a-zA-Z0-9.-]+)/(?P<zone_name>[a-zA-Z0-9.-]+)/$', 'bcommon.views.view_add_record'),
(r'^add_record/result/$', 'bcommon.views.add_record_result'), (r'^add_record/(?P<dns_server>[a-zA-Z0-9.-]+)/(?P<zone>[a-zA-Z0-9.-]+)/$', 'bcommon.views.view_add_record'),
(r'^delete_record/$', 'bcommon.views.view_delete_record'), (r'^add_record/result/$', 'bcommon.views.view_add_record_result'),
# (r'^delete_record/$', 'bcommon.views.view_delete_record'),
# (r'^delete_record/result/$', 'bcommon.views.view_delete_result'),
) )
if settings.DEBUG: if settings.DEBUG: