aboutsummaryrefslogtreecommitdiff
path: root/rpkid/rpki/gui/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid/rpki/gui/scripts')
-rw-r--r--rpkid/rpki/gui/scripts/adduser.py115
-rw-r--r--rpkid/rpki/gui/scripts/list_resources.py217
-rwxr-xr-xrpkid/rpki/gui/scripts/load_csv.py145
-rwxr-xr-xrpkid/rpki/gui/scripts/roa_check.py71
4 files changed, 548 insertions, 0 deletions
diff --git a/rpkid/rpki/gui/scripts/adduser.py b/rpkid/rpki/gui/scripts/adduser.py
new file mode 100644
index 00000000..8b475c0c
--- /dev/null
+++ b/rpkid/rpki/gui/scripts/adduser.py
@@ -0,0 +1,115 @@
+# $Id$
+#
+# Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+#
+#
+# Helper script to quickly set up a new portal-gui user/handle. This script
+# is designed to be safe to run multiple times for the same user.
+#
+# DO NOT EDIT! This script is automatically generated from adduser.py
+
+import os
+os.environ['DJANGO_SETTINGS_MODULE'] = 'rpki.gui.settings'
+
+from django.contrib.auth.models import User
+from django.conf import settings
+from rpki.gui.app.models import Conf
+
+# The username that apache runs as. This is required so that we can chown
+# the csv files that the portal-gui needs to write.
+WEB_USER='@WEBUSER@'
+
+import os
+import sys
+import getpass
+import pwd
+
+web_uid = pwd.getpwnam(WEB_USER)[2]
+
+if __name__ == '__main__':
+ if len(sys.argv) < 3:
+ print >>sys.stderr, 'usage: adduser <username> <user\'s email> <host handle>'
+ sys.exit(1)
+
+ if os.getuid() != 0:
+ print >>sys.stderr, 'error: this script must be run as root so it can set file permissions.'
+ sys.exit(1)
+
+ username = sys.argv[1]
+ email = sys.argv[2]
+ host = sys.argv[3]
+ print 'username=', username, 'email=', email, 'host=', host
+
+ user_set = User.objects.filter(username=username)
+ if user_set:
+ print >>sys.stderr, 'user already exists'
+ user = user_set[0]
+ else:
+ print >>sys.stderr, 'creating user'
+ password = getpass.getpass()
+ user = User.objects.create_user(username, email, password)
+
+ conf_set = Conf.objects.filter(handle=username)
+ if conf_set:
+ conf = conf_set[0]
+ else:
+ print >>sys.stderr, 'creating conf'
+ conf = Conf.objects.create(handle=username)
+
+ # always try to add the user as owner just in case the Conf object was
+ # created previously by the "list_resources" script
+ conf.owner.add(user)
+
+ if host != username:
+ host_set = Conf.objects.filter(handle=host)
+ if not host_set:
+ print >>sys.stderr, 'error: Conf object for host %s does not exist!' % host
+ sys.exit(1)
+
+ conf.host = host_set[0]
+ else:
+ print >>sys.stderr, '%s is self-hosted' % username
+ conf.save()
+
+ myrpki_dir = '%s/%s' % (settings.CONFDIR, username)
+ print 'myrpki_dir=', myrpki_dir
+ if not os.path.exists(myrpki_dir):
+ print 'creating ', myrpki_dir
+ os.mkdir(myrpki_dir)
+ os.chown(myrpki_dir, web_uid, -1)
+
+ # create enought of rpki.conf enough to fool portal-gui
+ myrpki_conf = myrpki_dir + '/rpki.conf'
+ if not os.path.exists(myrpki_conf):
+ print 'creating ', myrpki_conf
+ with open(myrpki_conf, 'w') as f:
+ print >>f, """[myrpki]
+run_rpkidemo=true
+run_rpkid=false
+asn_csv=%(path)s/asns.csv
+roa_csv=%(path)s/roas.csv
+prefix_csv=%(path)s/prefixes.csv""" % { 'path': myrpki_dir }
+
+ # create empty csv files so portal-gui doesn't barf
+ for base in ['roas', 'asns', 'prefixes']:
+ fname = '%s/%s.csv' % (myrpki_dir, base)
+ if not os.path.exists(fname):
+ print 'creating ', fname
+ with open(fname, 'w') as f:
+ # just create an empty file
+ pass
+ os.chown(fname, web_uid, -1)
+
+# vim:sw=4 ts=8
diff --git a/rpkid/rpki/gui/scripts/list_resources.py b/rpkid/rpki/gui/scripts/list_resources.py
new file mode 100644
index 00000000..77380505
--- /dev/null
+++ b/rpkid/rpki/gui/scripts/list_resources.py
@@ -0,0 +1,217 @@
+# $Id$
+#
+# Copyright (C) 2010 SPARTA, Inc. dba Cobham Analytic Solutions
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+#
+#
+# This script is reponsible for talking to rpkid and populating the
+# portal-gui's sqlite database. It asks rpkid for the list of received
+# resources, and the handles of any children.
+#
+# This script should be run in the directory containing the rpki.conf
+# for the handle that is self-hosting rpkid.
+#
+# Exit values:
+# 0 success, no errors
+# 1 fatal error
+# 2 usage error
+# 3 did not receive all <list_received_resources/> responses, try again
+# later
+
+import sys
+
+import os
+os.environ['DJANGO_SETTINGS_MODULE'] = 'rpki.gui.settings'
+
+from datetime import datetime
+import getopt
+from os.path import basename
+
+from rpki.myrpki import CA
+import rpki.config
+import rpki.x509
+import rpki.http
+import rpki.async
+import rpki.left_right
+import rpki.resource_set
+
+from rpki.gui.app import models
+
+verbose = False
+version = '$Id$'
+
+def query_rpkid():
+ """Fetch our received resources from the local rpkid using the rpki.conf
+ in the current directory."""
+ cfg_file = os.getenv("RPKI_CONF", "rpki.conf")
+ cfg = rpki.config.parser(cfg_file, "myrpki")
+ bpki_servers = CA(cfg_file, cfg.get("bpki_servers_directory"))
+ rpkid_base = "http://%s:%s/" % (cfg.get("rpkid_server_host"), cfg.get("rpkid_server_port"))
+
+ if verbose:
+ print 'current directory is', os.getcwd()
+ print 'cfg_file=', cfg_file
+ print 'bpki_servers=', bpki_servers.dir
+ print 'rpkid_base=', rpkid_base
+
+ call_rpkid = rpki.async.sync_wrapper(rpki.http.caller(
+ proto = rpki.left_right,
+ client_key = rpki.x509.RSA(PEM_file = bpki_servers.dir + "/irbe.key"),
+ client_cert = rpki.x509.X509(PEM_file = bpki_servers.dir + "/irbe.cer"),
+ server_ta = rpki.x509.X509(PEM_file = bpki_servers.cer),
+ server_cert = rpki.x509.X509(PEM_file = bpki_servers.dir + "/rpkid.cer"),
+ url = rpkid_base + "left-right",
+ debug = verbose))
+
+ if verbose:
+ print 'retrieving the list of <self/> handles served by this rpkid'
+ rpkid_reply = call_rpkid(rpki.left_right.self_elt.make_pdu(action="list"))
+
+ # retrieve info about each handle
+ pdus = []
+ handles = []
+ for h in rpkid_reply:
+ assert isinstance(h, rpki.left_right.self_elt)
+ if verbose:
+ print 'adding handle %s to query' % (h.self_handle,)
+ # keep a list of the handles served by rpkid so that we may check that
+ # all expected responses are received.
+ handles.append(h.self_handle)
+ pdus.extend(
+ [rpki.left_right.child_elt.make_pdu(action="list", self_handle=h.self_handle),
+ rpki.left_right.list_received_resources_elt.make_pdu(self_handle=h.self_handle)
+ #rpki.left_right.parent_elt.make_pdu(action="list", tag="parents", self_handle=handle),
+ #rpki.left_right.list_roa_requests_elt.make_pdu(tag='roas', self_handle=handle),
+ ])
+
+ if verbose:
+ print 'querying for children and resources'
+ return handles, call_rpkid(*pdus)
+
+def usage(rc):
+ print 'usage: %s [ -hvV ] [ --help ] [ --verbose ] [ --version ]' % basename(sys.argv[0],)
+ sys.exit(rc)
+
+try:
+ opts, args = getopt.getopt(sys.argv[1:], 'hvV', [ 'help', 'verbose', 'version'])
+except getopt.GetoptError, err:
+ print str(err)
+ usage(2)
+
+for o,a in opts:
+ if o in ('-h', '--help'):
+ usage(0)
+ elif o in ('-v', '--verbose'):
+ verbose = True
+ elif o in ('-V', '--version'):
+ print basename(sys.argv[0]), version
+ sys.exit(0)
+
+handles, pdus = query_rpkid()
+seen = set() # which handles we got <list_received_resources/> responses
+for pdu in pdus:
+ conf_set = models.Conf.objects.filter(handle=pdu.self_handle)
+ if conf_set.count():
+ conf = conf_set[0]
+ else:
+ if verbose:
+ print 'creating new conf for %s' % (pdu.self_handle,)
+ conf = models.Conf.objects.create(handle=pdu.self_handle)
+
+ #if isinstance(pdu, rpki.left_right.parent_elt):
+# print x.parent_handle, x.sia_base, x.sender_name, x.recipient_name, \
+# x.peer_contact_uri
+ if isinstance(pdu, rpki.left_right.child_elt):
+ # have we seen this child before?
+ child_set = conf.children.filter(handle=pdu.child_handle)
+ if not child_set:
+ if verbose:
+ print 'creating new child %s' % (pdu.child_handle,)
+ child = models.Child(conf=conf, handle=pdu.child_handle)
+ child.save()
+ #elif isinstance(x, rpki.left_right.list_roa_requests_elt):
+ # print x.asn, x.ipv4, x.ipv6
+ elif isinstance(pdu, rpki.left_right.list_received_resources_elt):
+ # keep track of handles we got replies for
+ seen.add(pdu.self_handle)
+ # have we seen this parent before?
+ parent_set = conf.parents.filter(handle=pdu.parent_handle)
+ if not parent_set:
+ if verbose:
+ print 'creating new parent %s' % (pdu.parent_handle,)
+ parent = models.Parent(conf=conf, handle=pdu.parent_handle)
+ parent.save()
+ else:
+ parent = parent_set[0]
+
+ not_before = datetime.strptime(pdu.notBefore, "%Y-%m-%dT%H:%M:%SZ")
+ not_after = datetime.strptime(pdu.notAfter, "%Y-%m-%dT%H:%M:%SZ")
+
+ # have we seen this resource cert before?
+ cert_set = parent.resources.filter(uri=pdu.uri)
+ if cert_set.count() == 0:
+ cert = models.ResourceCert(uri=pdu.uri, parent=parent,
+ not_before=not_before, not_after=not_after)
+ else:
+ cert = cert_set[0]
+ # update timestamps since it could have been modified
+ cert.not_before = not_before
+ cert.not_after = not_after
+ cert.save()
+
+ for asn in rpki.resource_set.resource_set_as(pdu.asn):
+ # see if this resource is already part of the cert
+ if cert.asn.filter(lo=asn.min, hi=asn.max).count() == 0:
+ # ensure this range wasn't seen from another of our parents
+ for v in models.Asn.objects.filter(lo=asn.min, hi=asn.max):
+ # determine if resource is delegated from another parent
+ if v.from_cert.filter(parent__in=conf.parents.all()).count():
+ cert.asn.add(v)
+ break
+ else:
+ if verbose:
+ print 'adding AS %s' % (asn,)
+ cert.asn.create(lo=asn.min, hi=asn.max)
+ cert.save()
+
+ # IPv4/6 - not separated in the django db
+ def add_missing_address(addr_set):
+ for ip in addr_set:
+ lo=str(ip.min)
+ hi=str(ip.max)
+ if cert.address_range.filter(lo=lo, hi=hi).count() == 0:
+ # ensure that this range wasn't previously seen from another of our parents
+ for v in models.AddressRange.objects.filter(lo=lo, hi=hi):
+ # determine if this resource is delegated from another parent as well
+ if v.from_cert.filter(parent__in=conf.parents.all()).count():
+ cert.address_range.add(v)
+ break
+ else:
+ if verbose:
+ print 'adding address range %s' % (ip,)
+ cert.address_range.create(lo=lo, hi=hi)
+ cert.save()
+
+ add_missing_address(rpki.resource_set.resource_set_ipv4(pdu.ipv4))
+ add_missing_address(rpki.resource_set.resource_set_ipv6(pdu.ipv6))
+
+# verify that we got responses for all expected handles
+for h in handles:
+ if h not in seen:
+ print >>sys.stderr, 'error: did not receive response for handle %s' % (h,)
+ sys.exit(3)
+
+sys.exit(0) # success
+
+# vim:sw=4 expandtab ts=4
diff --git a/rpkid/rpki/gui/scripts/load_csv.py b/rpkid/rpki/gui/scripts/load_csv.py
new file mode 100755
index 00000000..0ef49cce
--- /dev/null
+++ b/rpkid/rpki/gui/scripts/load_csv.py
@@ -0,0 +1,145 @@
+# $Id$
+#
+# Copyright (C) 2010 SPARTA, Inc. dba Cobham Analytic Solutions
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+#
+#
+# Helper script to load existing data from csv into the Django DB.
+# Primarly useful for the initial load, as the GUI does not sync changes
+# made directly to the csv files back into the database.
+#
+# This script should be run from the directory containing the rpki.conf
+# for the handle you are loading data
+#
+
+import sys, os
+os.environ['DJANGO_SETTINGS_MODULE'] = 'rpki.gui.settings'
+
+import csv
+import socket # for socket.error
+
+import rpki
+import rpki.resource_set
+import rpki.ipaddrs
+from rpki.myrpki import csv_reader
+
+from rpki.gui.app import models
+from rpki.gui.app.views import add_roa_requests
+
+cfg_file = os.getenv("RPKI_CONF", "rpki.conf")
+cfg = rpki.config.parser(cfg_file, "myrpki")
+handle = cfg.get('handle')
+asn_csv = cfg.get('asn_csv')
+prefix_csv = cfg.get('prefix_csv')
+roa_csv = cfg.get('roa_csv')
+
+print 'processing csv files for resource handle', handle
+
+conf = models.Conf.objects.get(handle=handle)
+
+# every parent has a favorite
+def best_child(address_range, parent, parent_range):
+ '''Return the child address range that is the closest match, or
+ returns the arguments if no children.'''
+ if address_range == parent_range:
+ return (parent, parent_range)
+ for q in list(parent.children.all()): # force strict evaluation
+ t = q.as_resource_range()
+ if t.min <= address_range.min and t.max >= address_range.max:
+ return best_child(address_range, q, t)
+ # check for overlap
+ if t.min <= address_range.min <= t.max or t.min <= address_range.max <= t.max:
+ raise RuntimeError, \
+ 'can not handle overlapping ranges: %s and %s' % (address_range, t)
+ return parent, parent_range
+
+def get_or_create_prefix(address_range):
+ '''Returns a AddressRange object for the resource_range_ip specified
+ as an argument. If no match is found, a new AddressRange object is
+ created as a child of the best matching received resource.'''
+
+ # get all resources from our parents
+ prefix_set = models.AddressRange.objects.filter(
+ from_cert__parent__in=conf.parents.all())
+
+ # gross, since we store the address ranges as strings in the django
+ # db, we can't use the normal __lte and __gte filters, so we get to
+ # do it in python instead.
+ for prefix in prefix_set:
+ prefix_range = prefix.as_resource_range()
+ if (prefix_range.min <= address_range.min and
+ prefix_range.max >= address_range.max):
+ # there should only ever be a single matching prefix
+ break
+ else:
+ raise RuntimeError, '%s does not match any received address range.' % (
+ address_range,)
+
+ # find the best match among the children + grandchildren
+ prefix, prefix_range = best_child(address_range, prefix, prefix_range)
+
+ print 'best match for %s is %s' % (address_range, prefix)
+ if prefix_range.min != address_range.min or prefix_range.max != address_range.max:
+ # create suballocation
+ print 'creating new range'
+ prefix = models.AddressRange.objects.create(lo=str(address_range.min),
+ hi=str(address_range.max), parent=prefix)
+ return prefix
+
+def get_or_create_asn(asn):
+ asn_set = models.Asn.objects.filter(lo__lte=asn.min, hi__gte=asn.max,
+ from_cert__parent__in=conf.parents.all())
+ if not asn_set:
+ raise RuntimeError, '%s does not match any received AS range' % (asn,)
+ best = best_child(asn, asn_set[0], asn_set[0].as_resource_range())[0]
+ print 'best match for %s is %s' % (asn, best)
+ if best.lo != asn.min or best.hi != asn.max:
+ best = models.Asn.objects.create(lo=asn.min, hi=asn.max, parent=best)
+ return best
+
+def do_asns():
+ print 'processing', asn_csv
+ for child_handle, asn in csv_reader(asn_csv, columns=2):
+ asn_range = rpki.resource_set.resource_range_as.parse_str(asn)
+ child = conf.children.get(handle=child_handle)
+ asn = get_or_create_asn(asn_range)
+ child.asn.add(asn)
+
+def do_prefixes():
+ print 'processing', prefix_csv
+ for child_handle, prefix in csv_reader(prefix_csv, columns=2):
+ child = conf.children.get(handle=child_handle)
+ try:
+ rs = rpki.resource_set.resource_range_ipv4.parse_str(prefix)
+ except ValueError, err:
+ rs = rpki.resource_set.resource_range_ipv6.parse_str(prefix)
+ obj = get_or_create_prefix(rs)
+ obj.allocated = child
+ obj.save()
+
+def do_roas():
+ print 'processing', roa_csv
+ for prefix, asn, group in csv_reader(roa_csv, columns=3):
+ try:
+ rs = rpki.resource_set.roa_prefix_ipv4.parse_str(prefix)
+ except ValueError, err:
+ rs = rpki.resource_set.roa_prefix_ipv6.parse_str(prefix)
+
+ print str(rs.min()), str(rs.max()), rs.max_prefixlen
+ obj = get_or_create_prefix(rs.to_resource_range())
+ add_roa_requests(conf, obj, [int(asn)], rs.max_prefixlen)
+
+do_asns()
+do_prefixes()
+do_roas()
diff --git a/rpkid/rpki/gui/scripts/roa_check.py b/rpkid/rpki/gui/scripts/roa_check.py
new file mode 100755
index 00000000..c280d935
--- /dev/null
+++ b/rpkid/rpki/gui/scripts/roa_check.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+# $Id$
+#
+# Runs through all the published ROAs and updates the Django DB with the
+# current active status of each defined ROA.
+#
+
+import socket
+
+from rcynic_output_iterator import rcynic_xml_iterator, rcynic_roa
+from rpki.resource_set import resource_set_ipv4, resource_set_ipv6
+from rpki.resource_set import roa_prefix_set_ipv4, roa_prefix_set_ipv6
+from rpki.resource_set import resource_range_ipv4, resource_range_ipv6
+from rpki.ipaddrs import v4addr, v6addr
+
+from rpki.gui.app.models import Roa
+
+# build up a list of all the authenticated roa's using the asn as the key
+roaiter = rcynic_xml_iterator(
+ rcynic_root='/home/melkins/rcynic/rcynic-data/',
+ xml_file='/home/melkins/rcynic/rcynic.xml')
+
+# key is an ASN
+# each element is a tuple of (resource_set_ipv4, resource_set_ipv6)
+roaauth = {}
+
+for roa in roaiter:
+ if isinstance(roa, rcynic_roa):
+ k = roa.asID
+ if not roaauth.has_key(k):
+ v = [resource_set_ipv4(), resource_set_ipv6()]
+ roaauth[k] = v
+ else:
+ v = roaauth[k]
+ for pfx in roa.prefix_sets:
+ if isinstance(pfx, roa_prefix_set_ipv4):
+ v[0] = v[0].union(pfx.to_resource_set())
+ elif isinstance(pfx, roa_prefix_set_ipv6):
+ v[1] = v[1].union(pfx.to_resource_set())
+
+#for k, v in roaauth.iteritems():
+# print 'asn %d : prefixes %s' % (k, ' '.join(map(str,v)))
+
+# run through all the ROA's in the GUI's database
+for roa in Roa.objects.all():
+ k = int(roa.asn)
+ valid = False
+ if roaauth.has_key(k):
+ # ensure that all prefixes listed in the roa are present
+ # we convert the list of prefixes into prefix sets and use the
+ # resource_set class to perform set comparisons
+ ipv4_set = resource_set_ipv4()
+ ipv6_set = resource_set_ipv6()
+ for pfx in roa.prefix.all():
+ # IP addresses are just stored as strings in the sqlite db
+ try:
+ ipv4_set.append(resource_range_ipv4(v4addr(str(pfx.lo)), v4addr(str(pfx.hi))))
+ except socket.error:
+ ipv6_set.append(resource_range_ipv6(v6addr(str(pfx.lo)), v6addr(str(pfx.hi))))
+ r = roaauth[k]
+ if ipv4_set.issubset(r[0]) and ipv6_set.issubset(r[1]):
+ valid = True
+ if valid:
+ if not roa.active:
+ roa.active = True
+ roa.save()
+ else:
+ print 'roa for asn %s is not valid' % (roa.asn, )
+ if roa.active:
+ roa.active = False
+ roa.save()