Merge branch 'master' of github.com:asmaps/nsupdate.info

This commit is contained in:
Arne Schauf 2013-09-29 21:20:38 +02:00
commit 39605becdb
9 changed files with 202 additions and 7 deletions

View File

@ -0,0 +1,20 @@
$ORIGIN .
$TTL 3600 ; 1 hour
nsupdate.info IN SOA ns1.thinkmo.de. root.thinkmo.de. (
2013093117 ; serial
7200 ; refresh (2 hours)
1800 ; retry (30 minutes)
604800 ; expire (1 week)
60 ; minimum (1 minute)
)
NS ns1.thinkmo.de.
NS ns3.thinkmo.de.
A 178.32.221.14
AAAA 2001:41d0:8:e00e::1
MX 10 mx.thinkmo.de.
$ORIGIN nsupdate.info.
$TTL 3600 ; 1 hour
www A 178.32.221.14
AAAA 2001:41d0:8:e00e::1
ipv4 A 178.32.221.14
ipv6 AAAA 2001:41d0:8:e00e::1

View File

@ -0,0 +1,18 @@
// configuration snippet for bind 9 nameserver (put it into /etc/bind9/named.conf )
key "nsupdate.info." {
// must be same algorithm as in the nsupdate/nsupdate/settings.py
algorithm hmac-sha512;
// the secret is just a shared secret in base64-encoding, you don't need
// to use a special tool to create it. Some random in base64 encoding should
// be OK.
secret "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYQ==";
};
zone nsupdate.info {
type master;
// bind9 needs write permissions into that directory and into that file:
file "/etc/bind/zones/nsupdate.info";
// everyone who has that key may update this zone:
allow-update { key "nsupdate.info."; };
};

View File

@ -198,6 +198,14 @@ def AuthorizedNicUpdateView(request):
def _update(hostname, ipaddr):
ipaddr = str(ipaddr) # XXX bug in dnspython: crashes if ipaddr is unicode, wants a str!
hosts = Host.filter_by_fqdn(hostname)
num_hosts = len(hosts)
if num_hosts == 0:
return False
if num_hosts > 1:
logging.error("fqdn %s has multiple entries" % hostname)
return False
hosts[0].poke()
try:
update(hostname, ipaddr)
logger.info('%s - received good update -> ip: %s' % (hostname, ipaddr, ))

View File

@ -0,0 +1,89 @@
# -*- 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):
# Adding field 'Host.last_api_update'
db.add_column(u'main_host', 'last_api_update',
self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Host.last_api_update'
db.delete_column(u'main_host', 'last_api_update')
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': '256'}),
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'},
'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': '256'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
u'main.host': {
'Meta': {'unique_together': "(('subdomain', 'domain'),)", 'object_name': 'Host'},
'comment': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', '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_api_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'subdomain': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
'update_secret': ('django.db.models.fields.CharField', [], {'max_length': '256'})
}
}
complete_apps = ['main']

View File

@ -6,6 +6,8 @@ from django.conf import settings
from django.db.models.signals import post_delete
from django.contrib.auth.hashers import make_password
from main import dnstools
import dns.resolver
from datetime import datetime
import re
@ -55,6 +57,7 @@ class Host(models.Model):
max_length=256, default='', blank=True, null=True)
last_update = models.DateTimeField(auto_now=True)
last_api_update = models.DateTimeField(blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name='hosts')
@ -78,6 +81,22 @@ class Host(models.Model):
return Host.objects.filter(
subdomain=splitted[0], domain__domain=splitted[1], **kwargs)
def getIPv4(self):
try:
return dnstools.query_ns(self.get_fqdn(), 'A')
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
return '-'
def getIPv6(self):
try:
return dnstools.query_ns(self.get_fqdn(), 'AAAA')
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
return '-'
def poke(self):
self.last_api_update = datetime.now()
self.save()
def generate_secret(self):
# note: we use a quick hasher for the update_secret as expensive
# more modern hashes might put too much load on the servers. also

View File

