aboutsummaryrefslogtreecommitdiff
path: root/scripts/rpkidemo
blob: 1937777756c035ea8094c0000b1f353cea883818 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#!/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)