#!/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.
****** Installing and Configuring ******

* GUI/Installing for new installs
* GUI/Upgrading for upgrading from a previous install
* GUI/Configuring
* GUI/UserModel for instructions on managing users

****** Using the GUI ******

****** GUI Examples ******

***** Logging in to the GUI *****

 01-login.jpg

***** The Dashboard - Let's Make a ROA *****

 02-dashboard.jpg

***** ROA List Currently Empty, So Let's Create One *****

 03-roas.jpg

***** Choose an AS and Prefix - Let MaxLen? Default *****

 04-create-roa.jpg

***** What Will the Consequences Be? - Confirm OK *****

 05-are-you-sure.jpg

***** Now We Can See ROAs - Let's Look at Routes *****

      06-roa-list.jpg

***** Real Effect on Routing Table *****

 07-route view.jpg

***** Ghostbusters etc. are Similar *****
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