@ -34,6 +34,21 @@
</form>
</div>
</div>
<hr>
<div class="row">
<div class="col-lg-6">
<h3>General Information</h3>
<p>We try to get your IP addresses through a trick. We host two fake images on a IPv4 and IPv6 domain and we can associate those with your session. If we find an IP address, we will update this information accordingly. This allows us to get your IPv4 address even when you are using your IPv6 and vice versa.</p>
<b>your IPv4:</b> <span class="ipv4adr">{{ request.session.ipv4 }}</span> <br>
<b>your IPv6:</b> <span class="ipv6adr">{{ request.session.ipv6 }}</span>
</div>
<div class="col-lg-6">
<h3>Host Information</h3>
<p>This information comes directly from the name server and shows the current IPs set for your domain.</p>
<b>Host IPv4:</b> <span class="ipv4adr">{{ host.getIPv4 }}</span> <br>
<b>Host IPv6:</b> <span class="ipv6adr">{{ host.getIPv6 }}</span>
</div>
</div>
{% endblock %}

View File

@ -3,14 +3,16 @@
{% block content %}
<div class="row">
<div class="col-lg-8">
<div class="col-lg-9">
<h3>Host List</h3>
<table class="table">
<tr><th>domain</th><th>last update</th><th>comment</th><th>action</th></tr>
<tr><th>domain</th><th>last IP update</th><th>IPv4</th><th>IPv6</th><th>comment</th><th>action</th></tr>
{% for host in hosts %}
<tr>
<td><b>{{ host.subdomain }}.{{ host.domain.domain }}</b></td>
<td>{{ host.last_update|date }}</td>
<td>{{ host.last_update|timesince }}</td>
<td>{{ host.getIPv4 }}</td>
<td>{{ host.getIPv6 }}</td>
<td>{{ host.comment }}</td>
<td>
<a href="{% url 'host_view' host.pk %}"><i class="icon icon-pencil"></i> edit</a> |
@ -22,7 +24,7 @@
{% endfor %}
</table>
</div>
<div class="col-lg-4">
<div class="col-lg-3">
<h3>New Host</h3>
<form method="post" action="">
{% csrf_token %}
@ -31,9 +33,11 @@
</form>
</div>
</div>
<hr>
<div class="row">
<div class="col-lg-12">
<div class="col-lg-6">
<h3>Information</h3>
<p>We try to get your IP addresses through a trick. We host two fake images on a IPv4 and IPv6 domain and we can associate those with your session. If we find an IP address, we will update this information accordingly. This allows us to get your IPv4 address even when you are using your IPv6 and vice versa.</p>
<b>your IPv4:</b> <span class="ipv4adr">{{ request.session.ipv4 }}</span> <br>
<b>your IPv6:</b> <span class="ipv6adr">{{ request.session.ipv6 }}</span>
</div>

View File

@ -1,7 +1,8 @@
from django.conf.urls import patterns, url
from django.views.generic import TemplateView
from main.views import (
HomeView, OverviewView, HostView, DeleteHostView, AboutView, HelpView, GenerateSecretView)
HomeView, OverviewView, HostView, DeleteHostView, AboutView, HelpView, GenerateSecretView,
RobotsTxtView, )
from api.views import (
MyIpView, DetectIpView, NicUpdateView, AuthorizedNicUpdateView)
@ -23,4 +24,5 @@ urlpatterns = patterns(
url(r'^nic/update$', NicUpdateView),
url(r'^nic/update_authorized$',
AuthorizedNicUpdateView, name='nic_update_authorized'),
url(r'^robots.txt$', RobotsTxtView),
)

View File

@ -1,13 +1,14 @@
# -*- coding: utf-8 -*-
from django.views.generic import TemplateView, CreateView
from django.views.generic.edit import UpdateView, DeleteView
from django.http import HttpResponseRedirect
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.utils.decorators import method_decorator
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied
import dns.inet
import dnstools
from main.forms import CreateHostForm, EditHostForm
from main.models import Host
@ -88,6 +89,7 @@ class OverviewView(CreateView):
self.object = form.save(commit=False)
self.object.created_by = self.request.user
self.object.save()
dnstools.add(self.object.get_fqdn(), self.request.META['REMOTE_ADDR'])
messages.add_message(self.request, messages.SUCCESS, 'Host added.')
return HttpResponseRedirect(self.get_success_url())
@ -152,3 +154,21 @@ class DeleteHostView(DeleteView):
context['nav_overview'] = True
context['hosts'] = Host.objects.filter(created_by=self.request.user)
return context
def RobotsTxtView(request):
"""
Dynamically serve robots.txt content.
If you like, you can optimize this by statically serving this by your web server.
:param request: django request object
:return: HttpResponse object
"""
content = """\
User-agent: *
Crawl-delay: 10
Disallow: /accounts/
Disallow: /myip/
Disallow: /nic/update/
"""
return HttpResponse(content, content_type="text/plain")