diff options
Diffstat (limited to 'scripts/rpkidemo')
-rwxr-xr-x | scripts/rpkidemo | 495 |
1 files changed, 0 insertions, 495 deletions
diff --git a/scripts/rpkidemo b/scripts/rpkidemo deleted file mode 100755 index fdb4e1bb..00000000 --- a/scripts/rpkidemo +++ /dev/null @@ -1,495 +0,0 @@ -#!/usr/bin/env python - -""" -Hosted GUI client startup script, for workshops, etc. - -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. - -In what we hope is the most common case, this script should be run -with no options. - -$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 - -python_version = sys.version_info[:2] - -have_ssl_module = python_version >= (2, 6) - -if python_version == (2, 5): - print """ - WARNING WARNING WARNING - - You are running Python version 2.5, which does not include - real SSL support. This means that sessions created by this - script will be vulnerable to monkey-in-the-middle attacks. - - Python 2.6 does not have this problem. - """ - while True: - answer = raw_input("Are you SURE you want to proceed? (yes/NO) ").strip().lower() - if answer in ("", "n", "no"): - sys.exit("You have chosen wisely") - elif answer in ("y", "yes"): - print "You have been warned" - break - else: - print 'Please answer "yes" or "no"' - -elif have_ssl_module: - try: - import ssl - except ImportError: - sys.exit("You're running Python 2.6+, but I can't find the ssl module, so you have no SSL support at all, argh!") - -else: - sys.exit("Sorry, this script requires Python 2.6+, I seem to be running in %s" % sys.version) - -# Ok, it's safe to import the other stuff we need now - -import os, subprocess, webbrowser, urllib2, getpass, re, errno, time, email.utils, httplib, socket, getopt, urllib, cookielib -import tempfile -from xml.etree.ElementTree import fromstring as ElementFromString - -def save(filename, data): - """ - Save data to a file. - """ - - tempname = "%s.%d.tmp" % (filename, os.getpid()) - f = open(tempname, "w") - f.write(data) - f.close() - os.rename(tempname, filename) - -def save_error(err): - """ - Save the data from the file-like object "f" into a temporary file - and open a web browser to view the result. - """ - - with tempfile.NamedTemporaryFile(prefix = "rpkidemo-error", suffix = ".html", delete = False) as tmpf: - tmpf.write(err.read()) - - # Save filename for use outside the with statement. This ensures - # the file is properly flushed prior to invoking the web browser. - fname = tmpf.name - - sys.stderr.write("errors saved in %s\n" % fname) - webbrowser.open("file://" + fname) - -class CSV_File(object): - """ - A CSV file that's being maintained by the GUI but being monitored, - downloaded, and used here. - """ - - def __init__(self, filename, url): - self.filename = filename - self.url = url - try: - self.timestamp = os.stat(filename).st_mtime - except: - self.store(0, "") - - def last_modified(self): - """ - Return CSV file timestamp formatted for use with HTTP. - """ - return email.utils.formatdate(self.timestamp, False, True) - - def store(self, timestamp, data): - """ - Save CSV file, and record new timestamp. - """ - save(self.filename, data) - self.timestamp = timestamp - os.utime(self.filename, (time.time(), timestamp)) - - -class AbstractHTTPSConnection(httplib.HTTPSConnection): - """ - Customization of httplib.HTTPSConnection to enable certificate - validation. - - This is an abstract class; subclass must set trust_anchor to the - filename of a anchor file in the format that the ssl module - expects. - """ - - trust_anchor = None - - def connect(self): - assert self.trust_anchor is not None - sock = socket.create_connection((self.host, self.port), self.timeout) - if getattr(self, "_tunnel_host", None): - self.sock = sock - self._tunnel() - self.sock = ssl.wrap_socket(sock, - keyfile = self.key_file, - certfile = self.cert_file, - cert_reqs = ssl.CERT_REQUIRED, - ssl_version = ssl.PROTOCOL_TLSv1, - ca_certs = self.trust_anchor) - - -class main(object): - """ - Main program. - """ - - # Environmental parameters - - top = os.path.realpath(os.path.join((sys.path[0] or "."), "..")) - cwd = os.getcwd() - - # Parameters that we might want to get from a config file. - # Just wire them all in for the moment. - - base_url = "https://demo.rpki.net/" - myrpki_url = base_url + "rpki/" - auth_url = myrpki_url + "demo/login" - example_myrpki_cfg = "%s/rpkid/examples/rpki.conf" % top - working_dir = "%s/rpkidemo-data" % cwd - myrpki_py = "%s/rpkid/myrpki.py" % top - user_agent = "RPKIDemo" - delay = 15 - trust_anchor = "%s/scripts/rpkidemo.pem" % top - - openssl = None - - def setup_openssl(self): - """ - Find a usable version of OpenSSL, or build one if we must. - """ - - 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): - self.openssl = f - break - - if self.openssl is None: - print "Couldn't find usable openssl on path, attempting to build one" - subprocess.check_call(("./configure",), cwd = self.top) - subprocess.check_call(("make",), cwd = os.path.join(self.top, "openssl")) - self.openssl = os.path.join(self.top, "openssl", "openssl", "apps", "openssl") - print "Done building openssl" - print - - if usable_openssl(self.openssl): - print "Using", self.openssl - else: - sys.exit("Could not find or build usable version of openssl, giving up") - - @staticmethod - def setup_utc(): - """ - This script thinks in UTC. - """ - - os.environ["TZ"] = "UTC" - time.tzset() - - def setup_username(self): - """ - Get username and password for web interface, construct urllib2 - "opener" tailored for our use, perform an initial GET (ignoring - result, other than exceptions) to test the username and password. - """ - - print "I need to know your username and password on the Django GUI server to proceed" - - while True: - - try: - self.username = raw_input("Username: ") - self.password = getpass.getpass() - - handlers = [] - - self.cookiejar = cookielib.CookieJar() - handlers.append(urllib2.HTTPCookieProcessor(self.cookiejar)) - - if have_ssl_module: - - class HTTPSConnection(AbstractHTTPSConnection): - trust_anchor = self.trust_anchor - - class HTTPSHandler(urllib2.HTTPSHandler): - def https_open(self, req): - return self.do_open(HTTPSConnection, req) - - handlers.append(HTTPSHandler) - - self.opener = urllib2.build_opener(*handlers) - - # Test login credentials - resp = self.opener.open(self.auth_url) # GET - - r = self.opener.open(urllib2.Request( - url = self.auth_url, - data = urllib.urlencode({ "username" : self.username, - "password" : self.password, - "csrfmiddlewaretoken" : self.csrftoken() }), - headers = { "Referer" : self.auth_url, - "User-Agent" : self.user_agent})) # POST - return - - except urllib2.URLError, e: - print "Could not log in to server: %s" % e - print "Please try again" - save_error(e) - - def csrftoken(self): - """ - Pull Django's CSFR token from cookie database. - - Django's login form requires the "csrfmiddlewaretoken." It turns out - this is the same value as the "csrftoken" cookie, so we don't need - to bother parsing the form. - """ - - return [c.value for c in self.cookiejar if c.name == "csrftoken"][0] - - def setup_working_directory(self): - """ - Create working directory and move to it. - """ - - try: - print "Creating", self.working_dir - os.mkdir(self.working_dir) - except OSError, e: - if e.errno != errno.EEXIST: - raise - print self.working_dir, "already exists, reusing it" - os.chdir(self.working_dir) - - def setup_config_file(self): - """ - Generate rpki.conf - """ - - if os.path.exists("rpki.conf"): - print "You already have a rpki.conf file, so I will use it" - return - - print "Generating rpki.conf" - section_regexp = re.compile("\s*\[\s*(.+?)\s*\]\s*$") - variable_regexp = re.compile("\s*([-a-zA-Z0-9_]+)\s*=\s*(.+?)\s*$") - f = open("rpki.conf", "w") - f.write("# Automatically generated, do not edit\n") - section = None - for line in open(self.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 - value = m.group(2) if option else None - if option == "handle": - line = "handle = %s\n" % self.username - if option == "openssl": - line = "openssl = %s\n" % self.openssl - if option in ("run_rpkid", "run_pubd", "run_rootd") and value != "false": - line = "%s = false\n" % option - f.write(line) - f.close() - - def myrpki(self, *cmd): - """ - Run a myrpki command. - """ - return subprocess.check_call((sys.executable, self.myrpki_py) + cmd) - - def upload(self, url, filename): - """ - Upload filename to URL, return result. - """ - - url = "%s%s/%s" % (self.myrpki_url, url, self.username) - data = open(filename).read() - print "Uploading", filename, "to", url - post_data = urllib.urlencode({ - "content" : data, - "csrfmiddlewaretoken" : self.csrftoken() }) # POST - try: - return self.opener.open(urllib2.Request(url, post_data, { - "User-Agent" : self.user_agent, - "Referer" : url})) - except urllib2.HTTPError, e: - sys.stderr.write("Problem uploading to URL %s\n" % url) - save_error(e) - raise - - def update(self): - """ - Run configure_resources, upload result, download updated result. - """ - - self.myrpki("configure_resources") - r = self.upload("demo/myrpki-xml", "myrpki.xml") - save("myrpki.xml", r.read()) - - def setup_csv_files(self): - """ - Create CSV file objects and synchronize timestamps. - """ - - self.csv_files = [ - CSV_File("asns.csv", "demo/down/asns/%s" % self.username), - CSV_File("prefixes.csv", "demo/down/prefixes/%s" % self.username), - CSV_File("roas.csv", "demo/down/roas/%s" % self.username) ] - - def upload_for_response(self, url, path): - """ - Upload an XML file to the requested URL and wait for for the server - to signal that a response is ready. - """ - - self.upload(url, path) - - print """ - Waiting for response to upload. This may require action by a human - being on the server side, so it may take a while, please be patient. - """ - - while True: - try: - return self.opener.open(urllib2.Request( - "%s%s/%s" % (self.myrpki_url, url, self.username), - None, - { "User-Agent" : self.user_agent })) - except urllib2.HTTPError, e: - # Portal GUI uses response code 503 to signal "not ready" - if e.code != 503: - sys.stderr.write("Problem getting response from %s: %s\n" % (url, e)) - save_error(e) - raise - time.sleep(self.delay) - - def setup_parent(self): - """ - Upload the user's identity.xml and wait for the portal gui to send - back the parent.xml response. - """ - - r = self.upload_for_response("demo/parent-request", "entitydb/identity.xml") - parent_data = r.read() - save("parent.xml", parent_data) - self.myrpki("configure_parent", "parent.xml") - - # Extract the parent_handle from the xml response and save it for use by - # setup_repository() - self.parent_handle = ElementFromString(parent_data).get("parent_handle") - - def setup_repository(self): - """ - Upload the repository referral to the portal-gui and wait the - response from the repository operator. - """ - - r = self.upload_for_response("demo/repository-request", "entitydb/repositories/%s.xml" % self.parent_handle) - save("repository.xml", r.read()) - self.myrpki("configure_repository", "repository.xml") - - def poll(self, csv_file): - """ - Poll for new version of a CSV file, save if changed, return - boolean indicating whether file has changed. - """ - - try: - url = self.myrpki_url + csv_file.url - r = self.opener.open(urllib2.Request(url, None, { - "If-Modified-Since" : csv_file.last_modified(), - "User-Agent" : self.user_agent })) - timestamp = time.mktime(r.info().getdate("Last-Modified")) - csv_file.store(timestamp, r.read()) - return True - except urllib2.HTTPError, e: - if e.code == 304: # 304 == "Not Modified" - return False - else: - sys.stderr.write("Problem polling URL %s\n" % url) - save_error(e) - raise - - def poll_loop(self): - """ - Loop forever, polling for updates. - """ - - while True: - changed = False - for csv_file in self.csv_files: - if self.poll(csv_file): - changed = True - if changed: - self.update() - time.sleep(self.delay) - - def getopt(self): - """ - Parse options. - """ - opts, argv = getopt.getopt(sys.argv[1:], "hi?", ["help"]) - for o, a in opts: - if o in ("-h", "--help", "-?"): - print __doc__ - sys.exit(0) - if argv: - sys.exit("Unexpected arguments %r" % (argv,)) - - def __init__(self): - self.getopt() - self.setup_utc() - self.setup_openssl() - self.setup_username() - self.setup_working_directory() - self.setup_config_file() - self.setup_csv_files() - self.myrpki("initialize") - self.setup_parent() - self.setup_repository() - self.update() - self.update() - - webbrowser.open(self.myrpki_url) - - self.poll_loop() - -main() - -# Local Variables: -# mode:python -# End: - -# vim:sw=2 ts=8 expandtab |