#!/usr/bin/env python """ Hosted GUI client startup script, for workshops, etc. This isn't usable yet. As of when this is run, we assume that the tarball (contents TBD and perhaps changing from one workshop to another) have been unpacked, that we are on some Unix-like machine, and that we are executing in a Python interpreter. We have to check anything else we care about. $Id$ Copyright (C) 2010 Internet Systems Consortium ("ISC") 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 ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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. """ # Check Python version before doing anything else import sys if sys.version_info[:2] not in ((2, 5), (2, 6)): sys.exit("Sorry, this script requires Python 2.5 or 2.6, I seem to be running in %s" % sys.version) # Ok, it's safe to import the other stuff we need import os, subprocess, webbrowser, urllib2, getpass, re, errno from xml.etree.ElementTree import fromstring as ElementFromString top = os.path.realpath(os.path.join((sys.path[0] or "."), "..")) cwd = os.getcwd() # Parameters that perhaps we should be getting from a config file. # Just wiring in for now, to get them all in one place. base_url = "http://some.where.example/rpki/blah/" example_myrpki_cfg = "%s/rpkid/examples/myrpki.conf" % top working_dir = "%s/rpkidemo-data" % cwd myrpki_py = "%s/rpkid/myrpki.py" % top # Find or build a usable copy of OpenSSL openssl = None def scrape(*args): return subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT).communicate()[0] def usable_openssl(f): return f is not None and os.path.exists(f) and "-ss_cert" in scrape(f, "ca", "-?") and "Usage cms" in scrape(f, "cms", "-?") for d in os.environ["PATH"].split(":"): f = os.path.join(d, "openssl") if usable_openssl(f): openssl = f break if openssl is None: print "Couldn't find usable openssl on path, attempting to build one" subprocess.check_call(("./configure",), cwd = top) subprocess.check_call(("make",), cwd = os.path.join(top, "openssl")) openssl = os.path.join(top, "openssl", "openssl", "apps", "openssl") print "Done building openssl" print if usable_openssl(openssl): print "Using", openssl else: sys.exit("Could not find or build usable version of openssl, giving up") print "I need to know your username and password on the Django GUI server to proceed" # Get username and password for web interface, construct urllib2 # "opener" tailored for our application. # # Perhaps put this in a loop that does a connection check to make sure # the given username and password works before proceeding? username = raw_input("Username: ") password = getpass.getpass() cookie_handler = urllib2.HTTPCookieProcessor() auth_handler = urllib2.HTTPDigestAuthHandler() auth_handler.add_password( realm = None, uri = base_uri, user = username, passwd = password) opener = urllilb2.build_opener(cookie_handler, auth_handler) # Create working directory and move to it. try: os.mkdir(working_dir) except OSError, e: if e.errno != errno.EEXIST: raise os.chdir(working_dir) # Generate config file section_regexp = re.compile("\s*\[\s*(.+?)\s*\]\s*$") variable_regexp = re.compile("\s*([-a-zA-Z0-9_]+)\s*=\s*(.+?)\s*$") f = open("myrpki.conf", "w") f.write("# Automatically generated, do not edit\n") section = None for line in open(example_myrpki_cfg): m = section_regexp.match(line) if m: section = m.group(1) m = variable_regexp.match(line) option = m.group(1) if m and section == "myrpki" else None if option == "handle": line = "handle = %s\n" % username if option == "openssl": line = "openssl = %s\n" % openssl if option in ("run_rpkid", "run_pubd", "run_rootd"): line = "%s = false\n" % option f.write(line) f.close() def myrpki(*cmd): return subprocess.check_call((sys.executable, myrpki_py) + cmd) def upload(url, filename): """ Upload filename to URL, return result. """ return opener(urllib2.Request( base_url + url, open(filename).read(), { "Content-Type" : "Application/XML", "User-Agent" : "RPKIDemo" })) def poll(filename, url): """ Poll for new version of URL, save as filename if changed, return boolean indicating whether file changed. """ try: r = opener(urllib2.Request( base_url + url, None, { "If-Modified-Since" : blah_blah_date_voodoo(filename), "User-Agent" : "RPKIDemo" })) save(filename, r.read()) blah_blah_more_date_voodoo(filename, r.info().getheader("Last-Modified")) return True except HTTPError, e: if e.code == 304: # 304 == "Not Modified" return False else: raise def save(filename, data): f = open(filename, "w") f.write(data) f.close() # Initialize BPKI myrpki("initialize") # Upload identity to parent r = upload("upload-child", "entitydb/identity.xml") # Save parent's response and extract parent's handle parent_data = r.read() save("parent.xml", parent_data) parent_handle = ElementFromString(parent_data).get("parent_handle") # Incorporate parent's respose into our entitydb myrpki("configure_parent", "parent.xml") # Upload repository request r = upload("upload-pubclient", "entitydb/repositories/%s.xml" % parent_handle) # Save and process repository's response save("repository.xml", r.read()) myrpki("configure_repository", "repository.xml") # Create empty CSV files if they don't already exist for f in ("prefixes.csv", "asns.csv", "roas.csv"): if not os.path.exists(f): open(f, "w").close() def update(): myrpki("configure_resources") r = upload("upload-resources", "myrpki.xml") save("myrpki.xml", r.read()) # Do two round trips to setup up BPKI stuff update() update() # Start web browser pointing at GUI webbrowser.open(base_url) # Loop forever, polling for updates while True: changed = False for f in ("prefixes.csv", "asns.csv", "roas.csv"): if poll(f): changed = True if changed: update() time.sleep(delay)