aboutsummaryrefslogtreecommitdiff
path: root/ca
diff options
context:
space:
mode:
Diffstat (limited to 'ca')
-rwxr-xr-xca/irbe_cli464
-rwxr-xr-xca/irdbd4
-rwxr-xr-xca/pubd4
-rwxr-xr-xca/rootd4
-rwxr-xr-xca/rpki-confgen455
-rw-r--r--ca/rpki-confgen.xml281
-rwxr-xr-xca/rpki-manage10
-rwxr-xr-xca/rpki-sql-backup16
-rwxr-xr-xca/rpki-sql-setup401
-rwxr-xr-xca/rpki-start-servers37
-rw-r--r--ca/rpki.wsgi6
-rwxr-xr-xca/rpkic4
-rwxr-xr-xca/rpkid4
-rwxr-xr-xca/rpkigui-apache-conf-gen755
-rwxr-xr-xca/rpkigui-import-routes1
-rwxr-xr-xca/rpkigui-query-routes17
-rw-r--r--ca/tests/Makefile.in42
-rwxr-xr-xca/tests/bgpsec-yaml.py52
-rw-r--r--ca/tests/left-right-protocol-samples.xml170
-rw-r--r--ca/tests/myrpki-xml-parse-test.py74
-rw-r--r--ca/tests/old_irdbd.py4
-rw-r--r--ca/tests/publication-control-protocol-samples.xml155
-rw-r--r--ca/tests/publication-protocol-samples.xml451
-rw-r--r--ca/tests/rrdp-samples.xml81
-rw-r--r--ca/tests/smoketest.6.yaml3
-rw-r--r--ca/tests/smoketest.py2316
-rw-r--r--ca/tests/sql-cleaner.py44
-rw-r--r--ca/tests/sql-dumper.py22
-rwxr-xr-xca/tests/test-rrdp.py123
-rw-r--r--ca/tests/testpoke.py118
-rw-r--r--ca/tests/xml-parse-test.py136
-rw-r--r--ca/tests/yamlconf.py1423
-rw-r--r--ca/tests/yamltest-test-all.sh26
-rwxr-xr-x[-rw-r--r--]ca/tests/yamltest.py1535
34 files changed, 4811 insertions, 4427 deletions
diff --git a/ca/irbe_cli b/ca/irbe_cli
index 91c12aa9..15a7a30d 100755
--- a/ca/irbe_cli
+++ b/ca/irbe_cli
@@ -37,245 +37,248 @@ Command line IR back-end control program for rpkid and pubd.
# Command line processing of this program is too complex and
# idiosyncratic to be worth trying to reimplement using argparse.
+import os
import sys
import getopt
import textwrap
import rpki.left_right
-import rpki.http
+import rpki.http_simple
import rpki.x509
import rpki.config
import rpki.log
import rpki.publication
-import rpki.async
pem_out = None
-class UsageWrapper(textwrap.TextWrapper):
- """
- Call interface around Python textwrap.Textwrapper class.
- """
+# This program needs a complete rewrite. In the meantime, shut up about lint.
+# pylint: skip-file
- def __call__(self, *args):
+class UsageWrapper(textwrap.TextWrapper):
"""
- Format arguments, with TextWrapper indentation.
+ Call interface around Python textwrap.Textwrapper class.
"""
- return self.fill(textwrap.dedent(" ".join(args)))
+
+ def __call__(self, *args):
+ """
+ Format arguments, with TextWrapper indentation.
+ """
+ return self.fill(textwrap.dedent(" ".join(args)))
usage_fill = UsageWrapper(subsequent_indent = " " * 4)
class reply_elt_mixin(object):
- """
- Protocol mix-in for printout of reply PDUs.
- """
-
- is_cmd = False
-
- def client_reply_decode(self):
- pass
-
- def client_reply_show(self):
- print self.element_name
- for i in self.attributes + self.elements:
- if getattr(self, i) is not None:
- print " %s: %s" % (i, getattr(self, i))
-
-class cmd_elt_mixin(reply_elt_mixin):
- """
- Protocol mix-in for command line client element PDUs.
- """
-
- is_cmd = True
-
- ## @var excludes
- # XML attributes and elements that should not be allowed as command
- # line arguments.
- excludes = ()
-
- @classmethod
- def usage(cls):
"""
- Generate usage message for this PDU.
+ Protocol mix-in for printout of reply PDUs.
"""
- args = " ".join("--" + x + "=" for x in cls.attributes + cls.elements if x not in cls.excludes)
- bools = " ".join("--" + x for x in cls.booleans)
- if args and bools:
- return args + " " + bools
- else:
- return args or bools
- def client_getopt(self, argv):
- """
- Parse options for this class.
- """
- # pylint: disable=W0621
- opts, argv = getopt.getopt(argv, "", [x + "=" for x in self.attributes + self.elements if x not in self.excludes] + list(self.booleans))
- for o, a in opts:
- o = o[2:]
- handler = getattr(self, "client_query_" + o, None)
- if handler is not None:
- handler(a)
- elif o in self.booleans:
- setattr(self, o, True)
- else:
- assert o in self.attributes
- setattr(self, o, a)
- return argv
-
- def client_query_bpki_cert(self, arg):
- """
- Special handler for --bpki_cert option.
- """
- self.bpki_cert = rpki.x509.X509(Auto_file = arg)
+ is_cmd = False
- def client_query_glue(self, arg):
- """
- Special handler for --bpki_glue option.
- """
- self.bpki_glue = rpki.x509.X509(Auto_file = arg)
+ def client_reply_decode(self):
+ pass
- def client_query_bpki_cms_cert(self, arg):
- """
- Special handler for --bpki_cms_cert option.
- """
- self.bpki_cms_cert = rpki.x509.X509(Auto_file = arg)
+ def client_reply_show(self):
+ print self.element_name
+ for i in self.attributes + self.elements:
+ if getattr(self, i) is not None:
+ print " %s: %s" % (i, getattr(self, i))
- def client_query_cms_glue(self, arg):
+class cmd_elt_mixin(reply_elt_mixin):
"""
- Special handler for --bpki_cms_glue option.
+ Protocol mix-in for command line client element PDUs.
"""
- self.bpki_cms_glue = rpki.x509.X509(Auto_file = arg)
-class cmd_msg_mixin(object):
- """
- Protocol mix-in for command line client message PDUs.
- """
+ is_cmd = True
+
+ ## @var excludes
+ # XML attributes and elements that should not be allowed as command
+ # line arguments.
+ excludes = ()
+
+ @classmethod
+ def usage(cls):
+ """
+ Generate usage message for this PDU.
+ """
+ args = " ".join("--" + x + "=" for x in cls.attributes + cls.elements if x not in cls.excludes)
+ bools = " ".join("--" + x for x in cls.booleans)
+ if args and bools:
+ return args + " " + bools
+ else:
+ return args or bools
+
+ def client_getopt(self, argv):
+ """
+ Parse options for this class.
+ """
+ # pylint: disable=W0621
+ opts, argv = getopt.getopt(argv, "", [x + "=" for x in self.attributes + self.elements if x not in self.excludes] + list(self.booleans))
+ for o, a in opts:
+ o = o[2:]
+ handler = getattr(self, "client_query_" + o, None)
+ if handler is not None:
+ handler(a)
+ elif o in self.booleans:
+ setattr(self, o, True)
+ else:
+ assert o in self.attributes
+ setattr(self, o, a)
+ return argv
+
+ def client_query_bpki_cert(self, arg):
+ """
+ Special handler for --bpki_cert option.
+ """
+ self.bpki_cert = rpki.x509.X509(Auto_file = arg)
+
+ def client_query_glue(self, arg):
+ """
+ Special handler for --bpki_glue option.
+ """
+ self.bpki_glue = rpki.x509.X509(Auto_file = arg)
+
+ def client_query_bpki_cms_cert(self, arg):
+ """
+ Special handler for --bpki_cms_cert option.
+ """
+ self.bpki_cms_cert = rpki.x509.X509(Auto_file = arg)
+
+ def client_query_cms_glue(self, arg):
+ """
+ Special handler for --bpki_cms_glue option.
+ """
+ self.bpki_cms_glue = rpki.x509.X509(Auto_file = arg)
- @classmethod
- def usage(cls):
+class cmd_msg_mixin(object):
"""
- Generate usage message for this PDU.
+ Protocol mix-in for command line client message PDUs.
"""
- for k, v in cls.pdus.items():
- if v.is_cmd:
- print usage_fill(k, v.usage())
+
+ @classmethod
+ def usage(cls):
+ """
+ Generate usage message for this PDU.
+ """
+ for k, v in cls.pdus.items():
+ if v.is_cmd:
+ print usage_fill(k, v.usage())
# left-right protcol
class left_right_msg(cmd_msg_mixin, rpki.left_right.msg):
- class self_elt(cmd_elt_mixin, rpki.left_right.self_elt):
- pass
+ class self_elt(cmd_elt_mixin, rpki.left_right.self_elt):
+ pass
- class bsc_elt(cmd_elt_mixin, rpki.left_right.bsc_elt):
+ class bsc_elt(cmd_elt_mixin, rpki.left_right.bsc_elt):
- excludes = ("pkcs10_request",)
+ excludes = ("pkcs10_request",)
- def client_query_signing_cert(self, arg):
- """--signing_cert option."""
- self.signing_cert = rpki.x509.X509(Auto_file = arg)
+ def client_query_signing_cert(self, arg):
+ """--signing_cert option."""
+ self.signing_cert = rpki.x509.X509(Auto_file = arg)
- def client_query_signing_cert_crl(self, arg):
- """--signing_cert_crl option."""
- self.signing_cert_crl = rpki.x509.CRL(Auto_file = arg)
+ def client_query_signing_cert_crl(self, arg):
+ """--signing_cert_crl option."""
+ self.signing_cert_crl = rpki.x509.CRL(Auto_file = arg)
- def client_reply_decode(self):
- global pem_out
- if pem_out is not None and self.pkcs10_request is not None:
- if isinstance(pem_out, str):
- pem_out = open(pem_out, "w")
- pem_out.write(self.pkcs10_request.get_PEM())
+ def client_reply_decode(self):
+ global pem_out
+ if pem_out is not None and self.pkcs10_request is not None:
+ if isinstance(pem_out, str):
+ pem_out = open(pem_out, "w")
+ pem_out.write(self.pkcs10_request.get_PEM())
- class parent_elt(cmd_elt_mixin, rpki.left_right.parent_elt):
- pass
+ class parent_elt(cmd_elt_mixin, rpki.left_right.parent_elt):
+ pass
- class child_elt(cmd_elt_mixin, rpki.left_right.child_elt):
- pass
+ class child_elt(cmd_elt_mixin, rpki.left_right.child_elt):
+ pass
- class repository_elt(cmd_elt_mixin, rpki.left_right.repository_elt):
- pass
+ class repository_elt(cmd_elt_mixin, rpki.left_right.repository_elt):
+ pass
- class list_published_objects_elt(cmd_elt_mixin, rpki.left_right.list_published_objects_elt):
- excludes = ("uri",)
+ class list_published_objects_elt(cmd_elt_mixin, rpki.left_right.list_published_objects_elt):
+ excludes = ("uri",)
- class list_received_resources_elt(cmd_elt_mixin, rpki.left_right.list_received_resources_elt):
- excludes = ("parent_handle", "notBefore", "notAfter", "uri", "sia_uri", "aia_uri", "asn", "ipv4", "ipv6")
+ class list_received_resources_elt(cmd_elt_mixin, rpki.left_right.list_received_resources_elt):
+ excludes = ("parent_handle", "notBefore", "notAfter", "uri", "sia_uri", "aia_uri", "asn", "ipv4", "ipv6")
- class report_error_elt(reply_elt_mixin, rpki.left_right.report_error_elt):
- pass
+ class report_error_elt(reply_elt_mixin, rpki.left_right.report_error_elt):
+ pass
- pdus = dict((x.element_name, x)
- for x in (self_elt, bsc_elt, parent_elt, child_elt, repository_elt,
- list_published_objects_elt, list_received_resources_elt, report_error_elt))
+ pdus = dict((x.element_name, x)
+ for x in (self_elt, bsc_elt, parent_elt, child_elt, repository_elt,
+ list_published_objects_elt, list_received_resources_elt, report_error_elt))
class left_right_sax_handler(rpki.left_right.sax_handler):
- pdu = left_right_msg
+ pdu = left_right_msg
class left_right_cms_msg(rpki.left_right.cms_msg):
- saxify = left_right_sax_handler.saxify
+ saxify = left_right_sax_handler.saxify
# Publication protocol
class publication_msg(cmd_msg_mixin, rpki.publication.msg):
- class config_elt(cmd_elt_mixin, rpki.publication.config_elt):
+ class config_elt(cmd_elt_mixin, rpki.publication.config_elt):
- def client_query_bpki_crl(self, arg):
- """
- Special handler for --bpki_crl option.
- """
- self.bpki_crl = rpki.x509.CRL(Auto_file = arg)
+ def client_query_bpki_crl(self, arg):
+ """
+ Special handler for --bpki_crl option.
+ """
+ self.bpki_crl = rpki.x509.CRL(Auto_file = arg)
- class client_elt(cmd_elt_mixin, rpki.publication.client_elt):
- pass
+ class client_elt(cmd_elt_mixin, rpki.publication.client_elt):
+ pass
- class certificate_elt(cmd_elt_mixin, rpki.publication.certificate_elt):
- pass
+ class certificate_elt(cmd_elt_mixin, rpki.publication.certificate_elt):
+ pass
- class crl_elt(cmd_elt_mixin, rpki.publication.crl_elt):
- pass
+ class crl_elt(cmd_elt_mixin, rpki.publication.crl_elt):
+ pass
- class manifest_elt(cmd_elt_mixin, rpki.publication.manifest_elt):
- pass
+ class manifest_elt(cmd_elt_mixin, rpki.publication.manifest_elt):
+ pass
- class roa_elt(cmd_elt_mixin, rpki.publication.roa_elt):
- pass
+ class roa_elt(cmd_elt_mixin, rpki.publication.roa_elt):
+ pass
- class report_error_elt(reply_elt_mixin, rpki.publication.report_error_elt):
- pass
+ class report_error_elt(reply_elt_mixin, rpki.publication.report_error_elt):
+ pass
- class ghostbuster_elt(cmd_elt_mixin, rpki.publication.ghostbuster_elt):
- pass
+ class ghostbuster_elt(cmd_elt_mixin, rpki.publication.ghostbuster_elt):
+ pass
- pdus = dict((x.element_name, x)
- for x in (config_elt, client_elt, certificate_elt, crl_elt,
- manifest_elt, roa_elt, report_error_elt,
- ghostbuster_elt))
+ pdus = dict((x.element_name, x)
+ for x in (config_elt, client_elt, certificate_elt, crl_elt,
+ manifest_elt, roa_elt, report_error_elt,
+ ghostbuster_elt))
class publication_sax_handler(rpki.publication.sax_handler):
- pdu = publication_msg
+ pdu = publication_msg
class publication_cms_msg(rpki.publication.cms_msg):
- saxify = publication_sax_handler.saxify
+ saxify = publication_sax_handler.saxify
# Usage
top_opts = ["config=", "help", "pem_out=", "quiet", "verbose"]
def usage(code = 1):
- if __doc__ is not None:
- print __doc__.strip()
+ if __doc__ is not None:
+ print __doc__.strip()
+ print
+ print "Usage:"
+ print
+ print "# Top-level options:"
+ print usage_fill(*["--" + x for x in top_opts])
+ print
+ print "# left-right protocol:"
+ left_right_msg.usage()
print
- print "Usage:"
- print
- print "# Top-level options:"
- print usage_fill(*["--" + x for x in top_opts])
- print
- print "# left-right protocol:"
- left_right_msg.usage()
- print
- print "# publication protocol:"
- publication_msg.usage()
- sys.exit(code)
+ print "# publication protocol:"
+ publication_msg.usage()
+ sys.exit(code)
# Main program
@@ -284,64 +287,45 @@ rpki.log.init("irbe_cli")
argv = sys.argv[1:]
if not argv:
- usage(0)
+ usage(0)
cfg_file = None
verbose = True
opts, argv = getopt.getopt(argv, "c:hpqv?", top_opts)
for o, a in opts:
- if o in ("-?", "-h", "--help"):
- usage(0)
- elif o in ("-c", "--config"):
- cfg_file = a
- elif o in ("-p", "--pem_out"):
- pem_out = a
- elif o in ("-q", "--quiet"):
- verbose = False
- elif o in ("-v", "--verbose"):
- verbose = True
+ if o in ("-?", "-h", "--help"):
+ usage(0)
+ elif o in ("-c", "--config"):
+ cfg_file = a
+ elif o in ("-p", "--pem_out"):
+ pem_out = a
+ elif o in ("-q", "--quiet"):
+ verbose = False
+ elif o in ("-v", "--verbose"):
+ verbose = True
if not argv:
- usage(1)
+ usage(1)
-cfg = rpki.config.parser(cfg_file, "irbe_cli")
+cfg = rpki.config.parser(set_filename = cfg_file, section = "irbe_cli")
q_msg_left_right = []
q_msg_publication = []
while argv:
- if argv[0] in left_right_msg.pdus:
- q_pdu = left_right_msg.pdus[argv[0]]()
- q_msg = q_msg_left_right
- elif argv[0] in publication_msg.pdus:
- q_pdu = publication_msg.pdus[argv[0]]()
- q_msg = q_msg_publication
- else:
- usage(1)
- argv = q_pdu.client_getopt(argv[1:])
- q_msg.append(q_pdu)
-
-import django
-
-from django.conf import settings
-
-settings.configure(
- DATABASES = { "default" : {
- "ENGINE" : "django.db.backends.mysql",
- "NAME" : cfg.get("sql-database", section = "irdbd"),
- "USER" : cfg.get("sql-username", section = "irdbd"),
- "PASSWORD" : cfg.get("sql-password", section = "irdbd"),
- "HOST" : "",
- "PORT" : "",
- "OPTIONS" : { "init_command": "SET storage_engine=INNODB" }}},
- INSTALLED_APPS = ("rpki.irdb",),
- MIDDLEWARE_CLASSES = (),
-)
-
-if django.VERSION >= (1, 7):
- from django.apps import apps
- apps.populate(settings.INSTALLED_APPS)
+ if argv[0] in left_right_msg.pdus:
+ q_pdu = left_right_msg.pdus[argv[0]]()
+ q_msg = q_msg_left_right
+ elif argv[0] in publication_msg.pdus:
+ q_pdu = publication_msg.pdus[argv[0]]()
+ q_msg = q_msg_publication
+ else:
+ usage(1)
+ argv = q_pdu.client_getopt(argv[1:])
+ q_msg.append(q_pdu)
+
+os.environ.update(DJANGO_SETTINGS_MODULE = "rpki.django_settings.irdb")
import rpki.irdb
@@ -350,46 +334,36 @@ irbe = server_ca.ee_certificates.get(purpose = "irbe")
if q_msg_left_right:
- class left_right_proto(object):
- cms_msg = left_right_cms_msg
- msg = left_right_msg
-
- rpkid = server_ca.ee_certificates.get(purpose = "rpkid")
+ rpkid = server_ca.ee_certificates.get(purpose = "rpkid")
- rpkid_url = "http://%s:%s/left-right/" % (
- cfg.get("server-host", section = "rpkid"),
- cfg.get("server-port", section = "rpkid"))
+ rpkid_url = "http://%s:%s/left-right/" % (
+ cfg.get("server-host", section = "rpkid"),
+ cfg.get("server-port", section = "rpkid"))
- call_rpkid = rpki.async.sync_wrapper(rpki.http.caller(
- proto = left_right_proto,
- client_key = irbe.private_key,
- client_cert = irbe.certificate,
- server_ta = server_ca.certificate,
- server_cert = rpkid.certificate,
- url = rpkid_url,
- debug = verbose))
-
- call_rpkid(*q_msg_left_right)
+ rpki.http_simple.client(
+ proto_cms_msg = left_right_cms_msg,
+ client_key = irbe.private_key,
+ client_cert = irbe.certificate,
+ server_ta = server_ca.certificate,
+ server_cert = rpkid.certificate,
+ url = rpkid_url,
+ debug = verbose,
+ q_msg = left_right_msg(*q_msg_left_right))
if q_msg_publication:
- class publication_proto(object):
- msg = publication_msg
- cms_msg = publication_cms_msg
-
- pubd = server_ca.ee_certificates.get(purpose = "pubd")
-
- pubd_url = "http://%s:%s/control/" % (
- cfg.get("server-host", section = "pubd"),
- cfg.get("server-port", section = "pubd"))
-
- call_pubd = rpki.async.sync_wrapper(rpki.http.caller(
- proto = publication_proto,
- client_key = irbe.private_key,
- client_cert = irbe.certificate,
- server_ta = server_ca.certificate,
- server_cert = pubd.certificate,
- url = pubd_url,
- debug = verbose))
-
- call_pubd(*q_msg_publication)
+ pubd = server_ca.ee_certificates.get(purpose = "pubd")
+
+ pubd_url = "http://%s:%s/control/" % (
+ cfg.get("server-host", section = "pubd"),
+ cfg.get("server-port", section = "pubd"))
+
+ rpki.http_simple.client(
+ proto_cms_msg = publication_cms_msg,
+ client_key = irbe.private_key,
+ client_cert = irbe.certificate,
+ server_ta = server_ca.certificate,
+ server_cert = pubd.certificate,
+ url = pubd_url,
+ debug = verbose,
+ q_msg = publication_msg(*q_msg_publication))
diff --git a/ca/irdbd b/ca/irdbd
index 493e3d72..c5ca652c 100755
--- a/ca/irdbd
+++ b/ca/irdbd
@@ -17,5 +17,5 @@
# PERFORMANCE OF THIS SOFTWARE.
if __name__ == "__main__":
- import rpki.irdbd
- rpki.irdbd.main()
+ import rpki.irdbd
+ rpki.irdbd.main()
diff --git a/ca/pubd b/ca/pubd
index 7d8ecbfa..69d3b476 100755
--- a/ca/pubd
+++ b/ca/pubd
@@ -17,5 +17,5 @@
# PERFORMANCE OF THIS SOFTWARE.
if __name__ == "__main__":
- import rpki.pubd
- rpki.pubd.main()
+ import rpki.pubd
+ rpki.pubd.main()
diff --git a/ca/rootd b/ca/rootd
index cb59f958..6caeb88f 100755
--- a/ca/rootd
+++ b/ca/rootd
@@ -17,5 +17,5 @@
# PERFORMANCE OF THIS SOFTWARE.
if __name__ == "__main__":
- import rpki.rootd
- rpki.rootd.main()
+ import rpki.rootd
+ rpki.rootd.main()
diff --git a/ca/rpki-confgen b/ca/rpki-confgen
index 07c87f0f..beb8506d 100755
--- a/ca/rpki-confgen
+++ b/ca/rpki-confgen
@@ -38,254 +38,259 @@ xml8_wrapper = textwrap.TextWrapper(initial_indent = space8, subsequent_indent =
class Option(object):
- def __init__(self, name, value, doc):
- self.name = name
- self.value = value
- self.doc = doc
-
- @property
- def width(self):
- return len(self.name)
-
- def to_xml(self):
- x = Element("option", name = self.name)
- if self.value is not None:
- x.set("value", self.value)
- for d in self.doc:
- SubElement(x, "doc").text = "\n" + xml8_wrapper.fill(d) + "\n" + space6
- return x
-
- def to_wiki(self, f):
- f.write("\n== %s == #%s\n" % (self.name, self.name))
- for d in self.doc:
- f.write("\n%s\n" % wiki_wrapper.fill(d))
- if self.value is None:
- f.write("\n%s\n" % wiki_wrapper.fill("No default value."))
- else:
- f.write("\n{{{\n#!ini\n%s = %s\n}}}\n" % (self.name, self.value))
-
- def to_conf(self, f, width):
- for i, d in enumerate(self.doc):
- f.write("%s\n%s\n" % ("" if i == 0 else "#", conf_wrapper.fill(d)))
- if self.value is None:
- f.write("\n#%-*s = ???\n" % (width - 1, self.name))
- else:
- f.write("\n%-*s = %s\n" % (width, self.name, self.value))
+ def __init__(self, name, value, doc):
+ self.name = name
+ self.value = value
+ self.doc = doc
+
+ @property
+ def width(self):
+ return len(self.name)
+
+ def to_xml(self):
+ x = Element("option", name = self.name)
+ if self.value is not None:
+ x.set("value", self.value)
+ for d in self.doc:
+ SubElement(x, "doc").text = "\n" + xml8_wrapper.fill(d) + "\n" + space6
+ return x
+
+ def to_wiki(self, f):
+ f.write("\n== %s == #%s\n" % (self.name, self.name))
+ for d in self.doc:
+ f.write("\n%s\n" % wiki_wrapper.fill(d))
+ if self.value is None:
+ f.write("\n%s\n" % wiki_wrapper.fill("No default value."))
+ else:
+ f.write("\n{{{\n#!ini\n%s = %s\n}}}\n" % (self.name, self.value))
+
+ def to_conf(self, f, width):
+ for i, d in enumerate(self.doc):
+ f.write("%s\n%s\n" % ("" if i == 0 else "#", conf_wrapper.fill(d)))
+ if self.value is None:
+ f.write("\n#%-*s = ???\n" % (width - 1, self.name))
+ else:
+ f.write("\n%-*s = %s\n" % (width, self.name, self.value))
class Section(object):
- def __init__(self, name):
- self.name = name
- self.doc = []
- self.options = []
-
- @property
- def width(self):
- return max(o.width for o in self.options)
-
- @classmethod
- def from_xml(cls, elt):
- self = cls(name = elt.get("name"))
- for x in elt.iterchildren("doc"):
- self.doc.append(" ".join(x.text.split()))
- for x in elt.iterchildren("option"):
- self.options.append(Option(name = x.get("name"), value = x.get("value"),
- doc = [" ".join(d.text.split())
- for d in x.iterchildren("doc")]))
- return self
-
- def to_xml(self):
- x = Element("section", name = self.name)
- for d in self.doc:
- SubElement(x, "doc").text = "\n" + xml6_wrapper.fill(d) + "\n" + space4
- x.extend(o.to_xml() for o in self.options)
- return x
-
- def to_wiki(self, f):
- f.write("\n= [%s] section = #%s\n" % (self.name, self.name))
- for d in self.doc:
- f.write("\n%s\n" % wiki_wrapper.fill(d))
- for o in self.options:
- o.to_wiki(f)
-
- def to_conf(self, f, width):
- f.write("\n" + "#" * 78 + "\n\n[" + self.name + "]\n")
- if self.doc:
- f.write("\n##")
- for i, d in enumerate(self.doc):
- f.write("%s\n%s\n" % ("" if i == 0 else "#", conf_wrapper.fill(d)))
- f.write("##\n")
- for o in self.options:
- o.to_conf(f, width)
+ def __init__(self, name):
+ self.name = name
+ self.doc = []
+ self.options = []
+
+ @property
+ def width(self):
+ return max(o.width for o in self.options)
+
+ @classmethod
+ def from_xml(cls, elt):
+ self = cls(name = elt.get("name"))
+ for x in elt.iterchildren("doc"):
+ self.doc.append(" ".join(x.text.split()))
+ for x in elt.iterchildren("option"):
+ self.options.append(Option(name = x.get("name"), value = x.get("value"),
+ doc = [" ".join(d.text.split())
+ for d in x.iterchildren("doc")]))
+ return self
+
+ def to_xml(self):
+ x = Element("section", name = self.name)
+ for d in self.doc:
+ SubElement(x, "doc").text = "\n" + xml6_wrapper.fill(d) + "\n" + space4
+ x.extend(o.to_xml() for o in self.options)
+ return x
+
+ def to_wiki(self, f):
+ f.write("\n= [%s] section = #%s\n" % (self.name, self.name))
+ for d in self.doc:
+ f.write("\n%s\n" % wiki_wrapper.fill(d))
+ for o in self.options:
+ o.to_wiki(f)
+
+ def to_conf(self, f, width):
+ f.write("\n" + "#" * 78 + "\n\n[" + self.name + "]\n")
+ if self.doc:
+ f.write("\n##")
+ for i, d in enumerate(self.doc):
+ f.write("%s\n%s\n" % ("" if i == 0 else "#", conf_wrapper.fill(d)))
+ f.write("##\n")
+ for o in self.options:
+ o.to_conf(f, width)
def wiki_header(f, ident, toc):
- f.write("\n".join((
- "{{{",
- "#!comment",
- "",
- star78,
- "THIS PAGE WAS GENERATED AUTOMATICALLY, DO NOT EDIT.",
- "",
- "Generated from " + ident,
- " by $Id$",
- star78,
- "",
- "}}}",
- "")))
- if toc is not None:
- f.write("[[TracNav(%s)]]\n" % toc)
- f.write("[[PageOutline]]\n")
+ f.write("\n".join((
+ "{{{",
+ "#!comment",
+ "",
+ star78,
+ "THIS PAGE WAS GENERATED AUTOMATICALLY, DO NOT EDIT.",
+ "",
+ "Generated from " + ident,
+ " by $Id$",
+ star78,
+ "",
+ "}}}",
+ "")))
+ if toc is not None:
+ f.write("[[TracNav(%s)]]\n" % toc)
+ f.write("[[PageOutline]]\n")
def conf_header(f, ident):
- f.write("\n".join((
- "# Automatically generated. Edit as needed, but be careful of overwriting.",
- "#",
- "# Generated from " + ident,
- "# by $Id$",
- "")))
+ f.write("\n".join((
+ "# Automatically generated. Edit as needed, but be careful of overwriting.",
+ "#",
+ "# Generated from " + ident,
+ "# by $Id$",
+ "")))
# http://stackoverflow.com/questions/9027028/argparse-argument-order
class CustomAction(argparse.Action):
- def __call__(self, parser, namespace, values, option_string = None):
- if not "ordered_args" in namespace:
- namespace.ordered_args = []
- namespace.ordered_args.append((self.dest, values))
+ def __call__(self, parser, namespace, values, option_string = None):
+ if not "ordered_args" in namespace:
+ namespace.ordered_args = []
+ namespace.ordered_args.append((self.dest, values))
class CustomFlagAction(argparse.Action):
- def __init__(self, option_strings, dest, default = None,
- required = False, help = None): # pylint: disable=W0622
- super(CustomFlagAction, self).__init__(
- option_strings = option_strings, dest = dest, nargs = 0,
- const = None, default = default, required = required, help = help)
- def __call__(self, parser, namespace, values, option_string = None):
- if not "ordered_args" in namespace:
- namespace.ordered_args = []
- namespace.ordered_args.append((self.dest, None))
+ def __init__(self, option_strings, dest, default = None,
+ required = False, help = None): # pylint: disable=W0622
+ super(CustomFlagAction, self).__init__(
+ option_strings = option_strings,
+ dest = dest,
+ nargs = 0,
+ const = None,
+ default = default,
+ required = required,
+ help = help)
+ def __call__(self, parser, namespace, values, option_string = None):
+ if not "ordered_args" in namespace:
+ namespace.ordered_args = []
+ namespace.ordered_args.append((self.dest, None))
class main(object):
- def __init__(self):
- self.sections = []
- self.section_map = None
- self.option_map = None
- self.ident = None
- self.toc = None
-
- parser = argparse.ArgumentParser(description = __doc__)
- parser.add_argument("--read-xml", metavar = "FILE", action = CustomAction,
- required = True, type = argparse.FileType("r"),
- help = "XML input file defining sections and options")
- parser.add_argument("--write-xml", metavar = "FILE", action = CustomAction,
- help = "XML file to write")
- parser.add_argument("--write-wiki", metavar = "FILE", action = CustomAction,
- help = "TracWiki file to write")
- parser.add_argument("--write-conf", metavar = "FILE", action = CustomAction,
- help = "rpki.conf configuration file to write")
- parser.add_argument("--set", metavar = "VARVAL", action = CustomAction,
- help = "variable setting in form \"VAR=VAL\"")
- parser.add_argument("--pwgen", metavar = "VAR", action = CustomAction,
- help = "set variable to generated password")
- parser.add_argument("--toc", metavar = "TRACNAV", action = CustomAction,
- help = "set TOC value to use with TracNav plugin")
- parser.add_argument("--autoconf", action = CustomFlagAction,
- help = "configure [autoconf] section")
- args = parser.parse_args()
-
- for cmd, arg in args.ordered_args:
- getattr(self, "do_" + cmd)(arg)
-
-
- def do_read_xml(self, arg):
- self.option_map = None
- root = ElementTree(file = arg).getroot()
- self.ident = root.get("ident")
- self.sections.extend(Section.from_xml(x) for x in root)
- self.option_map = {}
- self.section_map = {}
- for section in self.sections:
- if section.name in self.section_map:
- sys.exit("Duplicate section %s" % section.name)
- self.section_map[section.name] = section
- for option in section.options:
- name = (section.name, option.name)
- if name in self.option_map:
- sys.exit("Duplicate option %s::%s" % name)
- self.option_map[name] = option
-
-
- def do_set(self, arg):
- try:
- name, value = arg.split("=", 1)
- section, option = name.split("::")
- except ValueError:
- sys.exit("Couldn't parse --set specification \"%s\"" % arg)
- name = (section, option)
- if name not in self.option_map:
- sys.exit("Couldn't find option %s::%s" % name)
- self.option_map[name].value = value
-
-
- def do_pwgen(self, arg):
- try:
- section, option = arg.split("::")
- except ValueError:
- sys.exit("Couldn't parse --pwgen specification \"%s\"" % arg)
- name = (section, option)
- if name not in self.option_map:
- sys.exit("Couldn't find option %s::%s" % name)
- self.option_map[name].value = base64.urlsafe_b64encode(os.urandom(66))
-
-
- def do_autoconf(self, ignored):
- try:
- import rpki.autoconf
- for option in self.section_map["autoconf"].options:
+ def __init__(self):
+ self.sections = []
+ self.section_map = None
+ self.option_map = None
+ self.ident = None
+ self.toc = None
+
+ parser = argparse.ArgumentParser(description = __doc__)
+ parser.add_argument("--read-xml", metavar = "FILE", action = CustomAction,
+ required = True, type = argparse.FileType("r"),
+ help = "XML input file defining sections and options")
+ parser.add_argument("--write-xml", metavar = "FILE", action = CustomAction,
+ help = "XML file to write")
+ parser.add_argument("--write-wiki", metavar = "FILE", action = CustomAction,
+ help = "TracWiki file to write")
+ parser.add_argument("--write-conf", metavar = "FILE", action = CustomAction,
+ help = "rpki.conf configuration file to write")
+ parser.add_argument("--set", metavar = "VARVAL", action = CustomAction,
+ help = "variable setting in form \"VAR=VAL\"")
+ parser.add_argument("--pwgen", metavar = "VAR", action = CustomAction,
+ help = "set variable to generated password")
+ parser.add_argument("--toc", metavar = "TRACNAV", action = CustomAction,
+ help = "set TOC value to use with TracNav plugin")
+ parser.add_argument("--autoconf", action = CustomFlagAction,
+ help = "configure [autoconf] section")
+ args = parser.parse_args()
+
+ for cmd, arg in args.ordered_args:
+ getattr(self, "do_" + cmd)(arg)
+
+
+ def do_read_xml(self, arg):
+ self.option_map = None
+ root = ElementTree(file = arg).getroot()
+ self.ident = root.get("ident")
+ self.sections.extend(Section.from_xml(x) for x in root)
+ self.option_map = {}
+ self.section_map = {}
+ for section in self.sections:
+ if section.name in self.section_map:
+ sys.exit("Duplicate section %s" % section.name)
+ self.section_map[section.name] = section
+ for option in section.options:
+ name = (section.name, option.name)
+ if name in self.option_map:
+ sys.exit("Duplicate option %s::%s" % name)
+ self.option_map[name] = option
+
+
+ def do_set(self, arg):
try:
- option.value = getattr(rpki.autoconf, option.name)
- except AttributeError:
- pass
- except ImportError:
- sys.exit("rpki.autoconf module is not available")
- except KeyError:
- sys.exit("Couldn't find autoconf section")
+ name, value = arg.split("=", 1)
+ section, option = name.split("::")
+ except ValueError:
+ sys.exit("Couldn't parse --set specification \"%s\"" % arg)
+ name = (section, option)
+ if name not in self.option_map:
+ sys.exit("Couldn't find option %s::%s" % name)
+ self.option_map[name].value = value
- def do_write_xml(self, arg):
- x = Element("configuration", ident = self.ident)
- x.extend(s.to_xml() for s in self.sections)
- ElementTree(x).write(arg, pretty_print = True, encoding = "us-ascii")
-
-
- def do_write_wiki(self, arg):
- if "%" in arg:
- for section in self.sections:
- with open(arg % section.name, "w") as f:
- wiki_header(f, self.ident, self.toc)
- section.to_wiki(f)
- else:
- with open(arg, "w") as f:
- for i, section in enumerate(self.sections):
- if i == 0:
- wiki_header(f, self.ident, self.toc)
- else:
- f.write("\f\n")
- section.to_wiki(f)
-
-
- def do_write_conf(self, arg):
- with open(arg, "w") as f:
- conf_header(f, self.ident)
- width = max(s.width for s in self.sections)
- for section in self.sections:
- section.to_conf(f, width)
+ def do_pwgen(self, arg):
+ try:
+ section, option = arg.split("::")
+ except ValueError:
+ sys.exit("Couldn't parse --pwgen specification \"%s\"" % arg)
+ name = (section, option)
+ if name not in self.option_map:
+ sys.exit("Couldn't find option %s::%s" % name)
+ self.option_map[name].value = base64.urlsafe_b64encode(os.urandom(66))
- def do_toc(self, arg):
- self.toc = arg
+ def do_autoconf(self, ignored):
+ try:
+ import rpki.autoconf
+ for option in self.section_map["autoconf"].options:
+ try:
+ option.value = getattr(rpki.autoconf, option.name)
+ except AttributeError:
+ pass
+ except ImportError:
+ sys.exit("rpki.autoconf module is not available")
+ except KeyError:
+ sys.exit("Couldn't find autoconf section")
+
+
+ def do_write_xml(self, arg):
+ x = Element("configuration", ident = self.ident)
+ x.extend(s.to_xml() for s in self.sections)
+ ElementTree(x).write(arg, pretty_print = True, encoding = "us-ascii")
+
+
+ def do_write_wiki(self, arg):
+ if "%" in arg:
+ for section in self.sections:
+ with open(arg % section.name, "w") as f:
+ wiki_header(f, self.ident, self.toc)
+ section.to_wiki(f)
+ else:
+ with open(arg, "w") as f:
+ for i, section in enumerate(self.sections):
+ if i == 0:
+ wiki_header(f, self.ident, self.toc)
+ else:
+ f.write("\f\n")
+ section.to_wiki(f)
+
+
+ def do_write_conf(self, arg):
+ with open(arg, "w") as f:
+ conf_header(f, self.ident)
+ width = max(s.width for s in self.sections)
+ for section in self.sections:
+ section.to_conf(f, width)
+
+
+ def do_toc(self, arg):
+ self.toc = arg
if __name__ == "__main__":
- main()
+ main()
diff --git a/ca/rpki-confgen.xml b/ca/rpki-confgen.xml
index ac36d3db..c100b7b9 100644
--- a/ca/rpki-confgen.xml
+++ b/ca/rpki-confgen.xml
@@ -186,17 +186,17 @@
</doc>
</option>
- <option name = "publication_root_cert_directory"
- value = "${myrpki::publication_base_directory}.root">
+ <option name = "rrdp_publication_base_directory"
+ value = "${autoconf::datarootdir}/rpki/rrdp-publication">
<doc>
- Root of local directory tree where rootd (sigh) should write out
- published data. This is just like publication_base_directory, but
- rootd is too dumb to use pubd and needs its own directory in
- which to write one certificate, one CRL, and one manifest.
- Neither rootd nor rsyncd much cares //where// you tell them to put
- this stuff, the important thing is that the rsync URIs in
- generated certificates match up with the published objects so that
- relying parties can find and verify rootd's published outputs.
+ Root of local directory tree where pubd should write out RRDP
+ files. You need to configure this, and the configuration
+ should match up with the directory where you point the web
+ server (usually Apache) that serves the RRDP files. Neither
+ pubd nor Apache much cares //where// you tell it to put this
+ stuff, the important thing is that all the URIs match up so
+ that relying parties can find and verify rpkid's published
+ outputs.
</doc>
</option>
@@ -209,15 +209,6 @@
</doc>
</option>
- <option name = "publication_root_module"
- value = "root">
- <doc>
- rsyncd module name corresponding to publication_root_cert_directory.
- This has to match the module you configured into `rsyncd.conf`.
- Leave this alone unless you have some need to change it.
- </doc>
- </option>
-
<option name = "publication_rsync_server"
value = "${myrpki::pubd_server_host}">
<doc>
@@ -226,6 +217,15 @@
</doc>
</option>
+ <option name = "publication_rrdp_notification_uri"
+ value = "https://${myrpki::pubd_server_host}/rrdp/notify.xml">
+
+ <doc>
+ URI for RRDP notification file. In most cases this should be
+ a HTTPS URL for the notify.xml file on the publication server.
+ </doc>
+ </option>
+
<option name = "start_rpkid"
value = "${myrpki::run_rpkid}">
<doc>
@@ -282,11 +282,25 @@
</doc>
</option>
+ <option name = "shared_sql_engine"
+ value = "mysql">
+ <doc>
+ Database engine to use. Default is MySQL, because that's what
+ we've been using for years. Now that all runtime database
+ access is via Django ORM, changing to another engine supported
+ by Django is just a configuration issue.
+ </doc>
+ <doc>
+ Current supported values are "mysql" (the default), "sqlite3",
+ and "postgresql".
+ </doc>
+ </option>
+
<option name = "shared_sql_username"
value = "rpki">
<doc>
If you're comfortable with having all of the databases use the
- same MySQL username, set that value here. The default setting
+ same SQL username, set that value here. The default setting
of this variable should be fine.
</doc>
</option>
@@ -294,7 +308,7 @@
<option name = "shared_sql_password">
<doc>
If you're comfortable with having all of the databases use the
- same MySQL password, set that value here. You should use a
+ same SQL password, set that value here. You should use a
locally generated password either here or in the individual
settings below. The installation process generates a random
value for this option, which satisfies this requirement, so
@@ -302,6 +316,14 @@
</doc>
</option>
+ <option name = "rpkid_sql_engine"
+ value = "${myrpki::shared_sql_engine}">
+ <doc>
+ SQL engine to use for rpkid's database. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
<option name = "rpkid_sql_database"
value = "rpkid">
<doc>
@@ -326,6 +348,14 @@
</doc>
</option>
+ <option name = "irdbd_sql_engine"
+ value = "${myrpki::shared_sql_engine}">
+ <doc>
+ SQL engine to use for irdbd's database. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
<option name = "irdbd_sql_database"
value = "irdbd">
<doc>
@@ -350,6 +380,14 @@
</doc>
</option>
+ <option name = "pubd_sql_engine"
+ value = "${myrpki::shared_sql_engine}">
+ <doc>
+ SQL engine to use for pubd's database. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
<option name = "pubd_sql_database"
value = "pubd">
<doc>
@@ -385,24 +423,31 @@
Certificates and keys may be in either DER or PEM format.
</doc>
+ <option name = "sql-engine"
+ value = "${myrpki::rpkid_sql_engine}">
+ <doc>
+ SQL engine for rpkid.
+ </doc>
+ </option>
+
<option name = "sql-database"
value = "${myrpki::rpkid_sql_database}">
<doc>
- MySQL database name for rpkid.
+ SQL database name for rpkid.
</doc>
</option>
<option name = "sql-username"
value = "${myrpki::rpkid_sql_username}">
<doc>
- MySQL user name for rpkid.
+ SQL user name for rpkid.
</doc>
</option>
<option name = "sql-password"
value = "${myrpki::rpkid_sql_password}">
<doc>
- MySQL password for rpkid.
+ SQL password for rpkid.
</doc>
</option>
@@ -490,24 +535,31 @@
configuration than the other daemons.
</doc>
+ <option name = "sql-engine"
+ value = "${myrpki::irdbd_sql_engine}">
+ <doc>
+ SQL engine for irdbd.
+ </doc>
+ </option>
+
<option name = "sql-database"
value = "${myrpki::irdbd_sql_database}">
<doc>
- MySQL database name for irdbd.
+ SQL database name for irdbd.
</doc>
</option>
<option name = "sql-username"
value = "${myrpki::irdbd_sql_username}">
<doc>
- MySQL user name for irdbd.
+ SQL user name for irdbd.
</doc>
</option>
<option name = "sql-password"
value = "${myrpki::irdbd_sql_password}">
<doc>
- MySQL password for irdbd.
+ SQL password for irdbd.
</doc>
</option>
@@ -543,24 +595,31 @@
BPKI certificates and keys may be either DER or PEM format.
</doc>
+ <option name = "sql-engine"
+ value = "${myrpki::pubd_sql_engine}">
+ <doc>
+ SQL engine for pubd.
+ </doc>
+ </option>
+
<option name = "sql-database"
value = "${myrpki::pubd_sql_database}">
<doc>
- MySQL database name for pubd.
+ SQL database name for pubd.
</doc>
</option>
<option name = "sql-username"
value = "${myrpki::pubd_sql_username}">
<doc>
- MySQL user name for pubd.
+ SQL user name for pubd.
</doc>
</option>
<option name = "sql-password"
value = "${myrpki::pubd_sql_password}">
<doc>
- MySQL password for pubd.
+ SQL password for pubd.
</doc>
</option>
@@ -577,6 +636,20 @@
</doc>
</option>
+ <option name = "rrdp-publication-base"
+ value = "${myrpki::rrdp_publication_base_directory}">
+ <doc>
+ Root of local directory tree where pubd should write out RRDP
+ files. You need to configure this, and the configuration
+ should match up with the directory where you point the web
+ server (usually Apache) that serves the RRDP files. Neither
+ pubd nor Apache much cares //where// you tell it to put this
+ stuff, the important thing is that all the URIs match up so
+ that relying parties can find and verify rpkid's published
+ outputs.
+ </doc>
+ </option>
+
<option name = "server-host"
value = "${myrpki::pubd_server_host}">
<doc>
@@ -618,6 +691,15 @@
</doc>
</option>
+ <option name = "pubd-crl"
+ value = "${myrpki::bpki_servers_directory}/ca.crl">
+ <doc>
+ Where pubd should look for the CRL covering its own BPKI EE
+ certificate. Don't change this unless you really know what
+ you are doing.
+ </doc>
+ </option>
+
<option name = "irbe-cert"
value = "${myrpki::bpki_servers_directory}/irbe.cer">
<doc>
@@ -638,10 +720,9 @@
</doc>
<doc>
- Ok, if that wasn't enough to scare you off: rootd is a mess, and
- needs to be rewritten, or, better, merged into rpkid. It
- doesn't use the publication protocol, and it requires far too
- many configuration parameters.
+ Ok, if that wasn't enough to scare you off: rootd is a mess,
+ needs to be rewritten, or, better, merged into rpkid, and
+ requires far too many configuration parameters.
</doc>
<doc>
@@ -712,6 +793,13 @@
</doc>
</option>
+ <option name = "pubd-bpki-cert">
+ <doc>
+ BPKI certificate for pubd. Don't set this unless you really
+ know what you are doing.
+ </doc>
+ </option>
+
<option name = "server-host"
value = "${myrpki::rootd_server_host}">
<doc>
@@ -726,72 +814,85 @@
</doc>
</option>
- <option name = "rpki-root-dir"
- value = "${myrpki::publication_base_directory}">
+ <option name = "rpki_data_dir"
+ value = "${myrpki::bpki_servers_directory}">
<doc>
- Where rootd should write its output. Yes, rootd should be using
- pubd instead of publishing directly, but it doesn't. This
- needs to match pubd's configuration.
+ Directory where rootd should store its RPKI data files. This
+ is only used to construct other variables, rootd itself
+ doesn't read it.
</doc>
</option>
- <option name = "rpki-base-uri"
- value = "rsync://${myrpki::publication_rsync_server}/${myrpki::publication_rsync_module}/">
+ <option name = "rpki_base_uri"
+ value = "rsync://${myrpki::publication_rsync_server}/${myrpki::publication_rsync_module}/${myrpki::handle}-root/root">
<doc>
- rsync URI corresponding to directory containing rootd's outputs.
+ rsync URI corresponding to directory containing rootd's
+ outputs. This is only used to construct other variables,
+ rootd itself doesn't read it.
</doc>
</option>
<option name = "rpki-root-cert-uri"
- value = "rsync://${myrpki::publication_rsync_server}/${myrpki::publication_root_module}/root.cer">
+ value = "${rootd::rpki_base_uri}.cer">
<doc>
rsync URI for rootd's root (self-signed) RPKI certificate.
</doc>
</option>
- <option name = "rpki-root-key"
- value = "${myrpki::bpki_servers_directory}/root.key">
+ <option name = "rpki-root-cert-file"
+ value = "${rootd::rpki_data_dir}/root.cer">
+ <doc>
+ Filename of rootd's root RPKI certificate.
+ </doc>
+ </option>
+
+ <option name = "rpki-root-key-file"
+ value = "${rootd::rpki_data_dir}/root.key">
<doc>
Private key corresponding to rootd's root RPKI certificate.
</doc>
</option>
- <option name = "rpki-root-cert"
- value = "${myrpki::publication_root_cert_directory}/root.cer">
+ <option name = "rpki-root-crl-uri"
+ value = "${rootd::rpki_base_uri}/root.crl">
<doc>
- Filename (as opposed to rsync URI) of rootd's root RPKI
- certificate.
+ URI of the CRL for rootd's root RPKI certificate.
</doc>
</option>
- <option name = "rpki-subject-pkcs10"
- value = "${myrpki::bpki_servers_directory}/rootd.subject.pkcs10">
+ <option name = "rpki-root-crl-file"
+ value = "${rootd::rpki_data_dir}/root.crl">
<doc>
- Where rootd should stash a copy of the PKCS #10 request it gets
- from its one (and only) child
+ Filename of the CRL for rootd's root RPKI certificate.
</doc>
</option>
- <option name = "rpki-subject-lifetime"
- value = "30d">
+ <option name = "rpki-root-manifest-uri"
+ value = "${rootd::rpki_base_uri}/root.mft">
<doc>
- Lifetime of the one and only RPKI certificate rootd issues.
+ URI of the manifest for rootd's root RPKI certificate.
</doc>
</option>
- <option name = "rpki-root-crl"
- value = "root.crl">
+ <option name = "rpki-root-manifest-file"
+ value = "${rootd::rpki_data_dir}/root.mft">
<doc>
- Filename (relative to rootd-base-uri and rpki-root-dir) of the CRL
- for rootd's root RPKI certificate.
+ Filename of the manifest for rootd's root RPKI certificate.
</doc>
</option>
- <option name = "rpki-root-manifest"
- value = "root.mft">
+ <option name = "rpki-subject-pkcs10-file"
+ value = "${rootd::rpki_data_dir}/subject.pkcs10">
<doc>
- Filename (relative to rootd-base-uri and rpki-root-dir) of the
- manifest for rootd's root RPKI certificate.
+ Where rootd should stash a copy of the PKCS #10 request it gets
+ from its one (and only) child
+ </doc>
+ </option>
+
+ <option name = "rpki-subject-lifetime"
+ value = "30d">
+ <doc>
+ Lifetime of the one and only RPKI certificate rootd issues.
</doc>
</option>
@@ -803,44 +904,52 @@
</doc>
</option>
- <option name = "rpki-subject-cert"
- value = "${myrpki::handle}.cer">
+ <option name = "rpki-subject-cert-uri"
+ value = "${rootd::rpki_base_uri}/${myrpki::handle}.cer">
<doc>
- Filename (relative to rootd-base-uri and rpki-root-dir) of the one
- (and only) RPKI certificate rootd issues.
+ URI of the one (and only) RPKI certificate rootd issues.
</doc>
</option>
- </section>
-
- <section name = "web_portal">
-
- <doc>
- Glue to allow the Django application to pull user configuration
- from this file rather than directly editing settings.py.
- </doc>
-
- <option name = "sql-database"
- value = "${myrpki::irdbd_sql_database}">
+ <option name = "rpki-subject-cert-file"
+ value = "${rootd::rpki_data_dir}/${myrpki::handle}.cer">
<doc>
- SQL database name the web portal should use.
+ Filename of the one (and only) RPKI certificate rootd issues.
</doc>
</option>
- <option name = "sql-username"
- value = "${myrpki::irdbd_sql_username}">
+ <option name = "pubd-contact-uri"
+ value = "http://${myrpki::pubd_server_host}:${myrpki::pubd_server_port}/client/${myrpki::handle}-root">
<doc>
- SQL user name the web portal should use.
+ URI at which rootd should contact pubd for service.
</doc>
</option>
- <option name = "sql-password"
- value = "${myrpki::irdbd_sql_password}">
+ <option name = "rrdp-notification-uri"
+ value = "${myrpki::publication_rrdp_notification_uri">
<doc>
- SQL password the web portal should use.
+ RRDP URI for inclusion in generated objects.
</doc>
</option>
+ </section>
+
+ <section name = "web_portal">
+
+ <doc>
+ Glue to allow Django to pull user configuration from this file
+ rather than requiring the user to edit settings.py.
+ </doc>
+
+ <!--
+ We used to have SQL settings for the GUI here, but since
+ they're pretty much required to be identical to the ones for
+ irdbd at this point, the duplicate entries were just another
+ chance to misconfigure something, so I removed them. Not yet
+ sure whether this was the right approach. Too much historical
+ baggage in this file.
+ -->
+
<option name = "secret-key">
<doc>
Site-specific secret key for Django.
diff --git a/ca/rpki-manage b/ca/rpki-manage
index 0d581ce9..16d0990d 100755
--- a/ca/rpki-manage
+++ b/ca/rpki-manage
@@ -1,13 +1,15 @@
#!/usr/bin/env python
import os
-from django.core.management import execute_from_command_line
# django-admin seems to have problems creating the superuser account when
# $LANG is unset or is set to something totally incompatible with UTF-8.
-if os.environ.get('LANG') in (None, "", "C"):
- os.environ['LANG'] = 'en_US.UTF-8'
-os.environ['DJANGO_SETTINGS_MODULE'] = 'rpki.gui.default_settings'
+if os.environ.get("LANG") in (None, "", "C"):
+ os.environ["LANG"] = "en_US.UTF-8"
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rpki.django_settings.gui")
+
+from django.core.management import execute_from_command_line
execute_from_command_line()
diff --git a/ca/rpki-sql-backup b/ca/rpki-sql-backup
index e60f9ae3..986e10b1 100755
--- a/ca/rpki-sql-backup
+++ b/ca/rpki-sql-backup
@@ -41,13 +41,13 @@ parser.add_argument("-o", "--output",
help = "destination for SQL dump (default: stdout)")
args = parser.parse_args()
-cfg = rpki.config.parser(args.config, "myrpki")
+cfg = rpki.config.parser(set_filename = args.config, section = "myrpki")
for name in ("rpkid", "irdbd", "pubd"):
- if cfg.getboolean("start_" + name, False):
- subprocess.check_call(
- ("mysqldump", "--add-drop-database",
- "-u", cfg.get("sql-username", section = name),
- "-p" + cfg.get("sql-password", section = name),
- "-B", cfg.get("sql-database", section = name)),
- stdout = args.output)
+ if cfg.getboolean("start_" + name, False):
+ subprocess.check_call(
+ ("mysqldump", "--add-drop-database",
+ "-u", cfg.get("sql-username", section = name),
+ "-p" + cfg.get("sql-password", section = name),
+ "-B", cfg.get("sql-database", section = name)),
+ stdout = args.output)
diff --git a/ca/rpki-sql-setup b/ca/rpki-sql-setup
index edc2c242..297571a2 100755
--- a/ca/rpki-sql-setup
+++ b/ca/rpki-sql-setup
@@ -27,7 +27,11 @@ import datetime
import rpki.config
import rpki.version
import rpki.autoconf
-import rpki.sql_schemas
+
+# This program implements its own schema versioning system as a poor
+# substitute for schema migrations. Now that we're moving to Django
+# ORM, this is pretty much useless, and should be removed at some point.
+
from rpki.mysql_import import MySQLdb, _mysql_exceptions
@@ -35,230 +39,221 @@ ER_NO_SUCH_TABLE = 1146 # See mysqld_ername.h
class RootDB(object):
- """
- Class to wrap MySQL actions that require root-equivalent access so
- we can defer such actions until we're sure they're really needed.
- Overall goal here is to prompt the user for the root password once
- at most, and not at all when not necessary.
- """
-
- def __init__(self, mysql_defaults = None):
- self.initialized = False
- self.mysql_defaults = mysql_defaults
-
- def __getattr__(self, name):
- if self.initialized:
- raise AttributeError
- if self.mysql_defaults is None:
- self.db = MySQLdb.connect(db = "mysql",
- user = "root",
- passwd = getpass.getpass("Please enter your MySQL root password: "))
- else:
- mysql_cfg = rpki.config.parser(self.mysql_defaults, "client")
- self.db = MySQLdb.connect(db = "mysql",
- user = mysql_cfg.get("user"),
- passwd = mysql_cfg.get("password"))
- self.cur = self.db.cursor()
- self.cur.execute("SHOW DATABASES")
- self.databases = set(d[0] for d in self.cur.fetchall())
- self.initialized = True
- return getattr(self, name)
-
- def close(self):
- if self.initialized:
- self.db.close()
+ """
+ Class to wrap MySQL actions that require root-equivalent access so
+ we can defer such actions until we're sure they're really needed.
+ Overall goal here is to prompt the user for the root password once
+ at most, and not at all when not necessary.
+ """
+
+ def __init__(self, mysql_defaults = None):
+ self.initialized = False
+ self.mysql_defaults = mysql_defaults
+
+ def __getattr__(self, name):
+ # pylint: disable=W0201
+ if self.initialized:
+ raise AttributeError
+ if self.mysql_defaults is None:
+ self.db = MySQLdb.connect(db = "mysql",
+ user = "root",
+ passwd = getpass.getpass("Please enter your MySQL root password: "))
+ else:
+ mysql_cfg = rpki.config.parser(set_filename = self.mysql_defaults, section = "client")
+ self.db = MySQLdb.connect(db = "mysql",
+ user = mysql_cfg.get("user"),
+ passwd = mysql_cfg.get("password"))
+ self.cur = self.db.cursor()
+ self.cur.execute("SHOW DATABASES")
+ self.databases = set(d[0] for d in self.cur.fetchall())
+ self.initialized = True
+ return getattr(self, name)
+
+ def close(self):
+ if self.initialized:
+ self.db.close() # pylint: disable=E1101
class UserDB(object):
- """
- Class to wrap MySQL access parameters for a particular database.
-
- NB: The SQL definitions for the upgrade_version table is embedded in
- this class rather than being declared in any of the .sql files.
- This is deliberate: nothing but the upgrade system should ever touch
- this table, and it's simpler to keep everything in one place.
-
- We have to be careful about SQL commits here, because CREATE TABLE
- implies an automatic commit. So presence of the magic table per se
- isn't significant, only its content (or lack thereof).
- """
-
- upgrade_version_table_schema = """
- CREATE TABLE upgrade_version (
- version TEXT NOT NULL,
- updated DATETIME NOT NULL
- ) ENGINE=InnoDB
"""
+ Class to wrap MySQL access parameters for a particular database.
+
+ NB: The SQL definitions for the upgrade_version table is embedded in
+ this class rather than being declared in any of the .sql files.
+ This is deliberate: nothing but the upgrade system should ever touch
+ this table, and it's simpler to keep everything in one place.
+
+ We have to be careful about SQL commits here, because CREATE TABLE
+ implies an automatic commit. So presence of the magic table per se
+ isn't significant, only its content (or lack thereof).
+ """
+
+ upgrade_version_table_schema = """
+ CREATE TABLE upgrade_version (
+ version TEXT NOT NULL,
+ updated DATETIME NOT NULL
+ ) ENGINE=InnoDB
+ """
+
+ def __init__(self, name):
+ self.name = name
+ self.database = cfg.get("sql-database", section = name)
+ self.username = cfg.get("sql-username", section = name)
+ self.password = cfg.get("sql-password", section = name)
+ self.db = None
+ self.cur = None
+
+ def open(self):
+ self.db = MySQLdb.connect(db = self.database, user = self.username, passwd = self.password)
+ self.db.autocommit(False)
+ self.cur = self.db.cursor()
+
+ def close(self):
+ if self.cur is not None:
+ self.cur.close()
+ self.cur = None
+ if self.db is not None:
+ # pylint: disable=E1101
+ self.db.commit()
+ self.db.close()
+ self.db = None
+
+ @property
+ def exists_and_accessible(self):
+ # pylint: disable=E1101
+ try:
+ MySQLdb.connect(db = self.database, user = self.username, passwd = self.password).close()
+ except:
+ return False
+ else:
+ return True
+
+ @property
+ def version(self):
+ try:
+ self.cur.execute("SELECT version FROM upgrade_version")
+ v = self.cur.fetchone()
+ return Version(None if v is None else v[0])
+ except _mysql_exceptions.ProgrammingError, e:
+ if e.args[0] != ER_NO_SUCH_TABLE:
+ raise
+ log("Creating upgrade_version table in %s" % self.name)
+ self.cur.execute(self.upgrade_version_table_schema)
+ return Version(None)
+
+ @version.setter
+ def version(self, v):
+ if v > self.version:
+ self.cur.execute("DELETE FROM upgrade_version")
+ self.cur.execute("INSERT upgrade_version (version, updated) VALUES (%s, %s)", (v, datetime.datetime.now()))
+ self.db.commit() # pylint: disable=E1101
+ log("Updated %s to %s" % (self.name, v))
- def __init__(self, name):
- self.name = name
- self.database = cfg.get("sql-database", section = name)
- self.username = cfg.get("sql-username", section = name)
- self.password = cfg.get("sql-password", section = name)
- self.db = None
- self.cur = None
-
- def open(self):
- self.db = MySQLdb.connect(db = self.database, user = self.username, passwd = self.password)
- self.db.autocommit(False)
- self.cur = self.db.cursor()
-
- def close(self):
- if self.cur is not None:
- self.cur.close()
- self.cur = None
- if self.db is not None:
- self.db.commit()
- self.db.close()
- self.db = None
-
- @property
- def exists_and_accessible(self):
- try:
- MySQLdb.connect(db = self.database, user = self.username, passwd = self.password).close()
- except: # pylint: disable=W0702
- return False
- else:
- return True
-
- @property
- def version(self):
- try:
- self.cur.execute("SELECT version FROM upgrade_version")
- v = self.cur.fetchone()
- return Version(None if v is None else v[0])
- except _mysql_exceptions.ProgrammingError, e:
- if e.args[0] != ER_NO_SUCH_TABLE:
- raise
- log("Creating upgrade_version table in %s" % self.name)
- self.cur.execute(self.upgrade_version_table_schema)
- return Version(None)
-
- @version.setter
- def version(self, v):
- if v > self.version:
- self.cur.execute("DELETE FROM upgrade_version")
- self.cur.execute("INSERT upgrade_version (version, updated) VALUES (%s, %s)", (v, datetime.datetime.now()))
- self.db.commit()
- log("Updated %s to %s" % (self.name, v))
-
- @property
- def schema(self):
- lines = []
- for line in getattr(rpki.sql_schemas, self.name, "").splitlines():
- line = " ".join(line.split())
- if line and not line.startswith("--"):
- lines.append(line)
- return [statement.strip() for statement in " ".join(lines).rstrip(";").split(";") if statement.strip()]
class Version(object):
- """
- A version number. This is a class in its own right to force the
- comparision and string I/O behavior we want.
- """
+ """
+ A version number. This is a class in its own right to force the
+ comparision and string I/O behavior we want.
+ """
- def __init__(self, v):
- if v is None:
- v = "0.0"
- self.v = tuple(v.lower().split("."))
+ def __init__(self, v):
+ if v is None:
+ v = "0.0"
+ self.v = tuple(v.lower().split("."))
- def __str__(self):
- return ".".join(self.v)
+ def __str__(self):
+ return ".".join(self.v)
- def __cmp__(self, other):
- return cmp(self.v, other.v)
+ def __cmp__(self, other):
+ return cmp(self.v, other.v)
class Upgrade(object):
- """
- One upgrade script. Really, just its filename and the Version
- object we parse from its filename, we don't need to read the script
- itself except when applying it, but we do need to sort all the
- available upgrade scripts into version order.
- """
-
- @classmethod
- def load_all(cls, name, dn):
- g = os.path.join(dn, "upgrade-%s-to-*.py" % name)
- for fn in glob.iglob(g):
- yield cls(g, fn)
-
- def __init__(self, g, fn):
- head, sep, tail = g.partition("*") # pylint: disable=W0612
- self.fn = fn
- self.version = Version(fn[len(head):-len(tail)])
-
- def __cmp__(self, other):
- return cmp(self.version, other.version)
-
- def apply(self, db):
- # db is an argument here primarily so the script we exec can get at it
- log("Applying %s to %s" % (self.fn, db.name))
- with open(self.fn, "r") as f:
- exec f # pylint: disable=W0122
+ """
+ One upgrade script. Really, just its filename and the Version
+ object we parse from its filename, we don't need to read the script
+ itself except when applying it, but we do need to sort all the
+ available upgrade scripts into version order.
+ """
+
+ @classmethod
+ def load_all(cls, name, dn):
+ g = os.path.join(dn, "upgrade-%s-to-*.py" % name)
+ for fn in glob.iglob(g):
+ yield cls(g, fn)
+
+ def __init__(self, g, fn):
+ head, sep, tail = g.partition("*") # pylint: disable=W0612
+ self.fn = fn
+ self.version = Version(fn[len(head):-len(tail)])
+
+ def __cmp__(self, other):
+ return cmp(self.version, other.version)
+
+ def apply(self, db):
+ # db is an argument here primarily so the script we exec can get at it
+ log("Applying %s to %s" % (self.fn, db.name))
+ with open(self.fn, "r") as f:
+ exec f # pylint: disable=W0122
def do_drop(name):
- db = UserDB(name)
- if db.database in root.databases:
- log("DROP DATABASE %s" % db.database)
- root.cur.execute("DROP DATABASE %s" % db.database)
- root.db.commit()
+ db = UserDB(name)
+ if db.database in root.databases:
+ log("DROP DATABASE %s" % db.database)
+ root.cur.execute("DROP DATABASE %s" % db.database)
+ root.db.commit() # pylint: disable=E1101
def do_create(name):
- db = UserDB(name)
- log("CREATE DATABASE %s" % db.database)
- root.cur.execute("CREATE DATABASE %s" % db.database)
- log("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY ###" % (db.database, db.username))
- root.cur.execute("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY %%s" % (db.database, db.username),
- (db.password,))
- root.db.commit()
- db.open()
- for statement in db.schema:
- if not statement.upper().startswith("DROP TABLE"):
- log(statement)
- db.cur.execute(statement)
- db.version = current_version
- db.close()
+ db = UserDB(name)
+ log("CREATE DATABASE %s" % db.database)
+ root.cur.execute("CREATE DATABASE %s" % db.database)
+ log("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY ###" % (db.database, db.username))
+ root.cur.execute("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY %%s" % (db.database, db.username),
+ (db.password,))
+ root.db.commit() # pylint: disable=E1101
+ db.open()
+ db.version = current_version
+ db.close()
def do_script_drop(name):
- db = UserDB(name)
- print "DROP DATABASE IF EXISTS %s;" % db.database
+ db = UserDB(name)
+ print "DROP DATABASE IF EXISTS %s;" % db.database
def do_drop_and_create(name):
- do_drop(name)
- do_create(name)
+ do_drop(name)
+ do_create(name)
def do_fix_grants(name):
- db = UserDB(name)
- if not db.exists_and_accessible:
- log("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY ###" % (db.database, db.username))
- root.cur.execute("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY %%s" % (db.database, db.username),
- (db.password,))
- root.db.commit()
+ db = UserDB(name)
+ if not db.exists_and_accessible:
+ log("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY ###" % (db.database, db.username))
+ root.cur.execute("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY %%s" % (db.database, db.username),
+ (db.password,))
+ root.db.commit() # pylint: disable=E1101
def do_create_if_missing(name):
- db = UserDB(name)
- if not db.exists_and_accessible:
- do_create(name)
+ db = UserDB(name)
+ if not db.exists_and_accessible:
+ do_create(name)
def do_apply_upgrades(name):
- upgrades = sorted(Upgrade.load_all(name, args.upgrade_scripts))
- if upgrades:
- db = UserDB(name)
- db.open()
- log("Current version of %s is %s" % (db.name, db.version))
- for upgrade in upgrades:
- if upgrade.version > db.version:
- upgrade.apply(db)
- db.version = upgrade.version
- db.version = current_version
- db.close()
+ upgrades = sorted(Upgrade.load_all(name, args.upgrade_scripts))
+ if upgrades:
+ db = UserDB(name)
+ db.open()
+ log("Current version of %s is %s" % (db.name, db.version))
+ for upgrade in upgrades:
+ if upgrade.version > db.version:
+ upgrade.apply(db)
+ db.version = upgrade.version
+ db.version = current_version
+ db.close()
def log(text):
- if args.verbose:
- print "#", text
+ if args.verbose:
+ print "#", text
parser = argparse.ArgumentParser(description = """\
Automated setup of all SQL stuff used by the RPKI CA tools. Pulls
@@ -299,13 +294,13 @@ parser.set_defaults(dispatch = do_create_if_missing)
args = parser.parse_args()
try:
- cfg = rpki.config.parser(args.config, "myrpki")
- root = RootDB(args.mysql_defaults)
- current_version = Version(rpki.version.VERSION)
- for program_name in ("irdbd", "rpkid", "pubd"):
- if cfg.getboolean("start_" + program_name, False):
- args.dispatch(program_name)
- root.close()
+ cfg = rpki.config.parser(set_filename = args.config, section = "myrpki")
+ root = RootDB(args.mysql_defaults)
+ current_version = Version(rpki.version.VERSION)
+ for program_name in ("irdbd", "rpkid", "pubd"):
+ if cfg.getboolean("start_" + program_name, False):
+ args.dispatch(program_name)
+ root.close()
except Exception, e:
- #raise
- sys.exit(str(e))
+ #raise
+ sys.exit(str(e))
diff --git a/ca/rpki-start-servers b/ca/rpki-start-servers
index 8a745896..9bf47d0c 100755
--- a/ca/rpki-start-servers
+++ b/ca/rpki-start-servers
@@ -38,9 +38,9 @@ os.environ["TZ"] = "UTC"
time.tzset()
def non_negative_integer(s):
- if int(s) < 0:
- raise ValueError
- return s
+ if int(s) < 0:
+ raise ValueError
+ return s
parser = argparse.ArgumentParser(description = __doc__)
parser.add_argument("-c", "--config",
@@ -64,24 +64,23 @@ group.add_argument("--log-syslog", default = "daemon", nargs = "?",
help = "log syslog")
args = parser.parse_args()
-cfg = rpki.config.parser(args.config, "myrpki")
+cfg = rpki.config.parser(set_filename = args.config, section = "myrpki")
def run(name, old_flag = None):
- if cfg.getboolean("start_" + name, cfg.getboolean("run_" + name if old_flag is None else old_flag, False)):
- # pylint: disable=E1103
- log_file = os.path.join(args.log_directory, name + ".log")
- cmd = (os.path.join(rpki.autoconf.libexecdir, name), "--config", cfg.filename, "--log-level", args.log_level)
- if args.log_file:
- cmd += ("--log-file", log_file)
- elif args.log_rotating_file_kbytes:
- cmd += ("--log-rotating-file", log_file, args.log_rotating_file_kbytes, args.log_backup_count)
- elif args.log_rotating_file_hours:
- cmd += ("--log-timed-rotating-file", log_file, args.log_rotating_file_hours, args.log_backup_count)
- else:
- cmd += ("--log-syslog", args.log_syslog)
- proc = subprocess.Popen(cmd)
- if proc.wait() != 0:
- sys.exit("Problem starting %s, pid %s" % (name, proc.pid))
+ if cfg.getboolean("start_" + name, cfg.getboolean("run_" + name if old_flag is None else old_flag, False)):
+ log_file = os.path.join(args.log_directory, name + ".log")
+ cmd = (os.path.join(rpki.autoconf.libexecdir, name), "--log-level", args.log_level)
+ if args.log_file:
+ cmd += ("--log-file", log_file)
+ elif args.log_rotating_file_kbytes:
+ cmd += ("--log-rotating-file", log_file, args.log_rotating_file_kbytes, args.log_backup_count)
+ elif args.log_rotating_file_hours:
+ cmd += ("--log-timed-rotating-file", log_file, args.log_rotating_file_hours, args.log_backup_count)
+ else:
+ cmd += ("--log-syslog", args.log_syslog)
+ proc = subprocess.Popen(cmd)
+ if proc.wait() != 0:
+ sys.exit("Problem starting %s, pid %s" % (name, proc.pid))
run("irdbd", "run_rpkid")
run("rpkid")
diff --git a/ca/rpki.wsgi b/ca/rpki.wsgi
index 72ba75ac..487650f7 100644
--- a/ca/rpki.wsgi
+++ b/ca/rpki.wsgi
@@ -21,7 +21,7 @@ import sys
import os
import rpki.autoconf
-os.environ['DJANGO_SETTINGS_MODULE'] = 'rpki.gui.default_settings'
+os.environ.update(DJANGO_SETTINGS_MODULE = "rpki.django_settings.gui")
# Needed for local_settings.py
sys.path.insert(1, rpki.autoconf.sysconfdir + '/rpki')
@@ -39,7 +39,7 @@ os.environ['DISABLE_SETPROCTITLE'] = 'yes'
if not os.environ.get('PYTHON_EGG_CACHE') and rpki.autoconf.WSGI_PYTHON_EGG_CACHE_DIR:
os.environ['PYTHON_EGG_CACHE'] = rpki.autoconf.WSGI_PYTHON_EGG_CACHE_DIR
-import django.core.handlers.wsgi
-application = django.core.handlers.wsgi.WSGIHandler()
+from django.core.wsgi import get_wsgi_application
+application = get_wsgi_application()
# vim:ft=python
diff --git a/ca/rpkic b/ca/rpkic
index 333a5eb7..3c3c7d99 100755
--- a/ca/rpkic
+++ b/ca/rpkic
@@ -17,5 +17,5 @@
# PERFORMANCE OF THIS SOFTWARE.
if __name__ == "__main__":
- import rpki.rpkic
- rpki.rpkic.main()
+ import rpki.rpkic
+ rpki.rpkic.main()
diff --git a/ca/rpkid b/ca/rpkid
index a4cc6cd3..f63e441e 100755
--- a/ca/rpkid
+++ b/ca/rpkid
@@ -17,5 +17,5 @@
# PERFORMANCE OF THIS SOFTWARE.
if __name__ == "__main__":
- import rpki.rpkid
- rpki.rpkid.main()
+ import rpki.rpkid
+ rpki.rpkid.main()
diff --git a/ca/rpkigui-apache-conf-gen b/ca/rpkigui-apache-conf-gen
index 6f71c7b1..b617b4ac 100755
--- a/ca/rpkigui-apache-conf-gen
+++ b/ca/rpkigui-apache-conf-gen
@@ -29,6 +29,50 @@ import rpki.autoconf
fqdn = socket.getfqdn()
vhost_template = """\
+
+#
+# Stuff that should be visible with both HTTP and HTTPS is (now)
+# outside the vhost block (see if this works properly...).
+#
+
+#
+# Allow access to the directory where rcynic-html writes
+# its output files.
+#
+<Directory %(RCYNIC_HTML_DIR)s>
+%(allow)s
+</Directory>
+
+#
+# Add alias pointing to rcynic-html's output files.
+#
+# If for some reason you need to change this, be careful to leave
+# the trailing slash off the URL, otherwise /rcynic will be
+# swallowed by the WSGIScriptAlias
+#
+Alias /rcynic %(RCYNIC_HTML_DIR)s/
+
+#
+# Allow access to the directory where pubd writes RRDP files.
+#
+<Directory %(datarootdir)s/rpki/rrdp-publication/>
+%(allow)s
+</Directory>
+
+#
+# Add alias pointing to pubd's RRD output files.
+#
+Alias /rrdp %(datarootdir)s/rpki/rrdp-publication/
+
+#
+# RRDP "notification" file needs a short expiration: this is
+# a critical part of how RRDP interacts with HTTP caching.
+#
+<LocationMatch ^/rrdp/updates[.]xml$>
+ ExpiresActive on
+ ExpiresDefault "access plus 5 minutes"
+</LocationMatch>
+
#
# By default, this configuration assumes that you use name-based
# virtual hosting. If that's not what you want, you may need
@@ -78,23 +122,6 @@ vhost_template = """\
Alias /site_media/ %(datarootdir)s/rpki/media/
#
- # Allow access to the directory where rcynic-html writes
- # its output files.
- #
- <Directory %(RCYNIC_HTML_DIR)s>
-%(allow)s
- </Directory>
-
- #
- # Add alias pointing to rcynic-html's output files.
- #
- # If for some reason you need to change this, be careful to leave
- # the trailing slash off the URL, otherwise /rcynic will be
- # swallowed by the WSGIScriptAlias
- #
- Alias /rcynic %(RCYNIC_HTML_DIR)s/
-
- #
# Redirect to the GUI dashboard when someone hits the bare vhost.
#
RedirectMatch ^/$ /rpki/
@@ -102,7 +129,7 @@ vhost_template = """\
#
# Enable HTTPS
#
- SSLEngine on
+ SSLEngine on
#
# Specify HTTPS server certificate and key files for this virtual host.
@@ -161,369 +188,379 @@ NameVirtualHost *:443
'''
def Guess(args):
- """
- Guess what platform this is and dispatch to platform constructor.
- """
-
- system = platform.system()
- if system == "FreeBSD":
- return FreeBSD(args)
- if system == "Darwin":
- return Darwin(args)
- if system == "Linux":
- distro = platform.linux_distribution()[0].lower()
- if distro == "debian":
- return Debian(args)
- if distro == "ubuntu":
- return Ubuntu(args)
- if distro in ("fedora", "centos"):
- return Redhat(args)
- raise NotImplementedError("Can't guess what platform this is, sorry")
+ """
+ Guess what platform this is and dispatch to platform constructor.
+ """
+
+ system = platform.system()
+ if system == "FreeBSD":
+ return FreeBSD(args)
+ if system == "Darwin":
+ return Darwin(args)
+ if system == "Linux":
+ distro = platform.linux_distribution()[0].lower()
+ if distro == "debian":
+ return Debian(args)
+ if distro == "ubuntu":
+ return Ubuntu(args)
+ if distro in ("fedora", "centos"):
+ return Redhat(args)
+ raise NotImplementedError("Can't guess what platform this is, sorry")
+
class Platform(object):
- """
- Abstract base class representing an operating system platform.
- """
-
- apache_cer = os.path.join(rpki.autoconf.sysconfdir, "rpki", "apache.cer")
- apache_key = os.path.join(rpki.autoconf.sysconfdir, "rpki", "apache.key")
-
- apache_conf = os.path.join(rpki.autoconf.sysconfdir, "rpki", "apache.conf")
- apache_conf_sample = apache_conf + ".sample"
-
- apache_conf_preface = ""
-
- def __init__(self, args):
- self.args = args
- self.log("RPKI Apache configuration: platform \"%s\", action \"%s\"" % (
- self.__class__.__name__, args.action))
- getattr(self, args.action)()
-
- def log(self, msg):
- if self.args.verbose:
- print msg
-
- def run(self, *cmd, **kwargs):
- self.log("Running %s" % " ".join(cmd))
- subprocess.check_call(cmd, **kwargs)
-
- req_cmd = ("openssl", "req", "-new",
- "-config", "/dev/stdin",
- "-out", "/dev/stdout",
- "-keyout", apache_key,
- "-newkey", "rsa:2048")
-
- x509_cmd = ("openssl", "x509", "-req", "-sha256",
- "-signkey", apache_key,
- "-in", "/dev/stdin",
- "-out", apache_cer,
- "-days", "3650")
-
- req_conf = '''\
- [req]
- default_bits = 2048
- default_md = sha256
- distinguished_name = req_dn
- prompt = no
- encrypt_key = no
- [req_dn]
- CN = %s
- ''' % fqdn
-
- def unlink(self, fn, silent = False):
- if os.path.lexists(fn):
- if not silent:
- self.log("Removing %s" % fn)
- os.unlink(fn)
- elif not silent:
- self.log("Would have removed %s if it existed" % fn)
-
- def del_certs(self, silent = False):
- self.unlink(self.apache_cer, silent)
- self.unlink(self.apache_key, silent)
-
- def add_certs(self):
- if os.path.exists(self.apache_cer) and os.path.exists(self.apache_key):
- return
- self.del_certs()
- req = subprocess.Popen(self.req_cmd,
- stdin = subprocess.PIPE,
- stdout = subprocess.PIPE,
- stderr = open("/dev/null", "w"))
- x509 = subprocess.Popen(self.x509_cmd,
- stdin = req.stdout,
- stderr = open("/dev/null", "w"))
- req.stdin.write(self.req_conf)
- req.stdin.close()
- if req.wait():
- raise subprocess.CalledProcessError(req.returncode, self.req_cmd)
- if x509.wait():
- raise subprocess.CalledProcessError(x509.returncode, self.x509_cmd)
- self.log("Created %s and %s, chmoding %s" % (
- self.apache_cer, self.apache_key, self.apache_key))
- os.chmod(self.apache_key, 0600)
-
- _vhost = None
-
- @property
- def vhost(self):
- if self._vhost is None:
- allow = allow_22_template if self.args.apache_version <= 22 else allow_24_template
- self._vhost = vhost_template % dict(rpki.autoconf.__dict__, fqdn = fqdn, allow = allow)
- return self._vhost
-
- @property
- def name_virtual_host(self):
- return name_virtual_host_template if self.args.apache_version <= 22 else ""
-
- @property
- def too_complex(self):
- return textwrap.dedent('''\
- # It looks like you already have HTTPS enabled in your
- # Apache configuration, which makes your configuration too
- # complex for us to enable support for the RPKI GUI automatically.
- #
- # To enable support, take a look at %s
- # and copy what you need from that file into %s,
- # paying attention to the comments which mark the bits that
- # you might (or might not) need to change or omit, depending
- # on the details of your particular Apache configuration.
- ''' % (self.apache_conf_sample, self.apache_conf))
-
- def install(self):
- with open(self.apache_conf_sample, "w") as f:
- self.log("Writing %s" % f.name)
- f.write(self.apache_conf_preface)
- f.write(self.name_virtual_host)
- f.write(self.vhost)
- if not os.path.exists(self.apache_conf):
- self.unlink(self.apache_conf)
- with open(self.apache_conf, "w") as f:
- self.log("Writing %s" % f.name)
- if self.test_url("https://%s/" % fqdn):
- f.write(self.too_complex)
- sys.stdout.write(self.too_complex)
- else:
- if not self.test_tcp("localhost", 443):
+ """
+ Abstract base class representing an operating system platform.
+ """
+
+ apache_cer = os.path.join(rpki.autoconf.sysconfdir, "rpki", "apache.cer")
+ apache_key = os.path.join(rpki.autoconf.sysconfdir, "rpki", "apache.key")
+
+ apache_conf = os.path.join(rpki.autoconf.sysconfdir, "rpki", "apache.conf")
+ apache_conf_sample = apache_conf + ".sample"
+
+ apache_conf_preface = ""
+
+ def __init__(self, args):
+ self.args = args
+ self.log("RPKI Apache configuration: platform \"%s\", action \"%s\"" % (
+ self.__class__.__name__, args.action))
+ getattr(self, args.action)()
+
+ def log(self, msg):
+ if self.args.verbose:
+ print msg
+
+ def run(self, *cmd, **kwargs):
+ self.log("Running %s" % " ".join(cmd))
+ subprocess.check_call(cmd, **kwargs)
+
+ req_cmd = ("openssl", "req", "-new",
+ "-config", "/dev/stdin",
+ "-out", "/dev/stdout",
+ "-keyout", apache_key,
+ "-newkey", "rsa:2048")
+
+ x509_cmd = ("openssl", "x509", "-req", "-sha256",
+ "-signkey", apache_key,
+ "-in", "/dev/stdin",
+ "-out", apache_cer,
+ "-days", "3650")
+
+ req_conf = '''\
+ [req]
+ default_bits = 2048
+ default_md = sha256
+ distinguished_name = req_dn
+ prompt = no
+ encrypt_key = no
+ [req_dn]
+ CN = %s
+ ''' % fqdn
+
+ def unlink(self, fn, silent = False):
+ if os.path.lexists(fn):
+ if not silent:
+ self.log("Removing %s" % fn)
+ os.unlink(fn)
+ elif not silent:
+ self.log("Would have removed %s if it existed" % fn)
+
+ def del_certs(self, silent = False):
+ self.unlink(self.apache_cer, silent)
+ self.unlink(self.apache_key, silent)
+
+ def add_certs(self):
+ if os.path.exists(self.apache_cer) and os.path.exists(self.apache_key):
+ return
+ self.del_certs()
+ req = subprocess.Popen(self.req_cmd,
+ stdin = subprocess.PIPE,
+ stdout = subprocess.PIPE,
+ stderr = open("/dev/null", "w"))
+ x509 = subprocess.Popen(self.x509_cmd,
+ stdin = req.stdout,
+ stderr = open("/dev/null", "w"))
+ req.stdin.write(self.req_conf)
+ req.stdin.close()
+ if req.wait():
+ raise subprocess.CalledProcessError(req.returncode, self.req_cmd)
+ if x509.wait():
+ raise subprocess.CalledProcessError(x509.returncode, self.x509_cmd)
+ self.log("Created %s and %s, chmoding %s" % (
+ self.apache_cer, self.apache_key, self.apache_key))
+ os.chmod(self.apache_key, 0600)
+
+ _vhost = None
+
+ @property
+ def vhost(self):
+ if self._vhost is None:
+ allow = allow_22_template if self.args.apache_version <= 22 else allow_24_template
+ self._vhost = vhost_template % dict(rpki.autoconf.__dict__, fqdn = fqdn, allow = allow)
+ return self._vhost
+
+ @property
+ def name_virtual_host(self):
+ return name_virtual_host_template if self.args.apache_version <= 22 else ""
+
+ @property
+ def too_complex(self):
+ return textwrap.dedent('''\
+ # It looks like you already have HTTPS enabled in your
+ # Apache configuration, which makes your configuration too
+ # complex for us to enable support for the RPKI GUI automatically.
+ #
+ # To enable support, take a look at %s
+ # and copy what you need from that file into %s,
+ # paying attention to the comments which mark the bits that
+ # you might (or might not) need to change or omit, depending
+ # on the details of your particular Apache configuration.
+ ''' % (self.apache_conf_sample, self.apache_conf))
+
+ @property
+ def apache_conf_target(self):
+ raise NotImplementedError
+
+ def restart(self):
+ raise NotImplementedError
+
+ def install(self):
+ with open(self.apache_conf_sample, "w") as f:
+ self.log("Writing %s" % f.name)
f.write(self.apache_conf_preface)
f.write(self.name_virtual_host)
- f.write(self.vhost)
- if not os.path.exists(self.apache_conf_target):
- self.unlink(self.apache_conf_target)
- self.log("Symlinking %s to %s" % (
- self.apache_conf_target, self.apache_conf))
- os.symlink(self.apache_conf, self.apache_conf_target)
- self.add_certs()
- self.enable()
- self.restart()
-
- def enable(self):
- pass
-
- def disable(self):
- pass
-
- def remove(self):
- try:
- same = open(self.apache_conf, "r").read() == open(self.apache_conf_sample, "r").read()
- except: # pylint: disable=W0702
- same = False
- self.unlink(self.apache_conf_sample)
- if same:
- self.unlink(self.apache_conf)
- self.unlink(self.apache_conf_target)
- self.disable()
- self.restart()
-
- def purge(self):
- self.remove()
- self.unlink(self.apache_conf)
- self.del_certs()
-
- @staticmethod
- def test_url(url = "https://localhost/"):
- try:
- urllib2.urlopen(url).close()
- except IOError:
- return False
- else:
- return True
-
- @staticmethod
- def test_tcp(host = "localhost", port = 443, family = socket.AF_UNSPEC, proto = socket.SOCK_STREAM):
- try:
- addrinfo = socket.getaddrinfo(host, port, family, proto)
- except socket.error:
- return False
- for af, socktype, proto, canon, sa in addrinfo: # pylint: disable=W0612
- try:
- s = socket.socket(af, socktype, proto)
- s.connect(sa)
- s.close()
- except socket.error:
- continue
- else:
- return True
- return False
+ f.write(self.vhost)
+ if not os.path.exists(self.apache_conf):
+ self.unlink(self.apache_conf)
+ with open(self.apache_conf, "w") as f:
+ self.log("Writing %s" % f.name)
+ if self.test_url("https://%s/" % fqdn):
+ f.write(self.too_complex)
+ sys.stdout.write(self.too_complex)
+ else:
+ if not self.test_tcp("localhost", 443):
+ f.write(self.apache_conf_preface)
+ f.write(self.name_virtual_host)
+ f.write(self.vhost)
+ if not os.path.exists(self.apache_conf_target):
+ self.unlink(self.apache_conf_target)
+ self.log("Symlinking %s to %s" % (
+ self.apache_conf_target, self.apache_conf))
+ os.symlink(self.apache_conf, self.apache_conf_target)
+ self.add_certs()
+ self.enable()
+ self.restart()
+
+ def enable(self):
+ pass
+
+ def disable(self):
+ pass
+
+ def remove(self):
+ try:
+ same = open(self.apache_conf, "r").read() == open(self.apache_conf_sample, "r").read()
+ except:
+ same = False
+ self.unlink(self.apache_conf_sample)
+ if same:
+ self.unlink(self.apache_conf)
+ self.unlink(self.apache_conf_target)
+ self.disable()
+ self.restart()
+
+ def purge(self):
+ self.remove()
+ self.unlink(self.apache_conf)
+ self.del_certs()
+
+ @staticmethod
+ def test_url(url = "https://localhost/"):
+ try:
+ urllib2.urlopen(url).close()
+ except IOError:
+ return False
+ else:
+ return True
+
+ @staticmethod
+ def test_tcp(host = "localhost", port = 443, family = socket.AF_UNSPEC, proto = socket.SOCK_STREAM):
+ try:
+ addrinfo = socket.getaddrinfo(host, port, family, proto)
+ except socket.error:
+ return False
+ for af, socktype, proto, canon, sa in addrinfo: # pylint: disable=W0612
+ try:
+ s = socket.socket(af, socktype, proto)
+ s.connect(sa)
+ s.close()
+ except socket.error:
+ continue
+ else:
+ return True
+ return False
+
class FreeBSD(Platform):
- """
- FreeBSD.
- """
+ """
+ FreeBSD.
+ """
- # On FreeBSD we have to ask httpd what version it is before we know
- # where to put files or what to call the service. In FreeBSD's makefiles,
- # this value is called APACHE_VERSION, and is calculated thusly:
- #
- # httpd -V | sed -ne 's/^Server version: Apache\/\([0-9]\)\.\([0-9]*\).*/\1\2/p'
+ # On FreeBSD we have to ask httpd what version it is before we know
+ # where to put files or what to call the service. In FreeBSD's makefiles,
+ # this value is called APACHE_VERSION, and is calculated thusly:
+ #
+ # httpd -V | sed -ne 's/^Server version: Apache\/\([0-9]\)\.\([0-9]*\).*/\1\2/p'
+
+ _apache_name = None
- _apache_name = None
+ @property
+ def apache_name(self):
+ if self._apache_name is None:
+ self._apache_name = "apache%s" % self.args.apache_version
+ return self._apache_name
- @property
- def apache_name(self):
- if self._apache_name is None:
- self._apache_name = "apache%s" % self.args.apache_version
- return self._apache_name
+ @property
+ def apache_conf_target(self):
+ return "/usr/local/etc/%s/Includes/rpki.conf" % self.apache_name
- @property
- def apache_conf_target(self):
- return "/usr/local/etc/%s/Includes/rpki.conf" % self.apache_name
+ apache_conf_preface = textwrap.dedent('''\
+ # These directives tell Apache to listen on the HTTPS port
+ # and to enable name-based virtual hosting. If you already
+ # have HTTPS enabled elsewhere in your configuration, you may
+ # need to remove these.
- apache_conf_preface = textwrap.dedent('''\
- # These directives tell Apache to listen on the HTTPS port
- # and to enable name-based virtual hosting. If you already
- # have HTTPS enabled elsewhere in your configuration, you may
- # need to remove these.
+ Listen [::]:443
+ Listen 0.0.0.0:443
+ ''')
- Listen [::]:443
- Listen 0.0.0.0:443
- ''')
+ def restart(self):
+ self.run("service", self.apache_name, "restart")
- def restart(self):
- self.run("service", self.apache_name, "restart")
class Debian(Platform):
- """
- Debian and related platforms like Ubuntu.
- """
+ """
+ Debian and related platforms like Ubuntu.
+ """
- @property
- def distribution_version(self):
- return tuple(int(v) for v in platform.linux_distribution()[1].split("."))
+ @property
+ def distribution_version(self):
+ return tuple(int(v) for v in platform.linux_distribution()[1].split("."))
- # As of Wheezy, Debian still wants the configuration filename
- # without the .conf suffix. Keep an eye on this, as it may change
- # in future releases.
- #
- @property
- def apache_conf_target(self):
- return "/etc/apache2/sites-available/rpki"
-
- snake_oil_cer = "/etc/ssl/certs/ssl-cert-snakeoil.pem"
- snake_oil_key = "/etc/ssl/private/ssl-cert-snakeoil.key"
-
- def add_certs(self):
- if not os.path.exists(self.snake_oil_cer) or not os.path.exists(self.snake_oil_key):
- return Platform.add_certs(self)
- if not os.path.exists(self.apache_cer):
- self.unlink(self.apache_cer)
- os.symlink(self.snake_oil_cer, self.apache_cer)
- if not os.path.exists(self.apache_key):
- self.unlink(self.apache_key)
- os.symlink(self.snake_oil_key, self.apache_key)
-
- def enable(self):
- self.run("a2enmod", "ssl")
- self.run("a2ensite", "rpki")
+ # As of Wheezy, Debian still wants the configuration filename
+ # without the .conf suffix. Keep an eye on this, as it may change
+ # in future releases.
#
- # In light of BREACH and CRIME attacks, mod_deflate is looking
- # like a bad idea, so make sure it's off.
- self.run("a2dismod", "deflate")
-
- def disable(self):
- self.run("a2dissite", "rpki")
+ @property
+ def apache_conf_target(self):
+ return "/etc/apache2/sites-available/rpki"
+
+ snake_oil_cer = "/etc/ssl/certs/ssl-cert-snakeoil.pem"
+ snake_oil_key = "/etc/ssl/private/ssl-cert-snakeoil.key"
+
+ def add_certs(self):
+ if not os.path.exists(self.snake_oil_cer) or not os.path.exists(self.snake_oil_key):
+ return Platform.add_certs(self)
+ if not os.path.exists(self.apache_cer):
+ self.unlink(self.apache_cer)
+ os.symlink(self.snake_oil_cer, self.apache_cer)
+ if not os.path.exists(self.apache_key):
+ self.unlink(self.apache_key)
+ os.symlink(self.snake_oil_key, self.apache_key)
+
+ def enable(self):
+ self.run("a2enmod", "ssl")
+ self.run("a2enmod", "expires")
+ self.run("a2ensite", "rpki")
+ #
+ # In light of BREACH and CRIME attacks, mod_deflate is looking
+ # like a bad idea, so make sure it's off.
+ self.run("a2dismod", "deflate")
+
+ def disable(self):
+ self.run("a2dissite", "rpki")
+
+ def restart(self):
+ self.run("service", "apache2", "restart")
- def restart(self):
- self.run("service", "apache2", "restart")
class Ubuntu(Debian):
-
- # On Ubuntu, the filename must end in .conf on Trusty and must not
- # end in .conf on Precise.
- @property
- def apache_conf_target(self):
- if self.distribution_version >= (14, 0):
- return "/etc/apache2/sites-available/rpki.conf"
- else:
- return "/etc/apache2/sites-available/rpki"
-
-class NIY(Platform):
- def __init__(self, args):
- super(NIY, self).__init__(args)
- raise NotImplementedError("Platform %s not implemented yet, sorry" % self.__class__.__name__)
-
-class Redhat(NIY):
- """
- Redhat family of Linux distributions (Fedora, CentOS).
- """
-
-class Darwin(NIY):
- """
- Mac OS X (aka Darwin).
- """
+
+ # On Ubuntu, the filename must end in .conf on Trusty and must not
+ # end in .conf on Precise.
+ @property
+ def apache_conf_target(self):
+ if self.distribution_version >= (14, 0):
+ return "/etc/apache2/sites-available/rpki.conf"
+ else:
+ return "/etc/apache2/sites-available/rpki"
+
+
+class NIY(Platform): # pylint: disable=W0223
+ def __init__(self, args):
+ super(NIY, self).__init__(args)
+ raise NotImplementedError("Platform %s not implemented yet, sorry" % self.__class__.__name__)
+
+class Redhat(NIY): # pylint: disable=W0223
+ "Redhat family of Linux distributions (Fedora, CentOS)."
+
+class Darwin(NIY): # pylint: disable=W0223
+ "Mac OS X (aka Darwin)."
+
def main():
- """
- Generate and (de)install configuration suitable for using Apache httpd
- to drive the RPKI web interface under WSGI.
- """
-
- parser = argparse.ArgumentParser(description = __doc__)
- group1 = parser.add_mutually_exclusive_group()
- group2 = parser.add_mutually_exclusive_group()
-
- parser.add_argument("-v", "--verbose",
- help = "whistle while you work", action = "store_true")
- parser.add_argument("--apache-version",
- help = "Apache version (default " + rpki.autoconf.APACHE_VERSION + ")",
- type = int, default = rpki.autoconf.APACHE_VERSION)
-
- group1.add_argument("--freebsd",
- help = "configure for FreeBSD",
- action = "store_const", dest = "platform", const = FreeBSD)
- group1.add_argument("--debian",
- help = "configure for Debian",
- action = "store_const", dest = "platform", const = Debian)
- group1.add_argument("--ubuntu",
- help = "configure for Ubuntu",
- action = "store_const", dest = "platform", const = Ubuntu)
- group1.add_argument("--redhat", "--fedora", "--centos",
- help = "configure for Redhat/Fedora/CentOS",
- action = "store_const", dest = "platform", const = Redhat)
- group1.add_argument("--macosx", "--darwin",
- help = "configure for Mac OS X (Darwin)",
- action = "store_const", dest = "platform", const = Darwin)
- group1.add_argument("--guess",
- help = "guess which platform configuration to use",
- action = "store_const", dest = "platform", const = Guess)
-
- group2.add_argument("-i", "--install",
- help = "install configuration",
- action = "store_const", dest = "action", const = "install")
- group2.add_argument("-r", "--remove", "--deinstall", "--uninstall",
- help = "remove configuration",
- action = "store_const", dest = "action", const = "remove")
- group2.add_argument("-P", "--purge",
- help = "remove configuration with extreme prejudice",
- action = "store_const", dest = "action", const = "purge")
-
- parser.set_defaults(platform = Guess, action = "install")
- args = parser.parse_args()
-
- try:
- args.platform(args)
- except Exception, e:
- sys.exit(str(e))
+ """
+ Generate and (de)install configuration suitable for using Apache httpd
+ to drive the RPKI web interface under WSGI.
+ """
+
+ parser = argparse.ArgumentParser(description = __doc__)
+ group1 = parser.add_mutually_exclusive_group()
+ group2 = parser.add_mutually_exclusive_group()
+
+ parser.add_argument("-v", "--verbose",
+ help = "whistle while you work", action = "store_true")
+ parser.add_argument("--apache-version",
+ help = "Apache version (default " + rpki.autoconf.APACHE_VERSION + ")",
+ type = int, default = rpki.autoconf.APACHE_VERSION)
+
+ group1.add_argument("--freebsd",
+ help = "configure for FreeBSD",
+ action = "store_const", dest = "platform", const = FreeBSD)
+ group1.add_argument("--debian",
+ help = "configure for Debian",
+ action = "store_const", dest = "platform", const = Debian)
+ group1.add_argument("--ubuntu",
+ help = "configure for Ubuntu",
+ action = "store_const", dest = "platform", const = Ubuntu)
+ group1.add_argument("--redhat", "--fedora", "--centos",
+ help = "configure for Redhat/Fedora/CentOS",
+ action = "store_const", dest = "platform", const = Redhat)
+ group1.add_argument("--macosx", "--darwin",
+ help = "configure for Mac OS X (Darwin)",
+ action = "store_const", dest = "platform", const = Darwin)
+ group1.add_argument("--guess",
+ help = "guess which platform configuration to use",
+ action = "store_const", dest = "platform", const = Guess)
+
+ group2.add_argument("-i", "--install",
+ help = "install configuration",
+ action = "store_const", dest = "action", const = "install")
+ group2.add_argument("-r", "--remove", "--deinstall", "--uninstall",
+ help = "remove configuration",
+ action = "store_const", dest = "action", const = "remove")
+ group2.add_argument("-P", "--purge",
+ help = "remove configuration with extreme prejudice",
+ action = "store_const", dest = "action", const = "purge")
+
+ parser.set_defaults(platform = Guess, action = "install")
+ args = parser.parse_args()
+
+ try:
+ args.platform(args)
+ except Exception, e:
+ sys.exit(str(e))
if __name__ == "__main__":
- main()
+ main()
diff --git a/ca/rpkigui-import-routes b/ca/rpkigui-import-routes
index 0fbe0126..fb8e381e 100755
--- a/ca/rpkigui-import-routes
+++ b/ca/rpkigui-import-routes
@@ -110,4 +110,3 @@ automatically.""")
except Exception as e:
logging.exception(e)
sys.exit(1)
-
diff --git a/ca/rpkigui-query-routes b/ca/rpkigui-query-routes
index 1f698f23..dc2835a0 100755
--- a/ca/rpkigui-query-routes
+++ b/ca/rpkigui-query-routes
@@ -49,18 +49,17 @@ qs = rv.RouteOrigin.objects.filter(
prefix_max__gte=r.max
)
-
-def validity_marker(route, roa, roa_prefix):
- "Return + if the roa would cause the route to be accepted, or - if not"
- # we already know the ROA covers this route because they are returned
- # from RouteOrigin.roas, so just check the ASN and max prefix length
- return '-' if (roa.asid == 0 or route.asn != roa.asid or
- route.prefixlen > roa_prefix.max_length) else '+'
-
# xxx.xxx.xxx.xxx/xx-xx is 22 characters
+# we already know the ROA covers this route because they are returned
+# from RouteOrigin.roas, so just check the ASN and max prefix length
+
for route in qs:
print route.as_resource_range(), route.asn, route.status
for pfx in route.roa_prefixes:
for roa in pfx.roas.all():
- print validity_marker(route, roa, pfx), pfx.as_roa_prefix(), roa.asid, roa.repo.uri
+ if roa.asid == 0 or route.asn != roa.asid or route.prefixlen > pfx.max_length:
+ validity_marker = '-'
+ else:
+ validity_marker = '+'
+ print validity_marker, pfx.as_roa_prefix(), roa.asid, roa.repo.uri
print
diff --git a/ca/tests/Makefile.in b/ca/tests/Makefile.in
index 9796dd2b..eb4357b1 100644
--- a/ca/tests/Makefile.in
+++ b/ca/tests/Makefile.in
@@ -3,12 +3,11 @@
PYTHON = @PYTHON@
abs_top_builddir = @abs_top_builddir@
-all: protocol-samples
+all:
+ @true
clean:
- rm -rf smoketest.dir left-right-protocol-samples publication-protocol-samples yamltest.dir rcynic.xml rcynic-data
-
-protocol-samples: left-right-protocol-samples/.stamp publication-protocol-samples/.stamp
+ rm -rf smoketest.dir left-right-protocol-samples publication-protocol-samples publication-control-protocol-samples rrdp-samples yamltest.dir rcynic.xml rcynic-data
left-right-protocol-samples/.stamp: left-right-protocol-samples.xml split-protocol-samples.xsl
rm -rf left-right-protocol-samples
@@ -16,20 +15,44 @@ left-right-protocol-samples/.stamp: left-right-protocol-samples.xml split-protoc
xsltproc --param verbose 0 --stringparam dir left-right-protocol-samples split-protocol-samples.xsl left-right-protocol-samples.xml
touch $@
+left-right-relaxng: left-right-protocol-samples/.stamp
+ xmllint --noout --relaxng ../../schemas/relaxng/left-right.rng left-right-protocol-samples/*.xml
+
publication-protocol-samples/.stamp: publication-protocol-samples.xml split-protocol-samples.xsl
rm -rf publication-protocol-samples
mkdir publication-protocol-samples
xsltproc --param verbose 0 --stringparam dir publication-protocol-samples split-protocol-samples.xsl publication-protocol-samples.xml
touch $@
-relaxng: protocol-samples
- xmllint --noout --relaxng ../../schemas/relaxng/left-right-schema.rng left-right-protocol-samples/*.xml
- xmllint --noout --relaxng ../../schemas/relaxng/up-down-schema.rng up-down-protocol-samples/*.xml
- xmllint --noout --relaxng ../../schemas/relaxng/publication-schema.rng publication-protocol-samples/*.xml
+publication-relaxng: publication-protocol-samples/.stamp
+ xmllint --noout --relaxng ../../schemas/relaxng/publication.rng publication-protocol-samples/*.xml
+
+publication-control-protocol-samples/.stamp: publication-control-protocol-samples.xml split-protocol-samples.xsl
+ rm -rf publication-control-protocol-samples
+ mkdir publication-control-protocol-samples
+ xsltproc --param verbose 0 --stringparam dir publication-control-protocol-samples split-protocol-samples.xsl publication-control-protocol-samples.xml
+ touch $@
+
+publication-control-relaxng: publication-control-protocol-samples/.stamp
+ xmllint --noout --relaxng ../../schemas/relaxng/publication-control.rng publication-control-protocol-samples/*.xml
+
+rrdp-samples/.stamp: rrdp-samples.xml split-protocol-samples.xsl
+ rm -rf rrdp-samples
+ mkdir rrdp-samples
+ xsltproc --param verbose 0 --stringparam dir rrdp-samples split-protocol-samples.xsl rrdp-samples.xml
+ touch $@
+
+rrdp-relaxng: rrdp-samples/.stamp
+ xmllint --noout --relaxng ../../schemas/relaxng/rrdp.rng rrdp-samples/*.xml
+
+up-down-relaxng:
+ xmllint --noout --relaxng ../../schemas/relaxng/up-down.rng up-down-protocol-samples/*.xml
+
+relaxng: up-down-relaxng left-right-relaxng publication-relaxng publication-control-relaxng rrdp-relaxng
all-tests:: relaxng
-parse-test: protocol-samples
+parse-test: left-right-protocol-samples publication-protocol-samples publication-control-protocol-samples
${PYTHON} xml-parse-test.py
all-tests:: parse-test
@@ -67,7 +90,6 @@ YAMLTEST_CONFIG = smoketest.1.yaml
yamltest:
rm -rf yamltest.dir rcynic-data
- ${PYTHON} sql-cleaner.py
${PYTHON} yamltest.py ${YAMLTEST_CONFIG}
YAMLCONF_CONFIG = ${YAMLTEST_CONFIG}
diff --git a/ca/tests/bgpsec-yaml.py b/ca/tests/bgpsec-yaml.py
index 1562f86e..500d2b9d 100755
--- a/ca/tests/bgpsec-yaml.py
+++ b/ca/tests/bgpsec-yaml.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# $Id$
-#
+#
# Copyright (C) 2014 Dragon Research Labs ("DRL")
#
# Permission to use, copy, modify, and distribute this software for any
@@ -30,28 +30,28 @@ root = "Root"
class Kid(object):
- def __init__(self, n):
- self.name = "ISP-%03d" % n
- self.ipv4 = "10.%d.0.0/16" % n
- self.asn = n
- self.router_id = n * 10000
+ def __init__(self, i):
+ self.name = "ISP-%03d" % i
+ self.ipv4 = "10.%d.0.0/16" % i
+ self.asn = i
+ self.router_id = i * 10000
- @property
- def declare(self):
- return dict(name = self.name,
- ipv4 = self.ipv4,
- asn = self.asn,
- hosted_by = root,
- roa_request = [dict(asn = self.asn, ipv4 = self.ipv4)],
- router_cert = [dict(asn = self.asn, router_id = self.router_id)])
+ @property
+ def declare(self):
+ return dict(name = self.name,
+ ipv4 = self.ipv4,
+ asn = self.asn,
+ hosted_by = root,
+ roa_request = [dict(asn = self.asn, ipv4 = self.ipv4)],
+ router_cert = [dict(asn = self.asn, router_id = self.router_id)])
- @property
- def del_routercert(self):
- return dict(name = self.name, router_cert_del = [dict(asn = self.asn, router_id = self.router_id)])
+ @property
+ def del_routercert(self):
+ return dict(name = self.name, router_cert_del = [dict(asn = self.asn, router_id = self.router_id)])
- @property
- def add_routercert(self):
- return dict(name = self.name, router_cert_add = [dict(asn = self.asn, router_id = self.router_id)])
+ @property
+ def add_routercert(self):
+ return dict(name = self.name, router_cert_add = [dict(asn = self.asn, router_id = self.router_id)])
kids = [Kid(n + 1) for n in xrange(200)]
@@ -72,14 +72,14 @@ docs.append([shell_first,
gym = kids[50:70]
for kid in gym:
- docs.append([shell_next,
- kid.del_routercert,
- sleeper])
+ docs.append([shell_next,
+ kid.del_routercert,
+ sleeper])
for kid in gym:
- docs.append([shell_next,
- kid.add_routercert,
- sleeper])
+ docs.append([shell_next,
+ kid.add_routercert,
+ sleeper])
print '''\
# This configuration was generated by a script. Edit at your own risk.
diff --git a/ca/tests/left-right-protocol-samples.xml b/ca/tests/left-right-protocol-samples.xml
index 7b97386d..9729c68c 100644
--- a/ca/tests/left-right-protocol-samples.xml
+++ b/ca/tests/left-right-protocol-samples.xml
@@ -37,15 +37,15 @@
<completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="create" tag="a000" self_handle="42"/>
+ <tenant action="create" tag="a000" tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="create" tag="a000" self_handle="42"/>
+ <tenant action="create" tag="a000" tenant_handle="42"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="set" self_handle="42"
+ <tenant action="set" tenant_handle="42"
rekey="yes"
reissue="yes"
revoke="yes"
@@ -92,19 +92,19 @@
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
</bpki_glue>
- </self>
+ </tenant>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="set" self_handle="42"/>
+ <tenant action="set" tenant_handle="42"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="get" self_handle="42"/>
+ <tenant action="get" tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="get" self_handle="42">
+ <tenant action="get" tenant_handle="42">
<bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
@@ -143,15 +143,15 @@
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
</bpki_glue>
- </self>
+ </tenant>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="list"/>
+ <tenant action="list"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="list" self_handle="42">
+ <tenant action="list" tenant_handle="42">
<bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
@@ -190,22 +190,22 @@
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
</bpki_glue>
- </self>
- <self action="list" self_handle="99"/>
+ </tenant>
+ <tenant action="list" tenant_handle="99"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="destroy" self_handle="42"/>
+ <tenant action="destroy" tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <self action="destroy" self_handle="42"/>
+ <tenant action="destroy" tenant_handle="42"/>
</msg>
<!-- ==== -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="create" self_handle="42" bsc_handle="17"
+ <bsc action="create" tenant_handle="42" bsc_handle="17"
generate_keypair="yes"
key_type="rsa"
hash_alg="sha256"
@@ -233,13 +233,13 @@
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="create" self_handle="42" bsc_handle="17">
+ <bsc action="create" tenant_handle="42" bsc_handle="17">
<pkcs10_request>cmVxdWVzdAo=</pkcs10_request>
</bsc>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="set" self_handle="42" bsc_handle="17">
+ <bsc action="set" tenant_handle="42" bsc_handle="17">
<signing_cert>
MIIDHTCCAgWgAwIBAgIJAKUUCoKn9ovVMA0GCSqGSIb3DQEBBQUAMCYxJDAiBgNV
BAMTG1Rlc3QgQ2VydGlmaWNhdGUgQWxpY2UgUm9vdDAeFw0wNzA4MDExOTUzMDda
@@ -274,15 +274,15 @@
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="set" self_handle="42" bsc_handle="17"/>
+ <bsc action="set" tenant_handle="42" bsc_handle="17"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="get" self_handle="42" bsc_handle="17"/>
+ <bsc action="get" tenant_handle="42" bsc_handle="17"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="get" self_handle="42" bsc_handle="17">
+ <bsc action="get" tenant_handle="42" bsc_handle="17">
<signing_cert>
MIIDHTCCAgWgAwIBAgIJAKUUCoKn9ovVMA0GCSqGSIb3DQEBBQUAMCYxJDAiBgNV
BAMTG1Rlc3QgQ2VydGlmaWNhdGUgQWxpY2UgUm9vdDAeFw0wNzA4MDExOTUzMDda
@@ -306,11 +306,11 @@
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="list" self_handle="42"/>
+ <bsc action="list" tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="get" self_handle="42" bsc_handle="17">
+ <bsc action="get" tenant_handle="42" bsc_handle="17">
<signing_cert>
MIIDHTCCAgWgAwIBAgIJAKUUCoKn9ovVMA0GCSqGSIb3DQEBBQUAMCYxJDAiBgNV
BAMTG1Rlc3QgQ2VydGlmaWNhdGUgQWxpY2UgUm9vdDAeFw0wNzA4MDExOTUzMDda
@@ -334,24 +334,24 @@
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="destroy" self_handle="42" bsc_handle="17"/>
+ <bsc action="destroy" tenant_handle="42" bsc_handle="17"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <bsc action="destroy" self_handle="42" bsc_handle="17"/>
+ <bsc action="destroy" tenant_handle="42" bsc_handle="17"/>
</msg>
<!-- ==== -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="create" self_handle="42" parent_handle="666"
+ <parent action="create" tenant_handle="42" parent_handle="666"
peer_contact_uri="https://re.bar.example/bandicoot/"
sia_base="rsync://repo.foo.example/wombat/"
bsc_handle="17"
repository_handle="120"
sender_name="tweedledee"
recipient_name="tweedledum">
- <bpki_cms_cert>
+ <bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -369,8 +369,8 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_cert>
- <bpki_cms_glue>
+ </bpki_cert>
+ <bpki_glue>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -388,16 +388,16 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_glue>
+ </bpki_glue>
</parent>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="create" self_handle="42" parent_handle="666"/>
+ <parent action="create" tenant_handle="42" parent_handle="666"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="set" self_handle="42" parent_handle="666"
+ <parent action="set" tenant_handle="42" parent_handle="666"
peer_contact_uri="https://re.bar.example/bandicoot/"
sia_base="rsync://repo.foo.example/wombat/"
bsc_handle="17"
@@ -405,7 +405,7 @@
rekey="yes"
reissue="yes"
revoke="yes">
- <bpki_cms_cert>
+ <bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -423,8 +423,8 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_cert>
- <bpki_cms_glue>
+ </bpki_cert>
+ <bpki_glue>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -442,25 +442,25 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_glue>
+ </bpki_glue>
</parent>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="set" self_handle="42" parent_handle="666"/>
+ <parent action="set" tenant_handle="42" parent_handle="666"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="get" self_handle="42" parent_handle="666"/>
+ <parent action="get" tenant_handle="42" parent_handle="666"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="get" self_handle="42" parent_handle="666"
+ <parent action="get" tenant_handle="42" parent_handle="666"
peer_contact_uri="https://re.bar.example/bandicoot/"
sia_base="rsync://repo.foo.example/wombat/"
bsc_handle="17"
repository_handle="120">
- <bpki_cms_cert>
+ <bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -478,8 +478,8 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_cert>
- <bpki_cms_glue>
+ </bpki_cert>
+ <bpki_glue>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -497,21 +497,21 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_glue>
+ </bpki_glue>
</parent>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="list" self_handle="42"/>
+ <parent action="list" tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="list" self_handle="42" parent_handle="666"
+ <parent action="list" tenant_handle="42" parent_handle="666"
peer_contact_uri="https://re.bar.example/bandicoot/"
sia_base="rsync://repo.foo.example/wombat/"
bsc_handle="17"
repository_handle="120">
- <bpki_cms_cert>
+ <bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -529,8 +529,8 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_cert>
- <bpki_cms_glue>
+ </bpki_cert>
+ <bpki_glue>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
@@ -548,22 +548,22 @@
sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cms_glue>
+ </bpki_glue>
</parent>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="destroy" self_handle="42"
+ <parent action="destroy" tenant_handle="42"
parent_handle="666"/> </msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <parent action="destroy" self_handle="42" parent_handle="666"/>
+ <parent action="destroy" tenant_handle="42" parent_handle="666"/>
</msg>
<!-- ==== -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="create" self_handle="42" child_handle="3"
+ <child action="create" tenant_handle="42" child_handle="3"
bsc_handle="17">
<bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
@@ -588,11 +588,11 @@
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="create" self_handle="42" child_handle="3"/>
+ <child action="create" tenant_handle="42" child_handle="3"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="set" self_handle="42" child_handle="3"
+ <child action="set" tenant_handle="42" child_handle="3"
bsc_handle="17"
reissue="yes">
<bpki_cert>
@@ -618,15 +618,15 @@
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="set" self_handle="42" child_handle="3"/>
+ <child action="set" tenant_handle="42" child_handle="3"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="get" self_handle="42" child_handle="3"/>
+ <child action="get" tenant_handle="42" child_handle="3"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="get" self_handle="42" child_handle="3"
+ <child action="get" tenant_handle="42" child_handle="3"
bsc_handle="17">
<bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
@@ -651,11 +651,11 @@
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="list" self_handle="42"/>
+ <child action="list" tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="list" self_handle="42" child_handle="3"
+ <child action="list" tenant_handle="42" child_handle="3"
bsc_handle="17">
<bpki_cert>
MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
@@ -680,17 +680,17 @@
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="destroy" self_handle="42" child_handle="3"/>
+ <child action="destroy" tenant_handle="42" child_handle="3"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <child action="destroy" self_handle="42" child_handle="3"/>
+ <child action="destroy" tenant_handle="42" child_handle="3"/>
</msg>
<!-- ==== -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="create" self_handle="42" repository_handle="120"
+ <repository action="create" tenant_handle="42" repository_handle="120"
peer_contact_uri="https://re.bar.example/bandicoot/"
bsc_handle="17">
<bpki_cert>
@@ -735,11 +735,11 @@
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="create" self_handle="42" repository_handle="120"/>
+ <repository action="create" tenant_handle="42" repository_handle="120"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="set" self_handle="42" repository_handle="120"
+ <repository action="set" tenant_handle="42" repository_handle="120"
peer_contact_uri="https://re.bar.example/bandicoot/"
bsc_handle="17">
<bpki_cert>
@@ -784,15 +784,15 @@
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="set" self_handle="42" repository_handle="120"/>
+ <repository action="set" tenant_handle="42" repository_handle="120"/>
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="get" self_handle="42" repository_handle="120"/>
+ <repository action="get" tenant_handle="42" repository_handle="120"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="get" self_handle="42" repository_handle="120"
+ <repository action="get" tenant_handle="42" repository_handle="120"
peer_contact_uri="https://re.bar.example/bandicoot/"
bsc_handle="17">
<bpki_cert>
@@ -837,11 +837,11 @@
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="list" self_handle="42"/>
+ <repository action="list" tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="list" self_handle="42" repository_handle="120"
+ <repository action="list" tenant_handle="42" repository_handle="120"
peer_contact_uri="https://re.bar.example/bandicoot/"
bsc_handle="17">
<bpki_cert>
@@ -886,21 +886,21 @@
</msg>
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="destroy" self_handle="42" repository_handle="120"/>
+ <repository action="destroy" tenant_handle="42" repository_handle="120"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <repository action="destroy" self_handle="42" repository_handle="120"/>
+ <repository action="destroy" tenant_handle="42" repository_handle="120"/>
</msg>
<!-- ==== -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_resources self_handle="42" child_handle="289"/>
+ <list_resources tenant_handle="42" child_handle="289"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_resources self_handle="42" child_handle="289"
+ <list_resources tenant_handle="42" child_handle="289"
valid_until="2008-04-01T00:00:00Z"
ipv4="10.0.0.44/32,10.3.0.44/32"
ipv6="fe80:deed:f00d::/48,fe80:dead:beef:2::-fe80:dead:beef:2::49"
@@ -910,16 +910,16 @@
<!-- === -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_roa_requests self_handle="42"/>
+ <list_roa_requests tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_roa_requests self_handle="42"
+ <list_roa_requests tenant_handle="42"
asn="666"
ipv4="10.0.0.44/32,10.3.0.44/32"
ipv6="fe80:deed:f00d::/48,fe80:dead:beef::/48-56"
/>
- <list_roa_requests self_handle="42"
+ <list_roa_requests tenant_handle="42"
asn="12345"
ipv4="10.0.0.44/32"
ipv6="2002:a00::/48-56"
@@ -929,11 +929,11 @@
<!-- === -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_received_resources self_handle="42"/>
+ <list_received_resources tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_received_resources self_handle="42"
+ <list_received_resources tenant_handle="42"
parent_handle="Alice"
notBefore="2010-02-22T03:44:23Z"
notAfter="2011-02-21T11:03:49Z"
@@ -942,7 +942,7 @@
aia_uri="rsync://arin.rpki.net/arin/arin.cer"
asn="1280,3557"
ipv4="149.20.0.0/16,192.5.4.0/23,204.152.184.0/21"/>
- <list_received_resources self_handle="42"
+ <list_received_resources tenant_handle="42"
parent_handle="Bob"
uri="rsync://arin.rpki.net/arin/1/uWqpa8GkcEDBZkEsmOEofeDKk9s.cer"
notBefore="2010-02-22T03:44:20Z"
@@ -958,11 +958,11 @@
<!-- === -->
<msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_published_objects self_handle="42"/>
+ <list_published_objects tenant_handle="42"/>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <list_published_objects self_handle="42" uri="rsync://rpki.example.org/rpki/DEMEtlxZrZes7TNGbe7XwVSMgW0.crl">
+ <list_published_objects tenant_handle="42" uri="rsync://rpki.example.org/rpki/DEMEtlxZrZes7TNGbe7XwVSMgW0.crl">
MIIBrjCBlwIBATANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEygwQzQzMDRCNjVDNTlBRDk3
QUNFRDMzNDY2REVFRDdDMTU0OEM4MTZEFw0wOTA5MjgyMDUxNDlaFw0wOTA5MjgyMTUxNDla
oDAwLjAfBgNVHSMEGDAWgBQMQwS2XFmtl6ztM0Zt7tfBVIyBbTALBgNVHRQEBAICAWkwDQYJ
@@ -973,7 +973,7 @@
fBk4i7H945v/zs7bLLMJxTs8+ao4iCDuknjbGhjWmi9xrTXDtcCXx607rPDkJQcJE2WnRS/U
HIA=
</list_published_objects>
- <list_published_objects self_handle="42" uri="rsync://rpki.example.org/rpki/DEMEtlxZrZes7TNGbe7XwVSMgW0.mft">
+ <list_published_objects tenant_handle="42" uri="rsync://rpki.example.org/rpki/DEMEtlxZrZes7TNGbe7XwVSMgW0.mft">
MIIHBQYJKoZIhvcNAQcCoIIG9jCCBvICAQMxDTALBglghkgBZQMEAgEwggEfBgsqhkiG9w0B
CRABGqCCAQ4EggEKMIIBBgICAWoYDzIwMDkwOTI4MjA1MTQ5WhgPMjAwOTA5MjgyMTUxNDla
BglghkgBZQMEAgEwgdIwRBYfREVNRXRseFpyWmVzN1ROR2JlN1h3VlNNZ1cwLmNybAMhAPgd
@@ -1009,7 +1009,7 @@
yML8lQJAFAyjnXJ+doGbqfTUpVH4q4drqRb73WbL0zf/Z2HGwhDlTmsAdjparWdQcfXIVrJF
ynS1fab9XZfj+VtBFKjooDjaLw==
</list_published_objects>
- <list_published_objects self_handle="42" uri="rsync://rpki.example.org/rpki/ldvxcHGdr3oKHcPj-gukmetNRZ0.roa">
+ <list_published_objects tenant_handle="42" uri="rsync://rpki.example.org/rpki/ldvxcHGdr3oKHcPj-gukmetNRZ0.roa">
MIIGnQYJKoZIhvcNAQcCoIIGjjCCBooCAQMxDTALBglghkgBZQMEAgEwMQYLKoZIhvcNAQkQ
ARigIgQgMB4CAg3lMBgwFgQCAAEwEDAGAwQAwAUEMAYDBADABQWgggSTMIIEjzCCA3egAwIB
AgIBAjANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEygwQzQzMDRCNjVDNTlBRDk3QUNFRDMz
@@ -1043,7 +1043,7 @@
+N931gu2r5I/XB/MGgGvXNWozK7RuMn55i5hMqI2NQs+/b7/AQU0+/i3g7SlLA8iZwHq49U2
ZXRCjLXcy0tQOWVsMnGfReN8oNDhHbc=
</list_published_objects>
- <list_published_objects self_handle="42" uri="rsync://rpki.example.org/rpki/xopNGcsB_p7eafYqXatmVV8HZd0.roa">
+ <list_published_objects tenant_handle="42" uri="rsync://rpki.example.org/rpki/xopNGcsB_p7eafYqXatmVV8HZd0.roa">
MIIGoQYJKoZIhvcNAQcCoIIGkjCCBo4CAQMxDTALBglghkgBZQMEAgEwMAYLKoZIhvcNAQkQ
ARigIQQfMB0CAgUAMBcwFQQCAAEwDzAFAwMAlRQwBgMEA8yYuKCCBJgwggSUMIIDfKADAgEC
AgEDMA0GCSqGSIb3DQEBCwUAMDMxMTAvBgNVBAMTKDBDNDMwNEI2NUM1OUFEOTdBQ0VEMzM0
@@ -1083,11 +1083,11 @@
<!-- === -->
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <report_error self_handle="42" error_code="your_hair_is_on_fire">text string</report_error>
+ <report_error tenant_handle="42" error_code="your_hair_is_on_fire">text string</report_error>
</msg>
<msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/left-right-spec/">
- <report_error self_handle="42" error_code="your_hair_is_on_fire"/>
+ <report_error tenant_handle="42" error_code="your_hair_is_on_fire"/>
</msg>
</completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
diff --git a/ca/tests/myrpki-xml-parse-test.py b/ca/tests/myrpki-xml-parse-test.py
index 9db7ec57..d915ea5b 100644
--- a/ca/tests/myrpki-xml-parse-test.py
+++ b/ca/tests/myrpki-xml-parse-test.py
@@ -25,77 +25,77 @@ relaxng = lxml.etree.RelaxNG(file = "myrpki.rng")
tree = lxml.etree.parse("myrpki.xml").getroot()
if False:
- print lxml.etree.tostring(tree, pretty_print = True, encoding = "us-ascii", xml_declaration = True)
+ print lxml.etree.tostring(tree, pretty_print = True, encoding = "us-ascii", xml_declaration = True)
relaxng.assertValid(tree)
def showitems(y):
- if False:
- for k, v in y.items():
- if v:
- print " ", k, v
+ if False:
+ for k, v in y.items():
+ if v:
+ print " ", k, v
def tag(t):
- return "{http://www.hactrn.net/uris/rpki/myrpki/}" + t
+ return "{http://www.hactrn.net/uris/rpki/myrpki/}" + t
print "My handle:", tree.get("handle")
print "Children:"
for x in tree.getiterator(tag("child")):
- print " ", x
- print " Handle:", x.get("handle")
- print " ASNS: ", rpki.resource_set.resource_set_as(x.get("asns"))
- print " IPv4: ", rpki.resource_set.resource_set_ipv4(x.get("v4"))
- print " Valid: ", x.get("valid_until")
- showitems(x)
+ print " ", x
+ print " Handle:", x.get("handle")
+ print " ASNS: ", rpki.resource_set.resource_set_as(x.get("asns"))
+ print " IPv4: ", rpki.resource_set.resource_set_ipv4(x.get("v4"))
+ print " Valid: ", x.get("valid_until")
+ showitems(x)
print
print "ROA requests:"
for x in tree.getiterator(tag("roa_request")):
- print " ", x
- print " ASN: ", x.get("asn")
- print " IPv4:", rpki.resource_set.roa_prefix_set_ipv4(x.get("v4"))
- print " IPv6:", rpki.resource_set.roa_prefix_set_ipv6(x.get("v6"))
- showitems(x)
+ print " ", x
+ print " ASN: ", x.get("asn")
+ print " IPv4:", rpki.resource_set.roa_prefix_set_ipv4(x.get("v4"))
+ print " IPv6:", rpki.resource_set.roa_prefix_set_ipv6(x.get("v6"))
+ showitems(x)
print
def showpem(label, b64, kind):
- cmd = ("openssl", kind, "-noout", "-text", "-inform", "DER")
- if kind == "x509":
- cmd += ("-certopt", "no_pubkey,no_sigdump")
- p = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
- text = p.communicate(input = base64.b64decode(b64))[0]
- if p.returncode != 0:
- raise subprocess.CalledProcessError(returncode = p.returncode, cmd = cmd)
- print label, text
+ cmd = ("openssl", kind, "-noout", "-text", "-inform", "DER")
+ if kind == "x509":
+ cmd += ("-certopt", "no_pubkey,no_sigdump")
+ p = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
+ text = p.communicate(input = base64.b64decode(b64))[0]
+ if p.returncode != 0:
+ raise subprocess.CalledProcessError(returncode = p.returncode, cmd = cmd)
+ print label, text
for x in tree.getiterator(tag("child")):
- cert = x.findtext(tag("bpki_certificate"))
- if cert:
- showpem("Child", cert, "x509")
+ cert = x.findtext(tag("bpki_certificate"))
+ if cert:
+ showpem("Child", cert, "x509")
for x in tree.getiterator(tag("parent")):
- print "Parent URI:", x.get("service_uri")
- cert = x.findtext(tag("bpki_certificate"))
- if cert:
- showpem("Parent", cert, "x509")
+ print "Parent URI:", x.get("service_uri")
+ cert = x.findtext(tag("bpki_certificate"))
+ if cert:
+ showpem("Parent", cert, "x509")
ca = tree.findtext(tag("bpki_ca_certificate"))
if ca:
- showpem("CA", ca, "x509")
+ showpem("CA", ca, "x509")
bsc = tree.findtext(tag("bpki_bsc_certificate"))
if bsc:
- showpem("BSC EE", bsc, "x509")
+ showpem("BSC EE", bsc, "x509")
repo = tree.findtext(tag("bpki_repository_certificate"))
if repo:
- showpem("Repository", repo, "x509")
+ showpem("Repository", repo, "x509")
req = tree.findtext(tag("bpki_bsc_pkcs10"))
if req:
- showpem("BSC EE", req, "req")
+ showpem("BSC EE", req, "req")
crl = tree.findtext(tag("bpki_crl"))
if crl:
- showpem("CA", crl, "crl")
+ showpem("CA", crl, "crl")
diff --git a/ca/tests/old_irdbd.py b/ca/tests/old_irdbd.py
index d66e683e..d26c3476 100644
--- a/ca/tests/old_irdbd.py
+++ b/ca/tests/old_irdbd.py
@@ -15,5 +15,5 @@
# PERFORMANCE OF THIS SOFTWARE.
if __name__ == "__main__":
- import rpki.old_irdbd
- rpki.old_irdbd.main()
+ import rpki.old_irdbd
+ rpki.old_irdbd.main()
diff --git a/ca/tests/publication-control-protocol-samples.xml b/ca/tests/publication-control-protocol-samples.xml
new file mode 100644
index 00000000..e094f3f6
--- /dev/null
+++ b/ca/tests/publication-control-protocol-samples.xml
@@ -0,0 +1,155 @@
+<!-- -*- SGML -*-
+ - $Id$
+ -
+ - Copyright (C) 2008 American Registry for Internet Numbers ("ARIN")
+ -
+ - 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 ARIN DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ARIN 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 is a collection of sample publication protocol PDU samples
+ - to use as test cases for the publication protocol RelaxNG schema.
+ -->
+
+<completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
+
+ <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="create" client_handle="3" base_uri="rsync://wombat.invalid/">
+ <bpki_cert>
+ MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
+ BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
+ MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
+ b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
+ G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
+ Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
+ DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
+ uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
+ 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
+ diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
+ ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
+ hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
+ cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
+ XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
+ sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
+ YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
+ 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
+ </bpki_cert>
+ </client>
+ </msg>
+
+ <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="create" client_handle="3"/>
+ </msg>
+
+ <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="set" client_handle="3">
+ <bpki_glue>
+ MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
+ BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
+ MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
+ b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
+ G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
+ Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
+ DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
+ uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
+ 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
+ diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
+ ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
+ hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
+ cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
+ XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
+ sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
+ YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
+ 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
+ </bpki_glue>
+ </client>
+ </msg>
+
+ <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="set" client_handle="3"/>
+ </msg>
+
+ <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="get" client_handle="3"/>
+ </msg>
+
+ <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="get" client_handle="3" base_uri="rsync://wombat.invalid/">
+ <bpki_cert>
+ MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
+ BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
+ MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
+ b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
+ G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
+ Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
+ DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
+ uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
+ 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
+ diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
+ ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
+ hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
+ cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
+ XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
+ sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
+ YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
+ 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
+ </bpki_cert>
+ </client>
+ </msg>
+
+ <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="list"/>
+ </msg>
+
+ <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="list" client_handle="3">
+ <bpki_cert>
+ MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
+ BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
+ MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
+ b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
+ G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
+ Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
+ DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
+ uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
+ 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
+ diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
+ ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
+ hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
+ cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
+ XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
+ sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
+ YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
+ 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
+ </bpki_cert>
+ </client>
+ </msg>
+
+ <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="destroy" client_handle="3"/>
+ </msg>
+
+ <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <client action="destroy" client_handle="3"/>
+ </msg>
+
+ <!-- === -->
+
+ <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <report_error error_code="your_hair_is_on_fire">text string</report_error>
+ </msg>
+
+ <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-control/">
+ <report_error error_code="your_hair_is_on_fire"/>
+ </msg>
+
+</completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
diff --git a/ca/tests/publication-protocol-samples.xml b/ca/tests/publication-protocol-samples.xml
index 96b095a7..6d0a99a9 100644
--- a/ca/tests/publication-protocol-samples.xml
+++ b/ca/tests/publication-protocol-samples.xml
@@ -1,370 +1,107 @@
<!-- -*- SGML -*-
- - $Id$
+ - $Id$
-
- - Copyright (C) 2008 American Registry for Internet Numbers ("ARIN")
+ - Sample PDUs for RPKI publication protocol, from current I-D.
-
- - 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.
+ - Copyright (c) 2014 IETF Trust and the persons identified as authors
+ - of the code. All rights reserved.
-
- - THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
- - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- - AND FITNESS. IN NO EVENT SHALL ARIN 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.
+ - Redistribution and use in source and binary forms, with or without
+ - modification, are permitted provided that the following conditions
+ - are met:
-
+ - * Redistributions of source code must retain the above copyright
+ - notice, this list of conditions and the following disclaimer.
-
- - This is a collection of sample publication protocol PDU samples
- - to use as test cases for the publication protocol RelaxNG schema.
+ - * Redistributions in binary form must reproduce the above copyright
+ - notice, this list of conditions and the following disclaimer in
+ - the documentation and/or other materials provided with the
+ - distribution.
+ -
+ - * Neither the name of Internet Society, IETF or IETF Trust, nor the
+ - names of specific contributors, may be used to endorse or promote
+ - products derived from this software without specific prior written
+ - permission.
+ -
+ - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ - POSSIBILITY OF SUCH DAMAGE.
-->
<completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
- <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="query" version="1">
- <config action="set">
- <bpki_crl>
- MIIBezBlAgEBMA0GCSqGSIb3DQEBCwUAMCMxITAfBgNVBAMTGFRlc3QgQ2VydGlm
- aWNhdGUgcHViZCBUQRcNMDgwNjAyMjE0OTQ1WhcNMDgwNzAyMjE0OTQ1WqAOMAww
- CgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAFWCWgBl4ljVqX/CHo+RpqYt
- vmKMnjPVflMXUB7i28RGP4DAq4l7deDU7Q82xEJyE4TXMWDWAV6UG6uUGum0VHWO
- cj9ohqyiZUGfOsKg2hbwkETm8sAENOsi1yNdyKGk6jZ16aF5fubxQqZa1pdGCSac
- 1/ZYC5sLLhEz3kmz+B9z9mXFVc5TgAh4dN3Gy5ftF8zZAFpDGnS4biCnRVqhGv6R
- 0Lh/5xmii+ZU6kNDhbeMsjJg+ZOmtN+wMeHSIbjiy0WuuaZ3k2xSh0C94anrHBZA
- vvCRhbazjR0Ef5OMZ5lcllw3uO8IHuoisHKkehy4Y0GySdj98fV+OuiRTH9vt/M=
- </bpki_crl>
- </config>
- </msg>
-
- <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="reply" version="1">
- <config action="set"/>
- </msg>
-
- <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="query" version="1">
- <config action="get"/>
- </msg>
-
- <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="reply" version="1">
- <config action="get">
- <bpki_crl>
- MIIBezBlAgEBMA0GCSqGSIb3DQEBCwUAMCMxITAfBgNVBAMTGFRlc3QgQ2VydGlm
- aWNhdGUgcHViZCBUQRcNMDgwNjAyMjE0OTQ1WhcNMDgwNzAyMjE0OTQ1WqAOMAww
- CgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADggEBAFWCWgBl4ljVqX/CHo+RpqYt
- vmKMnjPVflMXUB7i28RGP4DAq4l7deDU7Q82xEJyE4TXMWDWAV6UG6uUGum0VHWO
- cj9ohqyiZUGfOsKg2hbwkETm8sAENOsi1yNdyKGk6jZ16aF5fubxQqZa1pdGCSac
- 1/ZYC5sLLhEz3kmz+B9z9mXFVc5TgAh4dN3Gy5ftF8zZAFpDGnS4biCnRVqhGv6R
- 0Lh/5xmii+ZU6kNDhbeMsjJg+ZOmtN+wMeHSIbjiy0WuuaZ3k2xSh0C94anrHBZA
- vvCRhbazjR0Ef5OMZ5lcllw3uO8IHuoisHKkehy4Y0GySdj98fV+OuiRTH9vt/M=
- </bpki_crl>
- </config>
- </msg>
-
- <!-- === -->
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="create" client_handle="3" base_uri="rsync://wombat.invalid/">
- <bpki_cert>
- MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
- BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
- MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
- b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
- G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
- Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
- DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
- uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
- 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
- diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
- ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
- hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
- cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
- XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
- sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
- YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
- 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cert>
- </client>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="create" client_handle="3"/>
- </msg>
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="set" client_handle="3">
- <bpki_glue>
- MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
- BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
- MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
- b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
- G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
- Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
- DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
- uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
- 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
- diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
- ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
- hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
- cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
- XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
- sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
- YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
- 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_glue>
- </client>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="set" client_handle="3"/>
- </msg>
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="get" client_handle="3"/>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="get" client_handle="3" base_uri="rsync://wombat.invalid/">
- <bpki_cert>
- MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
- BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
- MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
- b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
- G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
- Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
- DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
- uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
- 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
- diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
- ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
- hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
- cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
- XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
- sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
- YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
- 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cert>
- </client>
- </msg>
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="list"/>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="list" client_handle="3">
- <bpki_cert>
- MIIDGzCCAgOgAwIBAgIJAKi+/+wUhQlxMA0GCSqGSIb3DQEBBQUAMCQxIjAgBgNV
- BAMTGVRlc3QgQ2VydGlmaWNhdGUgQm9iIFJvb3QwHhcNMDcwODAxMTk1MzEwWhcN
- MDcwODMxMTk1MzEwWjAkMSIwIAYDVQQDExlUZXN0IENlcnRpZmljYXRlIEJvYiBS
- b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArKYUtJaM5PH5917S
- G2ACc7iBYdQO2HYyu8Gb6i9Q2Gxc3cWEX7RTBvgOL79pWf3GIdnoupzMnoZVtY3G
- Ux2G/0WkmLui2TCeDhcfXdQ4rcp8J3V/6ESj+yuEPPOG8UN17mUKKgujrch6ZvgC
- DO9AyOK/uXu+ABQXTPsn2pVe2EVh3V004ShLi8GKgVdqb/rW/6GTg0Xb/zLT6WWM
- uT++6sXTlztJdQYkRamJvKfQDU1naC8mAkGf79Tba0xyBGAUII0GfREY6t4/+NAP
- 2Yyb3xNlBqcJoTov0JfNKHZcCZePr79j7LK/hkZxxip+Na9xDpE+oQRV+DRukCRJ
- diqg+wIDAQABo1AwTjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTDEsXJe6pjAQD4
- ULlB7+GMDBlimTAfBgNVHSMEGDAWgBTDEsXJe6pjAQD4ULlB7+GMDBlimTANBgkq
- hkiG9w0BAQUFAAOCAQEAWWkNcW6S1tKKqtzJsdfhjJiAAPQmOXJskv0ta/8f6Acg
- cum1YieNdtT0n96P7CUHOWP8QBb91JzeewR7b6WJLwb1Offs3wNq3kk75pJe89r4
- XY39EZHhMW+Dv0PhIKu2CgD4LeyH1FVTQkF/QObGEmkn+s+HTsuzd1l2VLwcP1Sm
- sqep6LAlFj62qqaIJzNeQ9NVkBqtkygnYlBOkaBTHfQTux3jYNpEo8JJB5e/WFdH
- YyMNrG2xMOtIC7T4+IOHgT8PgrNhaeDg9ctewj0X8Qi9nI9nXeinicLX8vj6hdEq
- 3ORv7RZMJNYqv1HQ3wUE2B7fCPFv7EUwzaCds1kgRQ==
- </bpki_cert>
- </client>
- </msg>
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="destroy" client_handle="3"/>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <client action="destroy" client_handle="3"/>
- </msg>
-
- <!-- === -->
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <certificate action="publish" uri="rsync://wombat.invalid/testbed/RIR/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.cer">
- MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEyhERjRBODAxN0U2
- NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFMkEzMB4XDTA4MDUyMjE4MDUxMloXDTA4MDUy
- NDE3NTQ1M1owMzExMC8GA1UEAxMoOEZCODIxOEYwNkU1MEFCNzAyQTdEOTZEQzhGMENEQ0Q4
- MjhGN0YxNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMeziKp0k5nP7v6SZoNs
- XIMQYRgNtC6Fr/9Xm/1yQHomiPqHUk47rHhGojYiK5AhkrwoYhkH4UjJl2iwklDYczXuaBU3
- F5qrKlZ4aZnjIxdlP7+hktVpeApL6yuJTUAYeC3UIxnLDVdD6phydZ/FOQluffiNDjzteCCv
- oyOUatqt8WB+oND6LToHp028g1YUYLHG6mur0dPdcHOVXLSmUDuZ1HDz1nDuYvIVKjB/MpH9
- aW9XeaQ6ZFIlZVPwuuvI2brR+ThH7Gv27GL/o8qFdC300VQfoTZ+rKPGDE8K1cI906BL4kiw
- x9z0oiDcE96QCz+B0vsjc9mGaA1jgAxlXWsCAwEAAaOCAhcwggITMB0GA1UdDgQWBBSPuCGP
- BuUKtwKn2W3I8M3Ngo9/FzAfBgNVHSMEGDAWgBTfSoAX5mqekXLkYS2M9Mg/I43iozBVBgNV
- HR8ETjBMMEqgSKBGhkRyc3luYzovL2xvY2FsaG9zdDo0NDAwL3Rlc3RiZWQvUklSLzEvMzBx
- QUYtWnFucEZ5NUdFdGpQVElQeU9ONHFNLmNybDBFBggrBgEFBQcBAQQ5MDcwNQYIKwYBBQUH
- MAKGKXJzeW5jOi8vbG9jYWxob3N0OjQ0MDAvdGVzdGJlZC9XT01CQVQuY2VyMBgGA1UdIAEB
- /wQOMAwwCgYIKwYBBQUHDgIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgZsG
- CCsGAQUFBwELBIGOMIGLMDQGCCsGAQUFBzAFhihyc3luYzovL2xvY2FsaG9zdDo0NDAwL3Rl
- c3RiZWQvUklSL1IwLzEvMFMGCCsGAQUFBzAKhkdyc3luYzovL2xvY2FsaG9zdDo0NDAwL3Rl
- c3RiZWQvUklSL1IwLzEvajdnaGp3YmxDcmNDcDlsdHlQRE56WUtQZnhjLm1uZjAaBggrBgEF
- BQcBCAEB/wQLMAmgBzAFAgMA/BUwPgYIKwYBBQUHAQcBAf8ELzAtMCsEAgABMCUDAwAKAzAO
- AwUAwAACAQMFAcAAAiAwDgMFAsAAAiwDBQDAAAJkMA0GCSqGSIb3DQEBCwUAA4IBAQCEhuH7
- jtI2PJY6+zwv306vmCuXhtu9Lr2mmRw2ZErB8EMcb5xypMrNqMoKeu14K2x4a4RPJkK4yATh
- M81FPNRsU5mM0acIRnAPtxjHvPME7PHN2w2nGLASRsZmaa+b8A7SSOxVcFURazENztppsolH
- eTpm0cpLItK7mNpudUg1JGuFo94VLf1MnE2EqARG1vTsNhel/SM/UvOArCCOBvf0Gz7kSuup
- DSZ7qx+LiDmtEsLdbGNQBiYPbLrDk41PHrxdx28qIj7ejZkRzNFw/3pi8/XK281h8zeHoFVu
- 6ghRPy5dbOA4akX/KG6b8XIx0iwPYdLiDbdWFbtTdPcXBauY
- </certificate>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <certificate action="publish" uri="rsync://wombat.invalid/testbed/RIR/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.cer"/>
- </msg>
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="query" version="3">
+ <!-- Zero or more PDUs -->
+ </msg>
+
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="reply" version="3">
+ <!-- Zero or more PDUs -->
+ </msg>
+
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="query" version="3">
+ <publish uri="rsync://wombat.example/Alice/blCrcCp9ltyPDNzYKPfxc.cer">
+ MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEyhE
+ RjRBODAxN0U2NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFMkEzMB4XDTA4
+ MDUyMjE4MDUxMloXDTA4MDUyNDE3NTQ1M1owMzExMC8GA1UEAxMoOEZCODIx
+ OEYwNkU1MEFCNzAyQTdEOTZEQzhGMENEQ0Q4MjhGN0YxNzCCASIwDQYJKoZI
+ hvcNAQEBBQADggEPADCCAQoCggEBAMeziKp0k5nP7v6SZoNsXIMQYRgNtC6F
+ r/9Xm/1yQHomiPqHUk47rHhGojYiK5AhkrwoYhkH4UjJl2iwklDYczXuaBU3
+ F5qrKlZ4aZnjIxdlP7+hktVpeApL6yuJTUAYeC3UIxnLDVdD6phydZ/FOQlu
+ ffiNDjzteCCvoyOUatqt8WB+oND6LToHp028g1YUYLHG6mur0dPdcHOVXLSm
+ UDuZ1HDz1nDuYvIVKjB/MpH9aW9XeaQ6ZFIlZVPwuuvI2brR+ThH7Gv27GL/
+ o8qFdC300VQfoTZ+rKPGDE8K1cI906BL4kiwx9z0oiDcE96QCz+B0vsjc9mG
+ aA1jgAxlXWsCAwEAAaOCAhcwggITMB0GA1UdDgQWBBSPuCGPBuUKtwKn2W3I
+ 8M3Ngo9/FzAfBgNVHSMEGDAWgBTfSoAX5mqekXLkYS2M9Mg/I43iozBVBgNV
+ HR8ETjBMMEqgSKBGhkRyc3luYzovL2xvY2FsaG9zdDo0NDAwL3Rlc3RiZWQv
+ UklSLzEvMzBxQUYtWnFucEZ5NUdFdGpQVElQeU9ONHFNLmNybDBFBggrBgEF
+ BQcBAQQ5MDcwNQYIKwYBBQUHMAKGKXJzeW5jOi8vbG9jYWxob3N0OjQ0MDAv
+ dGVzdGJlZC9XT01CQVQuY2VyMBgGA1UdIAEB/wQOMAwwCgYIKwYBBQUHDgIw
+ DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwgZsGCCsGAQUFBwEL
+ BIGOMIGLMDQGCCsGAQUFBzAFhihyc3luYzovL2xvY2FsaG9zdDo0NDAwL3Rl
+ c3RiZWQvUklSL1IwLzEvMFMGCCsGAQUFBzAKhkdyc3luYzovL2xvY2FsaG9z
+ dDo0NDAwL3Rlc3RiZWQvUklSL1IwLzEvajdnaGp3YmxDcmNDcDlsdHlQRE56
+ WUtQZnhjLm1uZjAaBggrBgEFBQcBCAEB/wQLMAmgBzAFAgMA/BUwPgYIKwYB
+ BQUHAQcBAf8ELzAtMCsEAgABMCUDAwAKAzAOAwUAwAACAQMFAcAAAiAwDgMF
+ AsAAAiwDBQDAAAJkMA0GCSqGSIb3DQEBCwUAA4IBAQCEhuH7jtI2PJY6+zwv
+ 306vmCuXhtu9Lr2mmRw2ZErB8EMcb5xypMrNqMoKeu14K2x4a4RPJkK4yATh
+ M81FPNRsU5mM0acIRnAPtxjHvPME7PHN2w2nGLASRsZmaa+b8A7SSOxVcFUR
+ azENztppsolHeTpm0cpLItK7mNpudUg1JGuFo94VLf1MnE2EqARG1vTsNhel
+ /SM/UvOArCCOBvf0Gz7kSuupDSZ7qx+LiDmtEsLdbGNQBiYPbLrDk41PHrxd
+ x28qIj7ejZkRzNFw/3pi8/XK281h8zeHoFVu6ghRPy5dbOA4akX/KG6b8XIx
+ 0iwPYdLiDbdWFbtTdPcXBauY
+ </publish>
+ </msg>
+
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="reply" version="3">
+ <publish uri="rsync://wombat.example/Alice/blCrcCp9ltyPDNzYKPfxc.cer"/>
+ </msg>
+
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="reply" version="3">
+ <report_error error_code="your_hair_is_on_fire">
+ Shampooing with sterno again, are we?
+ </report_error>
+ </msg>
+
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="reply" version="3">
+ <report_error error_code="your_hair_is_on_fire"/>
+ </msg>
+
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="query" version="3">
+ <withdraw uri="rsync://wombat.example/Alice/blCrcCp9ltyPDNzYKPfxc.cer" hash="deadf00d"/>
+ </msg>
+
+ <msg xmlns="http://www.hactrn.net/uris/rpki/publication-spec/" type="reply" version="3">
+ <withdraw uri="rsync://wombat.example/Alice/blCrcCp9ltyPDNzYKPfxc.cer"/>
+ </msg>
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <certificate action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.cer"/>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <certificate action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.cer"/>
- </msg>
-
- <!-- === -->
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <crl action="publish" uri="rsync://wombat.invalid/testbed/RIR/1/30qAF-ZqnpFy5GEtjPTIPyON4qM.crl">
- MIIBwzCBrAIBATANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEyhERjRBODAxN0U2NkE5RTkx
- NzJFNDYxMkQ4Q0Y0QzgzRjIzOERFMkEzFw0wODA1MjIxODA0MTZaFw0wODA1MjIxODA1MTZa
- MBQwEgIBAhcNMDgwNTIyMTc1ODQwWqAvMC0wHwYDVR0jBBgwFoAU30qAF+ZqnpFy5GEtjPTI
- PyON4qMwCgYDVR0UBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAKkM0Fb/pJpHVHWZyjp4wojH
- W2KkvA/DFtBiz3moxocSnkDVP3QI19uVvqdC6nH3hJyFmsAMwULR0f1XU/V4j+X+FqYEl6Nv
- p8zAEPIB4r8xbEFs7udRwXRAjkJmOQbv9aomF2i+d7jpTFVJxShZWOgsoGEhIy/aktKQrOIR
- c4ZDrXpQwXVj2Y7+cGVfQ4gvnPOdlyLcnNovoegazATvA3EcidBNPWRg7XTCz0LVBEB7JgPd
- nNyXRg35HdMEHBl7U9uUQJXP7S02oaQ1ehNDMfaJPgBBpQtAnM1lIzJfevd9+e4ywGsRpxAV
- 8wxTXSPd1jwuKtS0kwrgsrQ8Ya85xUE=
- </crl>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <crl action="publish" uri="rsync://wombat.invalid/testbed/RIR/1/30qAF-ZqnpFy5GEtjPTIPyON4qM.crl"/>
- </msg>
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <crl action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/1/30qAF-ZqnpFy5GEtjPTIPyON4qM.crl"/>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <crl action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/1/30qAF-ZqnpFy5GEtjPTIPyON4qM.crl"/>
- </msg>
-
- <!-- === -->
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <manifest action="publish" uri="rsync://wombat.invalid/testbed/RIR/R0/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.mft">
- MIIHCgYJKoZIhvcNAQcCoIIG+zCCBvcCAQMxDTALBglghkgBZQMEAgEwggEeBgsqhkiG9w0B
- CRABGqCCAQ0EggEJMIIBBQIBEhgPMjAwODA1MjIxODA1MTVaGA8yMDA4MDUyMjE4MDYxNVoG
- CWCGSAFlAwQCATCB0jBEFh9ZbTVUTzRJYnlDb0pNZ3E2R2o4dG41Mng5U0UuY2VyAyEA4L8Z
- WMyuhOx+o6kUfsRR++QjSaRaATy4UOeVtjvZVqYwRBYfWnRxbjB3NEVFbU9hclAzQmd1SUY3
- MDhhNTM4LmNlcgMhAGQI1gYJotxWmwzcmpLNFZJ656uWOjcPYANlbNz80xm8MEQWH2xxa1Vx
- RHEwMDBESW9ZVjlybXdLTGdrN2F6by5jZXIDIQB7jRAEpkPvc4s4PX9vDvnTifj3BIE145FO
- 1ne2kEejVqCCBBEwggQNMIIC9aADAgECAgEFMA0GCSqGSIb3DQEBCwUAMDMxMTAvBgNVBAMT
- KDhGQjgyMThGMDZFNTBBQjcwMkE3RDk2REM4RjBDRENEODI4RjdGMTcwHhcNMDgwNTIyMTc1
- NzQ5WhcNMDgwNTI0MTc1NDUzWjAzMTEwLwYDVQQDEyhERkRBMjMyMUJENEVCMDNFQTE1RkUy
- N0NGRkRGMEFGRkU1QjBFNjY4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2/Gk
- AHW5pDqye0+TvUp7sl0rVgmTfeHpVp18ypxvuovogVJgkjEtBEikfaFU0646wYD6JM6IJFJX
- lWLWd7bVmhkWViKuZL0VmT2wpUToNHCLUGUQUVVX8R7oSHFdTArv2AqH+6yt0LmczDH1y2M6
- 2Tgkz9wZ9ryyuPx3VX4PkHzUMlkGFICj1fvyXkcAu8jBaxR9UME1c413TPaMi6lMh1HUmtVN
- LJMP5+/SnwEAW/Z3dPClCFIgQXK3nAKPVzAIwADEiqhK7cSchhO7ikI1CVt0XzG4n7oaILc3
- Hq/DAxyiutw5GlkUlKPri2YJzJ3+H4P+TveSa/b02fVA5csm/QIDAQABo4IBKjCCASYwHQYD
- VR0OBBYEFN/aIyG9TrA+oV/ifP/fCv/lsOZoMB8GA1UdIwQYMBaAFI+4IY8G5Qq3AqfZbcjw
- zc2Cj38XMFgGA1UdHwRRME8wTaBLoEmGR3JzeW5jOi8vbG9jYWxob3N0OjQ0MDAvdGVzdGJl
- ZC9SSVIvUjAvMS9qN2doandibENyY0NwOWx0eVBETnpZS1BmeGMuY3JsMGAGCCsGAQUFBwEB
- BFQwUjBQBggrBgEFBQcwAoZEcnN5bmM6Ly9sb2NhbGhvc3Q6NDQwMC90ZXN0YmVkL1JJUi8x
- L2o3Z2hqd2JsQ3JjQ3A5bHR5UEROellLUGZ4Yy5jZXIwGAYDVR0gAQH/BA4wDDAKBggrBgEF
- BQcOAjAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBADpsE9HfgVTgmX1WeJTE
- fm87CXuOoGH85RFiAngSt5kR4gYCyadklOZ7Eta+ERUZVu4tcKO6sJOTuHPfVrAvR0VpgH+j
- PvXboYWSfwJdi00BC28ScrVM2zarA7B10+J6Oq8tbFlAyVBkrbuPet/axmndBtGWhrBTynGl
- nc/5L371Lxy6CrOYqXO0Qx3SrOKaailAe3zTIpHQeACqnPdL00zIBw/hVy/VNaH1wy+FmhAz
- TsmsQUrMyovJcu/ry5w0KHlP8BTnqfykikCWR+Lw0VQHmpJGAbtrmsOeIbfLY1zl7A81lDAl
- AG/ZH1DUdDOUIXMLHWur+D2rwjp7RL16LHYxggGqMIIBpgIBA4AU39ojIb1OsD6hX+J8/98K
- /+Ww5mgwCwYJYIZIAWUDBAIBoGswGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEaMBwGCSqG
- SIb3DQEJBTEPFw0wODA1MjIxODA1MTVaMC8GCSqGSIb3DQEJBDEiBCBj/GjEQw3LgKPf5DTz
- 8eu1fcp6/cQjqqne6ZqFkF42azANBgkqhkiG9w0BAQEFAASCAQBOY0uHNMwy/o1nFANSgha5
- PZxt8fz+wTrbeomCb+lxqQKq1clcSiQORVGc8NmqC8sS5OR3eTw/3qnK9yPHxz2UQ4hn1pBa
- +Zy5veM61qMaXCw6w98EyNcvUfA1AkezAjkabfHQDs3o4Ezh49thXXyRcBoF+O6Lmi+LZbT2
- 4jvfFbaXW9zsb6/DaoDkeHnlk+YYgfSP4wOnkK5uqxtDW8QpMPq3GGdIp0oJDkzEdj7VsWIL
- 9JP2mxxL8fTPVUyAPOmURYwYDXqhke2O9eVDiCYhrEfB8/84Rint4Cj8n5aCujnAtqtwxHpD
- 0NRYO/V1MjhG+ARy1vRH1Dm0r92RBam3
- </manifest>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <manifest action="publish" uri="rsync://wombat.invalid/testbed/RIR/R0/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.mft"/>
- </msg>
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <manifest action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/R0/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.mft"/>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <manifest action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/R0/1/j7ghjwblCrcCp9ltyPDNzYKPfxc.mft"/>
- </msg>
-
- <!-- === -->
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <roa action="publish" uri="rsync://wombat.invalid/testbed/RIR/R0/1/lqkUqDq000DIoYV9rmwKLgk7azo.roa">
- MIIGmwYJKoZIhvcNAQcCoIIGjDCCBogCAQMxDTALBglghkgBZQMEAgEwKgYLKoZIhvcNAQkQ
- ARigGwQZMBcCAgKaMBEwDwQCAAEwCTAHAwUACgMALKCCBJgwggSUMIIDfKADAgECAgEJMA0G
- CSqGSIb3DQEBCwUAMDMxMTAvBgNVBAMTKDhGQjgyMThGMDZFNTBBQjcwMkE3RDk2REM4RjBD
- RENEODI4RjdGMTcwHhcNMDgwNTIyMTc1ODI0WhcNMDgwNTI0MTc1NDUzWjAzMTEwLwYDVQQD
- Eyg5NkE5MTRBODNBQjREMzQwQzhBMTg1N0RBRTZDMEEyRTA5M0I2QjNBMIIBIjANBgkqhkiG
- 9w0BAQEFAAOCAQ8AMIIBCgKCAQEApoK50BjW5bcF4gsdaYhndtVADZvQk3RCsvuqDElF6uLi
- 9BYQq/NHyDOIMyJtvCmzjdv3Y135n1sNO7YvssqHlt7dMfCQTD5ND1GpFnQLdWP7stWM5AbO
- nJV6+PtDITUA/QHOli7Do0YCUgR6G+1QJsMu0DK+TRSzBJ6WP7WIYOBOOg3y/NKc1rkWhS1Q
- dcQepbHgQYZHzzpjNDR6+oYVuhuUEWx1P6O4pv/p+tpE0SDua7jBjMywIYHkPQBecf2IX1RU
- WNojB9dJlnRx5YUUneP2SvF2MrmdDbclgzwhf6alqD2OjiMuoBOG8yeTKcuhzCMnrFAklbst
- 6x3Rnq9BswIDAQABo4IBsTCCAa0wHQYDVR0OBBYEFJapFKg6tNNAyKGFfa5sCi4JO2s6MB8G
- A1UdIwQYMBaAFI+4IY8G5Qq3AqfZbcjwzc2Cj38XMFgGA1UdHwRRME8wTaBLoEmGR3JzeW5j
- Oi8vbG9jYWxob3N0OjQ0MDAvdGVzdGJlZC9SSVIvUjAvMS9qN2doandibENyY0NwOWx0eVBE
- TnpZS1BmeGMuY3JsMGAGCCsGAQUFBwEBBFQwUjBQBggrBgEFBQcwAoZEcnN5bmM6Ly9sb2Nh
- bGhvc3Q6NDQwMC90ZXN0YmVkL1JJUi8xL2o3Z2hqd2JsQ3JjQ3A5bHR5UEROellLUGZ4Yy5j
- ZXIwGAYDVR0gAQH/BA4wDDAKBggrBgEFBQcOAjAOBgNVHQ8BAf8EBAMCB4AwYwYIKwYBBQUH
- AQsEVzBVMFMGCCsGAQUFBzALhkdyc3luYzovL2xvY2FsaG9zdDo0NDAwL3Rlc3RiZWQvUklS
- L1IwLzEvbHFrVXFEcTAwMERJb1lWOXJtd0tMZ2s3YXpvLnJvYTAgBggrBgEFBQcBBwEB/wQR
- MA8wDQQCAAEwBwMFAAoDACwwDQYJKoZIhvcNAQELBQADggEBAL8iHwsyGOYhhIf3nVuL361y
- TOJSP8SR0mtQLHULPl+GkYk+5MRNWtL8ucTXFvniYJtOCXEGGEIO9eDXvkQIXQSz/qbF9URQ
- fuf38ghRza257syVhal6UHTgCFYuRIO9CUjcU1vkWUxH05BBIHlYdtlIQbAG/mRsCPCEgSmG
- bbQaomGlUOqmJMlKxLLcoAtz2vDrwVotgHyfS5h2mgINFjnlLcNLTci+sfs7/aQAkDYx7K98
- se/ZlMorvGkFNhHoOTcGIrWkYsfkbTygVwWRm278PaB3o4449Kvsg/gb8BZeHXRs68cr5Mcf
- jP7Q6jeypjTgDBnwb1yzoJIKWszFuSgxggGqMIIBpgIBA4AUlqkUqDq000DIoYV9rmwKLgk7
- azowCwYJYIZIAWUDBAIBoGswGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEYMBwGCSqGSIb3
- DQEJBTEPFw0wODA1MjIxNzU4MjRaMC8GCSqGSIb3DQEJBDEiBCDCyf9v9Wed515TRp2WwnyM
- 1rk6dB///X+aqIym2e9jdTANBgkqhkiG9w0BAQEFAASCAQAFvzrHeRPW+wn4WSyoyBEq0zKS
- Cyh5tu1qTR0NHs6Rr/p8Pk81P1HQLND/U+znJZKLWlO2niEHUXPIicPDYchbj8ApH9VxKA+1
- lCWllOzFAsYyZFr3/VNs9pVp2eT4F9eEYBrBVDSNrD72MMTlWm1T5MEXqltTJJOCKzUEX96x
- 91iW6A+4erop7S8hpCnxqkTin4bFVreqYcGc4CC4bh+L9pPqJnURcEk7Qeu/WEHQBm38voB4
- S11qRZNrJMQ99oiJR7hXDIBm66HjGqoUL2gPCfpgJEVVnM9pVv2k889z4eTTck2Qj54gga2W
- Xkvw4Je420aDx88s9T2+PqXcbZ4g
- </roa>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <roa action="publish" uri="rsync://wombat.invalid/testbed/RIR/R0/1/lqkUqDq000DIoYV9rmwKLgk7azo.roa"/>
- </msg>
-
- <msg version="1" type="query" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <roa action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/R0/1/lqkUqDq000DIoYV9rmwKLgk7azo.roa"/>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <roa action="withdraw" uri="rsync://wombat.invalid/testbed/RIR/R0/1/lqkUqDq000DIoYV9rmwKLgk7azo.roa"/>
- </msg>
-
- <!-- === -->
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <report_error error_code="your_hair_is_on_fire">text string</report_error>
- </msg>
-
- <msg version="1" type="reply" xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
- <report_error error_code="your_hair_is_on_fire"/>
- </msg>
</completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
diff --git a/ca/tests/rrdp-samples.xml b/ca/tests/rrdp-samples.xml
new file mode 100644
index 00000000..966d9887
--- /dev/null
+++ b/ca/tests/rrdp-samples.xml
@@ -0,0 +1,81 @@
+<!-- -*- SGML -*-
+ - $Id$
+ -
+ - This is a collection of sample RRDP PDU samples to use as test
+ - cases for the RRDP RelaxNG schema.
+ -
+ - Need to figure out whose copyright should be on these examples.
+ - BSD in any case so makes little practical difference, just need to
+ - be sure we give proper credit. Might be RIPE, might be IETF
+ - Trust, might be us for derivative work. Slap ours on for the
+ - moment, fix when we figure this out.
+ -
+ - Copyright (C) 2014 Dragon Research Labs ("DRL")
+ -
+ - 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 DRL DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL DRL 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.
+ -->
+
+<completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
+
+ <!-- Notification file: lists current snapshots and deltas -->
+
+ <notification xmlns="http://www.ripe.net/rpki/rrdp" version="1" session_id="d9f6dc91-0394-40b9-9663-66aef4bb623a" serial="185">
+ <snapshot uri="http://host.example/d9f6dc91-0394-40b9-9663-66aeb623a/snapshot/202.xml" hash="279b79fd8389e20585f26735ee70e0e4d4b8af23bb2e2e611c70e92d2433edea"/>
+ <delta serial="183" uri="http://host.example/d9f6c91-0394-40b9-9663-66aeb623a/deltas/183.xml" hash="a2d56ec180f2dde2a46bf90565932e25829b852a0b43107d5de6e41394c29100"/>
+ <delta serial="184" uri="http://host.example/d9f6c91-0394-40b9-9663-66aeb623a/deltas/184.xml" hash="a2d56ec180f2dde2a46b2e0565932e25829b852a0b43107d5de6e41394c29200"/>
+ <delta serial="185" uri="http://host.example/d9f6c91-0394-40b9-9663-66aeb623a/deltas/185.xml" hash="a2d56ec180f2dde2a46b2e0565932e25829b852a0b43107d5de6e41394c29201"/>
+ </notification>
+
+ <!-- Snapshot segment: think DNS AXFR -->
+
+ <snapshot version="1" xmlns="http://www.ripe.net/rpki/rrdp" session_id="d9f6dc91-0394-40b9-9663-66aef4bb623a" serial="1">
+ <publish uri="http://host.example/foo/bar/cer1.cer">
+ MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQD
+ jRBODAxN0U2NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFMkEzMB4XE
+ h8zeHoFVu6ghRPy5dbOA4akX/KG6b8XIx0iwPYdLiDbdWFbtTdPcXBau
+ </publish>
+ <publish uri="http://host.example/foo/bar/cer2.cer">
+ MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQD
+ h8zeHoFVu6ghRPy5dbOA4akX/KG6b8XIx0iwPYdLiDbdWFbtTdPcXBau
+ jRBODAxN0U2NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFMkEzMB4XD
+ </publish>
+ <publish uri="http://host.example/foo/bar/cer3.cer">
+ MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEwLwYDVQQD
+ h8zeHoFVu6ghRPy5dbOA4akX/KG6b8XIx0iwPYdLiDbdWFbtTdPcXBau
+ jRBODAxN0U2NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFMkEzMB4XD
+ </publish>
+ </snapshot>
+
+ <!-- Delta segment: think DNS IXFR -->
+
+ <delta version="1" xmlns="http://www.ripe.net/rpki/rrdp" session_id="d9f6dc91-0394-40b9-9663-66aef4bb623a" serial="3">
+ <publish uri="http://host.example/foo/bar/cer1.cer">
+ MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEw
+ jRBODAxN0U2NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFM
+ h8zeHoFVu6ghRPy5dbOA4akX/KG6b8XIx0iwPYdLiDbdWFbt
+ </publish>
+ <withdraw uri="http://host.example/foo/bar/cer1.cer" hash="deadf00d"/>
+ <publish uri="http://host.example/foo/bar/cer2.cer">
+ MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEw
+ h8zeHoFVu6ghRPy5dbOA4akX/KG6b8XIx0iwPYdLiDbdWFbt
+ jRBODAxN0U2NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFM
+ </publish>
+ <publish uri="http://host.example/foo/bar/cer3.cer" hash="deadf00d">
+ MIIE+jCCA+KgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAzMTEw
+ h8zeHoFVu6ghRPy5dbOA4akX/KG6b8XIx0iwPYdLiDbdWFbt
+ jRBODAxN0U2NkE5RTkxNzJFNDYxMkQ4Q0Y0QzgzRjIzOERFM
+ </publish>
+ <withdraw uri="http://host.example/foo/bar/cer4.cer" hash="deadf00d"/>
+ </delta>
+
+</completely_gratuitous_wrapper_element_to_let_me_run_this_through_xmllint>
diff --git a/ca/tests/smoketest.6.yaml b/ca/tests/smoketest.6.yaml
index e8d65433..e33e75c2 100644
--- a/ca/tests/smoketest.6.yaml
+++ b/ca/tests/smoketest.6.yaml
@@ -54,6 +54,9 @@ kids:
roa_request:
- asn: 64533
ipv6: 2001:db8::80/121
+ router_cert:
+ - router_id: 666
+ asn: 64533
ghostbusters:
- |
BEGIN:VCARD
diff --git a/ca/tests/smoketest.py b/ca/tests/smoketest.py
index 32f11cc3..9d82c640 100644
--- a/ca/tests/smoketest.py
+++ b/ca/tests/smoketest.py
@@ -47,8 +47,7 @@ import rpki.http
import rpki.log
import rpki.left_right
import rpki.config
-import rpki.publication
-import rpki.async
+import rpki.publication_control
from rpki.mysql_import import MySQLdb
@@ -68,7 +67,7 @@ parser.add_argument("yaml_file", type = argparse.FileType("r"),
help = "YAML description of test network")
args = parser.parse_args()
-cfg = rpki.config.parser(args.config, "smoketest", allow_missing = True)
+cfg = rpki.config.parser(set_filename = args.config, section = "smoketest", allow_missing = True)
# Load the YAML script early, so we can report errors ASAP
@@ -77,13 +76,14 @@ yaml_script = [y for y in yaml.safe_load_all(args.yaml_file)]
# Define port allocator early, so we can use it while reading config
def allocate_port():
- """
- Allocate a TCP port number.
- """
- global base_port
- p = base_port
- base_port += 1
- return p
+ """
+ Allocate a TCP port number.
+ """
+
+ global base_port
+ p = base_port
+ base_port += 1
+ return p
# Most filenames in the following are relative to the working directory.
@@ -139,14 +139,14 @@ pubd_last_cms_time = None
ecdsa_params = None
class CantRekeyYAMLLeaf(Exception):
- """
- Can't rekey YAML leaf.
- """
+ """
+ Can't rekey YAML leaf.
+ """
class CouldntIssueBSCEECertificate(Exception):
- """
- Couldn't issue BSC EE certificate
- """
+ """
+ Couldn't issue BSC EE certificate
+ """
sql_conversions = MySQLdb.converters.conversions.copy()
sql_conversions.update({
@@ -154,195 +154,202 @@ sql_conversions.update({
MySQLdb.converters.FIELD_TYPE.DATETIME : rpki.sundial.datetime.DateTime_or_None })
def main():
- """
- Main program.
- """
-
- rpki.log.init(smoketest_name, argparse.Namespace(log_level = logging.DEBUG,
- log_handler = lambda: logging.StreamHandler(sys.stdout)))
- logger.info("Starting")
-
- rpki.http.http_client.timeout = rpki.sundial.timedelta(hours = 1)
-
- pubd_process = None
- rootd_process = None
- rsyncd_process = None
-
- rpki_sql = mangle_sql(rpki_sql_file)
- irdb_sql = mangle_sql(irdb_sql_file)
- pubd_sql = mangle_sql(pub_sql_file)
-
- logger.info("Initializing test directory")
-
- # Connect to test directory, creating it if necessary
- try:
- os.chdir(smoketest_dir)
- except OSError:
- os.makedirs(smoketest_dir)
- os.chdir(smoketest_dir)
-
- # Now that we're in the right directory, we can figure out whether
- # we have a private openssl executable to use
- global prog_openssl
- if not os.path.exists(prog_openssl):
- prog_openssl = "openssl"
-
- # Discard everything but keys, which take a while to generate.
- # Apparently os.walk() can't tell the difference between directories
- # and symlinks to directories, so we have to handle both.
- for root, dirs, files in os.walk(".", topdown = False):
- for fn in files:
- if not fn.endswith(".key"):
- os.remove(os.path.join(root, fn))
- for d in dirs:
- try:
- os.rmdir(os.path.join(root, d))
- except OSError, e:
- if e.errno == errno.ENOTDIR:
- os.remove(os.path.join(root, d))
- else:
- raise
-
- logger.info("Reading master YAML configuration")
- y = yaml_script.pop(0)
-
- logger.info("Constructing internal allocation database")
- db = allocation_db(y)
+ """
+ Main program.
+ """
- logger.info("Constructing BPKI keys and certs for rootd")
- setup_bpki_cert_chain(rootd_name, ee = ("RPKI",))
+ rpki.log.init(smoketest_name, argparse.Namespace(log_level = logging.DEBUG,
+ log_handler = lambda: logging.StreamHandler(sys.stdout)))
+ logger.info("Starting")
- logger.info("Constructing BPKI keys and certs for pubd")
- setup_bpki_cert_chain(pubd_name, ee = ("PUBD", "IRBE"))
+ rpki.http.http_client.timeout = rpki.sundial.timedelta(hours = 1)
+ pubd_process = None
+ rootd_process = None
+ rsyncd_process = None
- for a in db:
- a.setup_bpki_certs()
+ rpki_sql = mangle_sql(rpki_sql_file)
+ irdb_sql = mangle_sql(irdb_sql_file)
+ pubd_sql = mangle_sql(pub_sql_file)
- setup_publication(pubd_sql)
- setup_rootd(db.root, y.get("rootd", {}))
- setup_rsyncd()
- setup_rcynic()
+ logger.info("Initializing test directory")
- for a in db.engines:
- a.setup_conf_file()
- a.setup_sql(rpki_sql, irdb_sql)
- a.sync_sql()
+ # Connect to test directory, creating it if necessary
+ try:
+ os.chdir(smoketest_dir)
+ except OSError:
+ os.makedirs(smoketest_dir)
+ os.chdir(smoketest_dir)
+
+ # Now that we're in the right directory, we can figure out whether
+ # we have a private openssl executable to use
+ global prog_openssl
+ if not os.path.exists(prog_openssl):
+ prog_openssl = "openssl"
+
+ # Discard everything but keys, which take a while to generate.
+ # Apparently os.walk() can't tell the difference between directories
+ # and symlinks to directories, so we have to handle both.
+ for root, dirs, files in os.walk(".", topdown = False):
+ for fn in files:
+ if not fn.endswith(".key"):
+ os.remove(os.path.join(root, fn))
+ for d in dirs:
+ try:
+ os.rmdir(os.path.join(root, d))
+ except OSError, e:
+ if e.errno == errno.ENOTDIR:
+ os.remove(os.path.join(root, d))
+ else:
+ raise
+
+ logger.info("Reading master YAML configuration")
+ y = yaml_script.pop(0)
+
+ logger.info("Constructing internal allocation database")
+ db = allocation_db(y)
+
+ logger.info("Constructing BPKI keys and certs for rootd")
+ setup_bpki_cert_chain(rootd_name, ee = ("RPKI",))
+
+ logger.info("Constructing BPKI keys and certs for pubd")
+ setup_bpki_cert_chain(pubd_name, ee = ("PUBD", "IRBE"))
+
+
+ for a in db:
+ a.setup_bpki_certs()
+
+ setup_publication(pubd_sql, db.root.irdb_db_name)
+ setup_rootd(db.root, y.get("rootd", {}), db)
+ setup_rsyncd()
+ setup_rcynic()
- try:
+ for a in db.engines:
+ a.setup_conf_file()
+ a.setup_sql(rpki_sql, irdb_sql)
+ a.sync_sql()
- logger.info("Starting rootd")
- rootd_process = subprocess.Popen((prog_python, prog_rootd, "--foreground", "--log-stdout", "--log-level", "debug", "--config", rootd_name + ".conf"))
+ try:
- logger.info("Starting pubd")
- pubd_process = subprocess.Popen((prog_python, prog_pubd, "--foreground", "--log-stdout", "--log-level", "debug", "--config", pubd_name + ".conf") +
- (("-p", pubd_name + ".prof") if args.profile else ()))
+ logger.info("Starting rootd")
+ rootd_process = subprocess.Popen((prog_python, prog_rootd, "--foreground", "--log-stdout", "--log-level", "debug"),
+ env = dict(os.environ, RPKI_CONF = rootd_name + ".conf"))
- logger.info("Starting rsyncd")
- rsyncd_process = subprocess.Popen((prog_rsyncd, "--daemon", "--no-detach", "--config", rsyncd_name + ".conf"))
+ logger.info("Starting pubd")
+ pubd_process = subprocess.Popen((prog_python, prog_pubd, "--foreground", "--log-stdout", "--log-level", "debug") +
+ (("-p", pubd_name + ".prof") if args.profile else ()),
+ env = dict(os.environ, RPKI_CONF = pubd_name + ".conf"))
- # Start rpkid and irdbd instances
- for a in db.engines:
- a.run_daemons()
+ logger.info("Starting rsyncd")
+ rsyncd_process = subprocess.Popen((prog_rsyncd, "--daemon", "--no-detach", "--config", rsyncd_name + ".conf"))
- # From this point on we'll be running event-driven, so the rest of
- # the code until final exit is all closures.
+ # Start rpkid and irdbd instances
+ for a in db.engines:
+ a.run_daemons()
- def start():
- rpki.async.iterator(db.engines, create_rpki_objects, created_rpki_objects)
+ # From this point on we'll be running event-driven, so the rest of
+ # the code until final exit is all closures.
- def create_rpki_objects(iterator, a):
- a.create_rpki_objects(iterator)
+ def start():
+ rpki.async.iterator(db.engines, create_rpki_objects, create_pubd_objects)
- def created_rpki_objects():
+ def create_rpki_objects(iterator, a):
+ a.create_rpki_objects(iterator)
- # Set pubd's BPKI CRL
- set_pubd_crl(yaml_loop)
+ def create_pubd_objects():
+ call_pubd([rpki.publication_control.client_elt.make_pdu(action = "create",
+ client_handle = db.root.client_handle + "-" + rootd_name,
+ base_uri = rootd_sia,
+ bpki_cert = cross_certify(rootd_name + "-TA", pubd_name + "-TA"))],
+ cb = lambda ignored: yaml_loop())
- def yaml_loop():
+ def yaml_loop():
- # This is probably where we should be updating expired BPKI
- # objects, particular CRLs
+ # This is probably where we should be updating expired BPKI
+ # objects, particular CRLs
- logger.info("Running cron for all RPKI engines")
- rpki.async.iterator(db.engines, run_cron, run_yaml)
+ logger.info("Running cron for all RPKI engines")
+ rpki.async.iterator(db.engines, run_cron, run_yaml)
- def run_cron(iterator, a):
- a.run_cron(iterator)
+ def run_cron(iterator, a):
+ a.run_cron(iterator)
- def run_yaml():
+ def run_yaml():
- # Run rcynic to check results
- run_rcynic()
+ # Run rcynic to check results
+ run_rcynic()
- # Apply next delta if we have one; otherwise, we're done.
- if yaml_script:
- logger.info("Applying deltas")
- db.apply_delta(yaml_script.pop(0), apply_delta_done)
- else:
- logger.info("No more deltas to apply, done")
- rpki.async.exit_event_loop()
+ # Apply next delta if we have one; otherwise, we're done.
+ if yaml_script:
+ logger.info("Applying deltas")
+ db.apply_delta(yaml_script.pop(0), apply_delta_done)
+ else:
+ logger.info("No more deltas to apply, done")
+ rpki.async.exit_event_loop()
- def apply_delta_done():
+ def apply_delta_done():
- # Resync IRDBs
- for a in db.engines:
- a.sync_sql()
+ # Resync IRDBs
+ for a in db.engines:
+ a.sync_sql()
- # Loop until we run out of control YAML
- yaml_loop()
+ # Loop until we run out of control YAML
+ yaml_loop()
- logger.info("Sleeping %d seconds while daemons start up", startup_delay)
- rpki.async.timer(start).set(rpki.sundial.timedelta(seconds = startup_delay))
- rpki.async.event_loop()
+ logger.info("Sleeping %d seconds while daemons start up", startup_delay)
+ rpki.async.timer(start).set(rpki.sundial.timedelta(seconds = startup_delay))
+ rpki.async.event_loop()
- # At this point we have gone into event-driven code.
+ # At this point we have gone into event-driven code.
- logger.info("Event loop exited normally")
+ logger.info("Event loop exited normally")
- except Exception, e:
- logger.exception("Event loop exited with an exception")
+ except Exception, e:
+ logger.exception("Event loop exited with an exception")
- finally:
- logger.info("Cleaning up")
- for a in db.engines:
- a.kill_daemons()
- for proc, name in ((rootd_process, "rootd"),
- (pubd_process, "pubd"),
- (rsyncd_process, "rsyncd")):
- # pylint: disable=E1103
- if proc is not None and proc.poll() is None:
- logger.info("Killing %s, pid %s", name, proc.pid)
- try:
- proc.terminate()
- except OSError:
- pass
- if proc is not None:
- logger.info("Daemon %s, pid %s exited with code %s", name, proc.pid, proc.wait())
+ finally:
+ logger.info("Cleaning up")
+ for a in db.engines:
+ a.kill_daemons()
+ for proc, name in ((rootd_process, "rootd"),
+ (pubd_process, "pubd"),
+ (rsyncd_process, "rsyncd")):
+ # pylint: disable=E1103
+ if proc is not None and proc.poll() is None:
+ logger.info("Killing %s, pid %s", name, proc.pid)
+ try:
+ proc.terminate()
+ except OSError:
+ pass
+ if proc is not None:
+ logger.info("Daemon %s, pid %s exited with code %s", name, proc.pid, proc.wait())
def cmd_sleep(cb, interval):
- """
- Set an alarm, then wait for it to go off.
- """
- howlong = rpki.sundial.timedelta.parse(interval)
- logger.info("Sleeping %r", howlong)
- rpki.async.timer(cb).set(howlong)
+ """
+ Set an alarm, then wait for it to go off.
+ """
+
+ howlong = rpki.sundial.timedelta.parse(interval)
+ logger.info("Sleeping %r", howlong)
+ rpki.async.timer(cb).set(howlong)
def cmd_shell(cb, *cmd):
- """
- Run a shell command.
- """
- cmd = " ".join(cmd)
- status = subprocess.call(cmd, shell = True)
- logger.info("Shell command returned status %d", status)
- cb()
+ """
+ Run a shell command.
+ """
+
+ cmd = " ".join(cmd)
+ status = subprocess.call(cmd, shell = True)
+ logger.info("Shell command returned status %d", status)
+ cb()
def cmd_echo(cb, *words):
- """
- Echo some text to the log.
- """
- logger.info(" ".join(words))
- cb()
+ """
+ Echo some text to the log.
+ """
+
+ logger.info(" ".join(words))
+ cb()
## @var cmds
# Dispatch table for commands embedded in delta sections
@@ -352,579 +359,954 @@ cmds = { "sleep" : cmd_sleep,
"echo" : cmd_echo }
class roa_request(object):
- """
- Representation for a roa_request object.
- """
-
- def __init__(self, asn, ipv4, ipv6):
- self.asn = asn
- self.v4 = rpki.resource_set.roa_prefix_set_ipv4("".join(ipv4.split())) if ipv4 else None
- self.v6 = rpki.resource_set.roa_prefix_set_ipv6("".join(ipv6.split())) if ipv6 else None
-
- def __eq__(self, other):
- return self.asn == other.asn and self.v4 == other.v4 and self.v6 == other.v6
-
- def __hash__(self):
- v4 = tuple(self.v4) if self.v4 is not None else None
- v6 = tuple(self.v6) if self.v6 is not None else None
- return self.asn.__hash__() + v4.__hash__() + v6.__hash__()
-
- def __str__(self):
- if self.v4 and self.v6: s = str(self.v4) + "," + str(self.v6)
- elif self.v4: s = str(self.v4)
- else: s = str(self.v6)
- return "%s: %s" % (self.asn, s)
-
- @classmethod
- def parse(cls, yaml):
- return cls(yaml.get("asn"), yaml.get("ipv4"), yaml.get("ipv6"))
-
-class router_cert(object):
- """
- Representation for a router_cert object.
- """
-
- _ecparams = None
- _keypair = None
- _pkcs10 = None
- _gski = None
-
- @classmethod
- def ecparams(cls):
- if cls._ecparams is None:
- cls._ecparams = rpki.x509.KeyParams.generateEC()
- return cls._ecparams
-
- def __init__(self, asn, router_id):
- self.asn = rpki.resource_set.resource_set_as("".join(str(asn).split()))
- self.router_id = router_id
- self.cn = "ROUTER-%08x" % self.asn[0].min
- self.sn = "%08x" % self.router_id
- self.eku = rpki.oids.id_kp_bgpsec_router
-
- @property
- def keypair(self):
- if self._keypair is None:
- self._keypair = rpki.x509.ECDSA.generate(self.ecparams())
- return self._keypair
-
- @property
- def pkcs10(self):
- if self._pkcs10 is None:
- self._pkcs10 = rpki.x509.PKCS10.create(keypair = self.keypair)
- return self._pkcs10
-
- @property
- def gski(self):
- if self._gski is None:
- self._gski = self.pkcs10.gSKI()
- return self._gski
-
- def __eq__(self, other):
- return self.asn == other.asn and self.sn == other.sn
-
- def __hash__(self):
- return tuple(self.asn).__hash__() + self.cn.__hash__() + self.sn.__hash__()
-
- def __str__(self):
- return "%s: %s,%s: %s" % (self.asn, self.cn, self.sn, self.gski)
-
- @classmethod
- def parse(cls, yaml):
- return cls(yaml.get("asn"), yaml.get("router_id"))
-
-class allocation_db(list):
- """
- Representation of all the entities and allocations in the test
- system. Almost everything is generated out of this database.
- """
-
- def __init__(self, yaml):
"""
- Initialize database from the (first) YAML document.
+ Representation for a roa_request object.
"""
- list.__init__(self)
- self.root = allocation(yaml, self)
- assert self.root.is_root
- if self.root.crl_interval is None:
- self.root.crl_interval = rpki.sundial.timedelta.parse(cfg.get("crl_interval", "1d")).convert_to_seconds()
- if self.root.regen_margin is None:
- self.root.regen_margin = rpki.sundial.timedelta.parse(cfg.get("regen_margin", "1d")).convert_to_seconds()
- for a in self:
- if a.sia_base is None:
- a.sia_base = (rootd_sia + "root/trunk/" if a.is_root else a.parent.sia_base) + a.name + "/"
- if a.base.valid_until is None:
- a.base.valid_until = a.parent.base.valid_until
- if a.crl_interval is None:
- a.crl_interval = a.parent.crl_interval
- if a.regen_margin is None:
- a.regen_margin = a.parent.regen_margin
- a.client_handle = "/".join(a.sia_base.split("/")[4:]).rstrip("/")
- self.root.closure()
- self.map = dict((a.name, a) for a in self)
- self.engines = [a for a in self if a.is_engine]
- for i, a in enumerate(self.engines):
- a.set_engine_number(i)
- for a in self:
- if a.is_hosted:
- a.hosted_by = self.map[a.hosted_by]
- a.hosted_by.hosts.append(a)
- assert a.is_twig, "%s is not twig" % a.name
- assert not a.hosted_by.is_hosted, "%s is hosted by a hosted entity" % a.name
-
- def apply_delta(self, delta, cb):
- """
- Apply a delta or run a command.
- """
-
- def loop(iterator, d):
- if isinstance(d, str):
- c = d.split()
- cmds[c[0]](iterator, *c[1:])
- else:
- self.map[d["name"]].apply_delta(d, iterator)
+ def __init__(self, asn, ipv4, ipv6):
+ self.asn = asn
+ self.v4 = rpki.resource_set.roa_prefix_set_ipv4("".join(ipv4.split())) if ipv4 else None
+ self.v6 = rpki.resource_set.roa_prefix_set_ipv6("".join(ipv6.split())) if ipv6 else None
- def done():
- self.root.closure()
- cb()
+ def __eq__(self, other):
+ return self.asn == other.asn and self.v4 == other.v4 and self.v6 == other.v6
- if delta is None:
- cb()
- else:
- rpki.async.iterator(delta, loop, done)
+ def __hash__(self):
+ v4 = tuple(self.v4) if self.v4 is not None else None
+ v6 = tuple(self.v6) if self.v6 is not None else None
+ return self.asn.__hash__() + v4.__hash__() + v6.__hash__()
- def dump(self):
- """
- Print content of the database.
- """
- for a in self:
- print a
+ def __str__(self):
+ if self.v4 and self.v6: s = str(self.v4) + "," + str(self.v6)
+ elif self.v4: s = str(self.v4)
+ else: s = str(self.v6)
+ return "%s: %s" % (self.asn, s)
-class allocation(object):
+ @classmethod
+ def parse(cls, yaml):
+ return cls(yaml.get("asn"), yaml.get("ipv4"), yaml.get("ipv6"))
- parent = None
- irdb_db_name = None
- irdb_port = None
- rpki_db_name = None
- rpki_port = None
- crl_interval = None
- regen_margin = None
- last_cms_time = None
- rpkid_process = None
- irdbd_process = None
-
- def __init__(self, yaml, db, parent = None):
- """
- Initialize one entity and insert it into the database.
- """
- db.append(self)
- self.name = yaml["name"]
- self.parent = parent
- self.kids = [allocation(k, db, self) for k in yaml.get("kids", ())]
- valid_until = None
- if "valid_until" in yaml:
- valid_until = rpki.sundial.datetime.from_datetime(yaml.get("valid_until"))
- if valid_until is None and "valid_for" in yaml:
- valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"])
- self.base = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(yaml.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(yaml.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(yaml.get("ipv6")),
- valid_until = valid_until)
- self.sia_base = yaml.get("sia_base")
- if "crl_interval" in yaml:
- self.crl_interval = rpki.sundial.timedelta.parse(yaml["crl_interval"]).convert_to_seconds()
- if "regen_margin" in yaml:
- self.regen_margin = rpki.sundial.timedelta.parse(yaml["regen_margin"]).convert_to_seconds()
- self.roa_requests = [roa_request.parse(y) for y in yaml.get("roa_request", yaml.get("route_origin", ()))]
- for r in self.roa_requests:
- if r.v4:
- self.base.v4 |= r.v4.to_resource_set()
- if r.v6:
- self.base.v6 |= r.v6.to_resource_set()
- self.router_certs = [router_cert.parse(y) for y in yaml.get("router_cert", ())]
- for r in self.router_certs:
- self.base.asn |= r.asn
- self.hosted_by = yaml.get("hosted_by")
- self.extra_conf = yaml.get("extra_conf", [])
- self.hosts = []
-
- def closure(self):
+class router_cert(object):
"""
- Compute the transitive resource closure.
+ Representation for a router_cert object.
"""
- resources = self.base
- for kid in self.kids:
- resources |= kid.closure()
- self.resources = resources
- return resources
- def apply_delta(self, yaml, cb):
+ _ecparams = None
+ _keypair = None
+ _pkcs10 = None
+ _gski = None
+
+ @classmethod
+ def ecparams(cls):
+ if cls._ecparams is None:
+ cls._ecparams = rpki.x509.KeyParams.generateEC()
+ return cls._ecparams
+
+ def __init__(self, asn, router_id):
+ self.asn = rpki.resource_set.resource_set_as("".join(str(asn).split()))
+ self.router_id = router_id
+ self.cn = "ROUTER-%08x" % self.asn[0].min
+ self.sn = "%08x" % self.router_id
+ self.eku = rpki.oids.id_kp_bgpsec_router
+
+ @property
+ def keypair(self):
+ if self._keypair is None:
+ self._keypair = rpki.x509.ECDSA.generate(self.ecparams())
+ return self._keypair
+
+ @property
+ def pkcs10(self):
+ if self._pkcs10 is None:
+ self._pkcs10 = rpki.x509.PKCS10.create(keypair = self.keypair)
+ return self._pkcs10
+
+ @property
+ def gski(self):
+ if self._gski is None:
+ self._gski = self.pkcs10.gSKI()
+ return self._gski
+
+ def __eq__(self, other):
+ return self.asn == other.asn and self.sn == other.sn
+
+ def __hash__(self):
+ return tuple(self.asn).__hash__() + self.cn.__hash__() + self.sn.__hash__()
+
+ def __str__(self):
+ return "%s: %s,%s: %s" % (self.asn, self.cn, self.sn, self.gski)
+
+ @classmethod
+ def parse(cls, yaml):
+ return cls(yaml.get("asn"), yaml.get("router_id"))
+
+class allocation_db(list):
"""
- Apply deltas to this entity.
+ Representation of all the entities and allocations in the test
+ system. Almost everything is generated out of this database.
"""
- logger.info("Applying delta: %s", yaml)
-
- def loop(iterator, kv):
- if kv[0] == "name":
- iterator()
- else:
- getattr(self, "apply_" + kv[0])(kv[1], iterator)
-
- rpki.async.iterator(yaml.items(), loop, cb)
-
- def apply_add_as(self, text, cb):
- self.base.asn |= rpki.resource_set.resource_set_as(text)
- cb()
-
- def apply_add_v4(self, text, cb):
- self.base.v4 |= rpki.resource_set.resource_set_ipv4(text)
- cb()
+ def __init__(self, yaml):
+ """
+ Initialize database from the (first) YAML document.
+ """
+
+ list.__init__(self)
+ self.root = allocation(yaml, self)
+ assert self.root.is_root
+ if self.root.crl_interval is None:
+ self.root.crl_interval = rpki.sundial.timedelta.parse(cfg.get("crl_interval", "1d")).convert_to_seconds()
+ if self.root.regen_margin is None:
+ self.root.regen_margin = rpki.sundial.timedelta.parse(cfg.get("regen_margin", "1d")).convert_to_seconds()
+ for a in self:
+ if a.sia_base is None:
+ a.sia_base = (rootd_sia + "root/trunk/" if a.is_root else a.parent.sia_base) + a.name + "/"
+ if a.base.valid_until is None:
+ a.base.valid_until = a.parent.base.valid_until
+ if a.crl_interval is None:
+ a.crl_interval = a.parent.crl_interval
+ if a.regen_margin is None:
+ a.regen_margin = a.parent.regen_margin
+ a.client_handle = "/".join(a.sia_base.split("/")[4:]).rstrip("/")
+ self.root.closure()
+ self.map = dict((a.name, a) for a in self)
+ self.engines = [a for a in self if a.is_engine]
+ for i, a in enumerate(self.engines):
+ a.set_engine_number(i)
+ for a in self:
+ if a.is_hosted:
+ a.hosted_by = self.map[a.hosted_by]
+ a.hosted_by.hosts.append(a)
+ assert a.is_twig, "%s is not twig" % a.name
+ assert not a.hosted_by.is_hosted, "%s is hosted by a hosted entity" % a.name
+
+ def apply_delta(self, delta, cb):
+ """
+ Apply a delta or run a command.
+ """
+
+ def loop(iterator, d):
+ if isinstance(d, str):
+ c = d.split()
+ cmds[c[0]](iterator, *c[1:])
+ else:
+ self.map[d["name"]].apply_delta(d, iterator)
+
+ def done():
+ self.root.closure()
+ cb()
+
+ if delta is None:
+ cb()
+ else:
+ rpki.async.iterator(delta, loop, done)
- def apply_add_v6(self, text, cb):
- self.base.v6 |= rpki.resource_set.resource_set_ipv6(text)
- cb()
+ def dump(self):
+ """
+ Print content of the database.
+ """
- def apply_sub_as(self, text, cb):
- self.base.asn |= rpki.resource_set.resource_set_as(text)
- cb()
+ for a in self:
+ print a
- def apply_sub_v4(self, text, cb):
- self.base.v4 |= rpki.resource_set.resource_set_ipv4(text)
- cb()
+class allocation(object):
- def apply_sub_v6(self, text, cb):
- self.base.v6 |= rpki.resource_set.resource_set_ipv6(text)
- cb()
+ parent = None
+ irdb_db_name = None
+ irdb_port = None
+ rpki_db_name = None
+ rpki_port = None
+ crl_interval = None
+ regen_margin = None
+ last_cms_time = None
+ rpkid_process = None
+ irdbd_process = None
+
+ def __init__(self, yaml, db, parent = None):
+ """
+ Initialize one entity and insert it into the database.
+ """
+
+ db.append(self)
+ self.name = yaml["name"]
+ self.parent = parent
+ self.kids = [allocation(k, db, self) for k in yaml.get("kids", ())]
+ valid_until = None
+ if "valid_until" in yaml:
+ valid_until = rpki.sundial.datetime.from_datetime(yaml.get("valid_until"))
+ if valid_until is None and "valid_for" in yaml:
+ valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"])
+ self.base = rpki.resource_set.resource_bag(
+ asn = rpki.resource_set.resource_set_as(yaml.get("asn")),
+ v4 = rpki.resource_set.resource_set_ipv4(yaml.get("ipv4")),
+ v6 = rpki.resource_set.resource_set_ipv6(yaml.get("ipv6")),
+ valid_until = valid_until)
+ self.sia_base = yaml.get("sia_base")
+ if "crl_interval" in yaml:
+ self.crl_interval = rpki.sundial.timedelta.parse(yaml["crl_interval"]).convert_to_seconds()
+ if "regen_margin" in yaml:
+ self.regen_margin = rpki.sundial.timedelta.parse(yaml["regen_margin"]).convert_to_seconds()
+ self.roa_requests = [roa_request.parse(y) for y in yaml.get("roa_request", yaml.get("route_origin", ()))]
+ for r in self.roa_requests:
+ if r.v4:
+ self.base.v4 |= r.v4.to_resource_set()
+ if r.v6:
+ self.base.v6 |= r.v6.to_resource_set()
+ self.router_certs = [router_cert.parse(y) for y in yaml.get("router_cert", ())]
+ for r in self.router_certs:
+ self.base.asn |= r.asn
+ self.hosted_by = yaml.get("hosted_by")
+ self.extra_conf = yaml.get("extra_conf", [])
+ self.hosts = []
+
+ def closure(self):
+ """
+ Compute the transitive resource closure.
+ """
+
+ resources = self.base
+ for kid in self.kids:
+ resources |= kid.closure()
+ self.resources = resources
+ return resources
+
+ def apply_delta(self, yaml, cb):
+ """
+ Apply deltas to this entity.
+ """
+
+ logger.info("Applying delta: %s", yaml)
+
+ def loop(iterator, kv):
+ if kv[0] == "name":
+ iterator()
+ else:
+ getattr(self, "apply_" + kv[0])(kv[1], iterator)
+
+ rpki.async.iterator(yaml.items(), loop, cb)
+
+ def apply_add_as(self, text, cb):
+ self.base.asn |= rpki.resource_set.resource_set_as(text)
+ cb()
+
+ def apply_add_v4(self, text, cb):
+ self.base.v4 |= rpki.resource_set.resource_set_ipv4(text)
+ cb()
+
+ def apply_add_v6(self, text, cb):
+ self.base.v6 |= rpki.resource_set.resource_set_ipv6(text)
+ cb()
+
+ def apply_sub_as(self, text, cb):
+ self.base.asn |= rpki.resource_set.resource_set_as(text)
+ cb()
+
+ def apply_sub_v4(self, text, cb):
+ self.base.v4 |= rpki.resource_set.resource_set_ipv4(text)
+ cb()
+
+ def apply_sub_v6(self, text, cb):
+ self.base.v6 |= rpki.resource_set.resource_set_ipv6(text)
+ cb()
+
+ def apply_valid_until(self, stamp, cb):
+ self.base.valid_until = rpki.sundial.datetime.from_datetime(stamp)
+ cb()
+
+ def apply_valid_for(self, text, cb):
+ self.base.valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(text)
+ cb()
+
+ def apply_valid_add(self, text, cb):
+ self.base.valid_until += rpki.sundial.timedelta.parse(text)
+ cb()
+
+ def apply_valid_sub(self, text, cb):
+ self.base.valid_until -= rpki.sundial.timedelta.parse(text)
+ cb()
+
+ def apply_roa_request_add(self, yaml, cb):
+ for y in yaml:
+ r = roa_request.parse(y)
+ if r not in self.roa_requests:
+ self.roa_requests.append(r)
+ cb()
+
+ def apply_roa_request_del(self, yaml, cb):
+ for y in yaml:
+ r = roa_request.parse(y)
+ if r in self.roa_requests:
+ self.roa_requests.remove(r)
+ cb()
+
+ def apply_router_cert_add(self, yaml, cb):
+ for y in yaml:
+ r = router_cert.parse(y)
+ if r not in self.router_certs:
+ self.router_certs.append(r)
+ cb()
+
+ def apply_router_cert_del(self, yaml, cb):
+ for y in yaml:
+ r = router_cert.parse(y)
+ if r in self.router_certs:
+ self.router_certs.remove(r)
+ cb()
+
+ def apply_rekey(self, target, cb):
+
+ def done(e):
+ if isinstance(e, Exception):
+ logger.exception("Exception while rekeying %s", self.name)
+ raise e
+ cb()
+
+ if target is None:
+ logger.info("Rekeying <tenant/> %s", self.name)
+ self.call_rpkid([rpki.left_right.self_elt.make_pdu(
+ action = "set", self_handle = self.name, rekey = "yes")], cb = done)
+ else:
+ logger.info("Rekeying <parent/> %s %s", self.name, target)
+ self.call_rpkid([rpki.left_right.parent_elt.make_pdu(
+ action = "set", self_handle = self.name, parent_handle = target, rekey = "yes")], cb = done)
+
+ def apply_revoke(self, target, cb):
+
+ def done(e):
+ if isinstance(e, Exception):
+ logger.exception("Exception while revoking %s", self.name)
+ raise e
+ cb()
+
+ if target is None:
+ logger.info("Revoking <tenant/> %s", self.name)
+ self.call_rpkid([rpki.left_right.self_elt.make_pdu(
+ action = "set", self_handle = self.name, revoke = "yes")], cb = done)
+ else:
+ logger.info("Revoking <parent/> %s %s", self.name, target)
+ self.call_rpkid([rpki.left_right.parent_elt.make_pdu(
+ action = "set", self_handle = self.name, parent_handle = target, revoke = "yes")], cb = done)
+
+ def __str__(self):
+ s = self.name + "\n"
+ if self.resources.asn: s += " ASN: %s\n" % self.resources.asn
+ if self.resources.v4: s += " IPv4: %s\n" % self.resources.v4
+ if self.resources.v6: s += " IPv6: %s\n" % self.resources.v6
+ if self.kids: s += " Kids: %s\n" % ", ".join(k.name for k in self.kids)
+ if self.parent: s += " Up: %s\n" % self.parent.name
+ if self.sia_base: s += " SIA: %s\n" % self.sia_base
+ return s + "Until: %s\n" % self.resources.valid_until
+
+
+ @property
+ def is_root(self):
+ return self.parent is None
+
+ @property
+ def is_twig(self):
+ return not self.is_root
+
+ @property
+ def is_hosted(self):
+ return self.hosted_by is not None
+
+ @property
+ def is_engine(self):
+ return not self.is_hosted
+
+ def set_engine_number(self, n):
+ """
+ Set the engine number for this entity.
+ """
+
+ self.irdb_db_name = "irdb%d" % n
+ self.irdb_port = allocate_port()
+ self.rpki_db_name = "rpki%d" % n
+ self.rpki_port = allocate_port()
+
+ def get_rpki_port(self):
+ """
+ Get rpki port to use for this entity.
+ """
+
+ if self.is_hosted:
+ assert self.hosted_by.rpki_port is not None
+ return self.hosted_by.rpki_port
+ else:
+ assert self.rpki_port is not None
+ return self.rpki_port
+
+ def setup_bpki_certs(self):
+ """
+ Create BPKI certificates for this entity.
+ """
+
+ logger.info("Constructing BPKI keys and certs for %s", self.name)
+ setup_bpki_cert_chain(name = self.name,
+ ee = ("RPKI", "IRDB", "IRBE"),
+ ca = ("SELF",))
+ self.rpkid_ta = rpki.x509.X509(PEM_file = self.name + "-TA.cer")
+ self.irbe_key = rpki.x509.RSA( PEM_file = self.name + "-IRBE.key")
+ self.irbe_cert = rpki.x509.X509(PEM_file = self.name + "-IRBE.cer")
+ self.rpkid_cert = rpki.x509.X509(PEM_file = self.name + "-RPKI.cer")
+
+ def setup_conf_file(self):
+ """
+ Write config files for this entity.
+ """
+
+ logger.info("Writing config files for %s", self.name)
+ assert self.rpki_port is not None
+ d = dict(my_name = self.name,
+ irdb_db_name = self.irdb_db_name,
+ irdb_db_pass = irdb_db_pass,
+ irdb_port = self.irdb_port,
+ rpki_db_name = self.rpki_db_name,
+ rpki_db_pass = rpki_db_pass,
+ rpki_port = self.rpki_port)
+ f = open(self.name + ".conf", "w")
+ f.write(conf_fmt_1 % d)
+ for line in self.extra_conf:
+ f.write(line + "\n")
+ f.close()
+
+ def setup_sql(self, rpki_sql, irdb_sql):
+ """
+ Set up this entity's IRDB.
+ """
+
+ logger.info("Setting up MySQL for %s", self.name)
+ db = MySQLdb.connect(user = "rpki", db = self.rpki_db_name, passwd = rpki_db_pass,
+ conv = sql_conversions)
+ cur = db.cursor()
+ db.autocommit(True)
+ for sql in rpki_sql:
+ try:
+ cur.execute(sql)
+ except:
+ if "DROP TABLE IF EXISTS" not in sql.upper():
+ raise
+ db.close()
+ db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass,
+ conv = sql_conversions)
+ cur = db.cursor()
+ db.autocommit(True)
+ for sql in irdb_sql:
+ try:
+ cur.execute(sql)
+ except:
+ if "DROP TABLE IF EXISTS" not in sql.upper():
+ raise
+ for s in [self] + self.hosts:
+ for kid in s.kids:
+ cur.execute("INSERT registrant (registrant_handle, registry_handle, valid_until) VALUES (%s, %s, %s)",
+ (kid.name, s.name, kid.resources.valid_until))
+ db.close()
+
+ def sync_sql(self):
+ """
+ Whack this entity's IRDB to match our master database. We do this
+ once during setup, then do it again every time we apply a delta to
+ this entity.
+ """
+
+ logger.info("Updating MySQL data for IRDB %s", self.name)
+ db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass,
+ conv = sql_conversions)
+ cur = db.cursor()
+ db.autocommit(True)
+ cur.execute("DELETE FROM registrant_asn")
+ cur.execute("DELETE FROM registrant_net")
+ cur.execute("DELETE FROM roa_request_prefix")
+ cur.execute("DELETE FROM roa_request")
+ cur.execute("DELETE FROM ee_certificate_asn")
+ cur.execute("DELETE FROM ee_certificate_net")
+ cur.execute("DELETE FROM ee_certificate")
+
+ for s in [self] + self.hosts:
+ for kid in s.kids:
+ cur.execute("SELECT registrant_id FROM registrant WHERE registrant_handle = %s AND registry_handle = %s",
+ (kid.name, s.name))
+ registrant_id = cur.fetchone()[0]
+ for as_range in kid.resources.asn:
+ cur.execute("INSERT registrant_asn (start_as, end_as, registrant_id) VALUES (%s, %s, %s)",
+ (as_range.min, as_range.max, registrant_id))
+ for v4_range in kid.resources.v4:
+ cur.execute("INSERT registrant_net (start_ip, end_ip, version, registrant_id) VALUES (%s, %s, 4, %s)",
+ (v4_range.min, v4_range.max, registrant_id))
+ for v6_range in kid.resources.v6:
+ cur.execute("INSERT registrant_net (start_ip, end_ip, version, registrant_id) VALUES (%s, %s, 6, %s)",
+ (v6_range.min, v6_range.max, registrant_id))
+ cur.execute("UPDATE registrant SET valid_until = %s WHERE registrant_id = %s",
+ (kid.resources.valid_until, registrant_id))
+ for r in s.roa_requests:
+ cur.execute("INSERT roa_request (self_handle, asn) VALUES (%s, %s)",
+ (s.name, r.asn))
+ roa_request_id = cur.lastrowid
+ for version, prefix_set in ((4, r.v4), (6, r.v6)):
+ if prefix_set:
+ cur.executemany("INSERT roa_request_prefix "
+ "(roa_request_id, prefix, prefixlen, max_prefixlen, version) "
+ "VALUES (%s, %s, %s, %s, %s)",
+ ((roa_request_id, x.prefix, x.prefixlen, x.max_prefixlen, version)
+ for x in prefix_set))
+ for r in s.router_certs:
+ cur.execute("INSERT ee_certificate (self_handle, pkcs10, gski, cn, sn, eku, valid_until) "
+ "VALUES (%s, %s, %s, %s, %s, %s, %s)",
+ (s.name, r.pkcs10.get_DER(), r.gski, r.cn, r.sn, r.eku, s.resources.valid_until))
+ ee_certificate_id = cur.lastrowid
+ cur.executemany("INSERT ee_certificate_asn (ee_certificate_id, start_as, end_as) VALUES (%s, %s, %s)",
+ ((ee_certificate_id, a.min, a.max) for a in r.asn))
+ db.close()
+
+ def run_daemons(self):
+ """
+ Run daemons for this entity.
+ """
+
+ logger.info("Running daemons for %s", self.name)
+ env = dict(os.environ, RPKI_CONF = self.name + ".conf")
+ self.rpkid_process = subprocess.Popen((prog_python, prog_rpkid, "--foreground", "--log-stdout", "--log-level", "debug") +
+ (("--profile", self.name + ".prof") if args.profile else ()),
+ env = env)
+ self.irdbd_process = subprocess.Popen((prog_python, prog_irdbd, "--foreground", "--log-stdout", "--log-level", "debug"),
+ env = env)
+
+ def kill_daemons(self):
+ """
+ Kill daemons for this entity.
+ """
+
+ # pylint: disable=E1103
+ for proc, name in ((self.rpkid_process, "rpkid"),
+ (self.irdbd_process, "irdbd")):
+ if proc is not None and proc.poll() is None:
+ logger.info("Killing daemon %s pid %s for %s", name, proc.pid, self.name)
+ try:
+ proc.terminate()
+ except OSError:
+ pass
+ if proc is not None:
+ logger.info("Daemon %s pid %s for %s exited with code %s",
+ name, proc.pid, self.name, proc.wait())
+
+ def call_rpkid(self, pdus, cb):
+ """
+ Send a left-right message to this entity's RPKI daemon and return
+ the response.
+
+ If this entity is hosted (does not run its own RPKI daemon), all
+ of this happens with the hosting RPKI daemon.
+ """
+
+ logger.info("Calling rpkid for %s", self.name)
+
+ if self.is_hosted:
+ logger.info("rpkid %s is hosted by rpkid %s, switching", self.name, self.hosted_by.name)
+ self = self.hosted_by
+ assert not self.is_hosted
+
+ assert isinstance(pdus, (list, tuple))
+ assert self.rpki_port is not None
+
+ q_msg = rpki.left_right.msg.query(*pdus)
+ q_cms = rpki.left_right.cms_msg_saxify()
+ q_der = q_cms.wrap(q_msg, self.irbe_key, self.irbe_cert)
+ q_url = "http://localhost:%d/left-right" % self.rpki_port
+
+ logger.debug(q_cms.pretty_print_content())
+
+ def done(r_der):
+ logger.info("Callback from rpkid %s", self.name)
+ r_cms = rpki.left_right.cms_msg_saxify(DER = r_der)
+ r_msg = r_cms.unwrap((self.rpkid_ta, self.rpkid_cert))
+ self.last_cms_time = r_cms.check_replay(self.last_cms_time, q_url)
+ logger.debug(r_cms.pretty_print_content())
+ assert r_msg.is_reply
+ for r_pdu in r_msg:
+ assert not isinstance(r_pdu, rpki.left_right.report_error_elt)
+ cb(r_msg)
+
+ def lose(e):
+ raise
+
+ rpki.http.client(
+ url = q_url,
+ msg = q_der,
+ callback = done,
+ errback = lose)
+
+ def cross_certify(self, certificant, reverse = False):
+ """
+ Cross-certify and return the resulting certificate.
+ """
+
+ if reverse:
+ certifier = certificant
+ certificant = self.name + "-SELF"
+ else:
+ certifier = self.name + "-SELF"
+ return cross_certify(certificant, certifier)
+
+ def create_rpki_objects(self, cb):
+ """
+ Create RPKI engine objects for this engine.
+
+ Root node of the engine tree is special, it too has a parent but
+ that one is the magic self-signed micro engine.
+
+ The rest of this is straightforward. There are a lot of objects
+ to create, but we can do batch them all into one honking PDU, then
+ issue one more PDU to set BSC EE certificates based on the PKCS
+ #10 requests we get back when we tell rpkid to generate BSC keys.
+ """
+
+ assert not self.is_hosted
+
+ selves = [self] + self.hosts
+
+ rpkid_pdus = []
+ pubd_pdus = []
+
+ for i, s in enumerate(selves):
+ logger.info("Creating RPKI objects for [%d] %s", i, s.name)
+
+ rpkid_pdus.append(rpki.left_right.self_elt.make_pdu(
+ action = "create",
+ self_handle = s.name,
+ crl_interval = s.crl_interval,
+ regen_margin = s.regen_margin,
+ bpki_cert = (s.cross_certify(s.hosted_by.name + "-TA", reverse = True)
+ if s.is_hosted else
+ rpki.x509.X509(Auto_file = s.name + "-SELF.cer"))))
+
+ rpkid_pdus.append(rpki.left_right.bsc_elt.make_pdu(
+ action = "create",
+ self_handle = s.name,
+ bsc_handle = "b",
+ generate_keypair = True))
+
+ pubd_pdus.append(rpki.publication_control.client_elt.make_pdu(
+ action = "create",
+ client_handle = s.client_handle,
+ base_uri = s.sia_base,
+ bpki_cert = s.cross_certify(pubd_name + "-TA", reverse = True)))
+
+ rpkid_pdus.append(rpki.left_right.repository_elt.make_pdu(
+ action = "create",
+ self_handle = s.name,
+ bsc_handle = "b",
+ repository_handle = "r",
+ bpki_cert = s.cross_certify(pubd_name + "-TA"),
+ peer_contact_uri = "http://localhost:%d/client/%s" % (pubd_port, s.client_handle)))
+
+ for k in s.kids:
+ rpkid_pdus.append(rpki.left_right.child_elt.make_pdu(
+ action = "create",
+ self_handle = s.name,
+ child_handle = k.name,
+ bsc_handle = "b",
+ bpki_cert = s.cross_certify(k.name + "-SELF")))
+
+ if s.is_root:
+ rootd_cert = s.cross_certify(rootd_name + "-TA")
+ rpkid_pdus.append(rpki.left_right.parent_elt.make_pdu(
+ action = "create",
+ self_handle = s.name,
+ parent_handle = "rootd",
+ bsc_handle = "b",
+ repository_handle = "r",
+ sia_base = s.sia_base,
+ bpki_cert = rootd_cert,
+ sender_name = s.name,
+ recipient_name = "rootd",
+ peer_contact_uri = "http://localhost:%s/" % rootd_port))
+ else:
+ rpkid_pdus.append(rpki.left_right.parent_elt.make_pdu(
+ action = "create",
+ self_handle = s.name,
+ parent_handle = s.parent.name,
+ bsc_handle = "b",
+ repository_handle = "r",
+ sia_base = s.sia_base,
+ bpki_cert = s.cross_certify(s.parent.name + "-SELF"),
+ sender_name = s.name,
+ recipient_name = s.parent.name,
+ peer_contact_uri = "http://localhost:%s/up-down/%s/%s" % (s.parent.get_rpki_port(),
+ s.parent.name, s.name)))
+
+ def one():
+ call_pubd(pubd_pdus, cb = two)
+
+ def two(vals):
+ self.call_rpkid(rpkid_pdus, cb = three)
+
+ def three(vals):
+
+ bsc_dict = dict((b.self_handle, b) for b in vals if isinstance(b, rpki.left_right.bsc_elt))
+
+ bsc_pdus = []
+
+ for s in selves:
+ b = bsc_dict[s.name]
+
+ logger.info("Issuing BSC EE cert for %s", s.name)
+ cmd = (prog_openssl, "x509", "-req", "-sha256", "-extfile", s.name + "-RPKI.conf",
+ "-extensions", "req_x509_ext", "-days", "30",
+ "-CA", s.name + "-SELF.cer", "-CAkey", s.name + "-SELF.key", "-CAcreateserial", "-text")
+ signer = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+ signed = signer.communicate(input = b.pkcs10_request.get_PEM())
+ if not signed[0]:
+ logger.warning(signed[1])
+ raise CouldntIssueBSCEECertificate("Couldn't issue BSC EE certificate")
+ s.bsc_ee = rpki.x509.X509(PEM = signed[0])
+ s.bsc_crl = rpki.x509.CRL(PEM_file = s.name + "-SELF.crl")
+ logger.info("BSC EE cert for %s SKI %s", s.name, s.bsc_ee.hSKI())
+
+ bsc_pdus.append(rpki.left_right.bsc_elt.make_pdu(
+ action = "set",
+ self_handle = s.name,
+ bsc_handle = "b",
+ signing_cert = s.bsc_ee,
+ signing_cert_crl = s.bsc_crl))
+
+ self.call_rpkid(bsc_pdus, cb = four)
+
+ def four(vals):
+ cb()
+
+ one()
+
+ def setup_yaml_leaf(self):
+ """
+ Generate certificates and write YAML scripts for leaf nodes.
+
+ We're cheating a bit here: properly speaking, we can't generate
+ issue or revoke requests without knowing the class, which is
+ generated on the fly, but at the moment the test case is
+ simplistic enough that the class will always be "1", so we just
+ wire in that value for now.
+
+ Well, ok, we just broke that assumption. Now we do something even
+ nastier, just to eke a bit more life out of this kludge. This
+ really needs to be rewritten, but it may require a different tool
+ than testpoke.
+ """
+
+ if not os.path.exists(self.name + ".key"):
+ logger.info("Generating RPKI key for %s", self.name)
+ subprocess.check_call((prog_openssl, "genrsa", "-out", self.name + ".key", "2048" ),
+ stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
+ ski = rpki.x509.RSA(PEM_file = self.name + ".key").gSKI()
+
+ if self.parent.is_hosted:
+ parent_host = self.parent.hosted_by.name
+ else:
+ parent_host = self.parent.name
- def apply_valid_until(self, stamp, cb):
- self.base.valid_until = rpki.sundial.datetime.from_datetime(stamp)
- cb()
+ self.cross_certify(self.parent.name + "-SELF")
+ self.cross_certify(parent_host + "-TA")
- def apply_valid_for(self, text, cb):
- self.base.valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(text)
- cb()
+ def run_cron(self, cb):
+ """
+ Trigger cron run for this engine.
+ """
- def apply_valid_add(self, text, cb):
- self.base.valid_until += rpki.sundial.timedelta.parse(text)
- cb()
+ logger.info("Running cron for %s", self.name)
- def apply_valid_sub(self, text, cb):
- self.base.valid_until -= rpki.sundial.timedelta.parse(text)
- cb()
+ assert self.rpki_port is not None
- def apply_roa_request_add(self, yaml, cb):
- for y in yaml:
- r = roa_request.parse(y)
- if r not in self.roa_requests:
- self.roa_requests.append(r)
- cb()
+ def done(result):
+ assert result == "OK", 'Expected "OK" result from cronjob, got %r' % result
+ cb()
- def apply_roa_request_del(self, yaml, cb):
- for y in yaml:
- r = roa_request.parse(y)
- if r in self.roa_requests:
- self.roa_requests.remove(r)
- cb()
+ rpki.http.client(
+ url = "http://localhost:%d/cronjob" % self.rpki_port,
+ msg = "Run cron now, please",
+ callback = done,
+ errback = done)
- def apply_router_cert_add(self, yaml, cb):
- for y in yaml:
- r = router_cert.parse(y)
- if r not in self.router_certs:
- self.router_certs.append(r)
- cb()
+ def run_yaml(self):
+ """
+ Run YAML scripts for this leaf entity. Since we're not bothering
+ to check the class list returned by the list command, the issue
+ command may fail, so we treat failure of the list command as an
+ error, but only issue a warning when issue fails.
+ """
- def apply_router_cert_del(self, yaml, cb):
- for y in yaml:
- r = router_cert.parse(y)
- if r in self.router_certs:
- self.router_certs.remove(r)
- cb()
+ logger.info("Running YAML for %s", self.name)
+ subprocess.check_call((prog_python, prog_poke, "-y", self.name + ".yaml", "-r", "list"))
+ if subprocess.call((prog_python, prog_poke, "-y", self.name + ".yaml", "-r", "issue")) != 0:
+ logger.warning("YAML issue command failed for %s, continuing", self.name)
- def apply_rekey(self, target, cb):
-
- def done(e):
- if isinstance(e, Exception):
- logger.exception("Exception while rekeying %s", self.name)
- raise e
- cb()
-
- if target is None:
- logger.info("Rekeying <self/> %s", self.name)
- self.call_rpkid([rpki.left_right.self_elt.make_pdu(
- action = "set", self_handle = self.name, rekey = "yes")], cb = done)
- else:
- logger.info("Rekeying <parent/> %s %s", self.name, target)
- self.call_rpkid([rpki.left_right.parent_elt.make_pdu(
- action = "set", self_handle = self.name, parent_handle = target, rekey = "yes")], cb = done)
-
- def apply_revoke(self, target, cb):
-
- def done(e):
- if isinstance(e, Exception):
- logger.exception("Exception while revoking %s", self.name)
- raise e
- cb()
-
- if target is None:
- logger.info("Revoking <self/> %s", self.name)
- self.call_rpkid([rpki.left_right.self_elt.make_pdu(
- action = "set", self_handle = self.name, revoke = "yes")], cb = done)
- else:
- logger.info("Revoking <parent/> %s %s", self.name, target)
- self.call_rpkid([rpki.left_right.parent_elt.make_pdu(
- action = "set", self_handle = self.name, parent_handle = target, revoke = "yes")], cb = done)
-
- def __str__(self):
- s = self.name + "\n"
- if self.resources.asn: s += " ASN: %s\n" % self.resources.asn
- if self.resources.v4: s += " IPv4: %s\n" % self.resources.v4
- if self.resources.v6: s += " IPv6: %s\n" % self.resources.v6
- if self.kids: s += " Kids: %s\n" % ", ".join(k.name for k in self.kids)
- if self.parent: s += " Up: %s\n" % self.parent.name
- if self.sia_base: s += " SIA: %s\n" % self.sia_base
- return s + "Until: %s\n" % self.resources.valid_until
-
-
- @property
- def is_root(self):
- return self.parent is None
-
- @property
- def is_twig(self):
- return not self.is_root
-
- @property
- def is_hosted(self):
- return self.hosted_by is not None
-
- @property
- def is_engine(self):
- return not self.is_hosted
-
- def set_engine_number(self, n):
+def setup_bpki_cert_chain(name, ee = (), ca = ()):
"""
- Set the engine number for this entity.
+ Build a set of BPKI certificates.
"""
- self.irdb_db_name = "irdb%d" % n
- self.irdb_port = allocate_port()
- self.rpki_db_name = "rpki%d" % n
- self.rpki_port = allocate_port()
- def get_rpki_port(self):
- """
- Get rpki port to use for this entity.
+ s = "exec >/dev/null 2>&1\n"
+ #s = "set -x\n"
+ for kind in ("TA",) + ee + ca:
+ d = dict(name = name,
+ kind = kind,
+ ca = "false" if kind in ee else "true",
+ openssl = prog_openssl)
+ f = open("%(name)s-%(kind)s.conf" % d, "w")
+ f.write(bpki_cert_fmt_1 % d)
+ f.close()
+ if not os.path.exists("%(name)s-%(kind)s.key" % d):
+ s += bpki_cert_fmt_2 % d
+ s += bpki_cert_fmt_3 % d
+ d = dict(name = name,
+ openssl = prog_openssl)
+ s += bpki_cert_fmt_4 % d
+ for kind in ee + ca:
+ d["kind"] = kind
+ s += bpki_cert_fmt_5 % d
+ for kind in ("TA",) + ca:
+ d["kind"] = kind
+ s += bpki_cert_fmt_6 % d
+ subprocess.check_call(s, shell = True)
+
+def setup_rootd(rpkid, rootd_yaml, db):
"""
- if self.is_hosted:
- assert self.hosted_by.rpki_port is not None
- return self.hosted_by.rpki_port
- else:
- assert self.rpki_port is not None
- return self.rpki_port
-
- def setup_bpki_certs(self):
+ Write the config files for rootd.
"""
- Create BPKI certificates for this entity.
- """
- logger.info("Constructing BPKI keys and certs for %s", self.name)
- setup_bpki_cert_chain(name = self.name,
- ee = ("RPKI", "IRDB", "IRBE"),
- ca = ("SELF",))
- self.rpkid_ta = rpki.x509.X509(PEM_file = self.name + "-TA.cer")
- self.irbe_key = rpki.x509.RSA( PEM_file = self.name + "-IRBE.key")
- self.irbe_cert = rpki.x509.X509(PEM_file = self.name + "-IRBE.cer")
- self.rpkid_cert = rpki.x509.X509(PEM_file = self.name + "-RPKI.cer")
-
- def setup_conf_file(self):
+
+ rpkid.cross_certify(rootd_name + "-TA", reverse = True)
+ cross_certify(pubd_name + "-TA", rootd_name + "-TA")
+ logger.info("Writing config files for %s", rootd_name)
+ d = dict(rootd_name = rootd_name,
+ rootd_port = rootd_port,
+ rpkid_name = rpkid.name,
+ pubd_name = pubd_name,
+ rootd_sia = rootd_sia,
+ rsyncd_dir = rsyncd_dir,
+ openssl = prog_openssl,
+ lifetime = rootd_yaml.get("lifetime", "30d"),
+ pubd_port = pubd_port,
+ rootd_handle = db.root.client_handle + "-" + rootd_name)
+ f = open(rootd_name + ".conf", "w")
+ f.write(rootd_fmt_1 % d)
+ f.close()
+ s = "exec >/dev/null 2>&1\n"
+ #s = "set -x\n"
+ if not os.path.exists("root.key"):
+ s += rootd_fmt_2 % d
+ s += rootd_fmt_3 % d
+ subprocess.check_call(s, shell = True)
+
+def setup_rcynic():
"""
- Write config files for this entity.
+ Write the config file for rcynic.
"""
- logger.info("Writing config files for %s", self.name)
- assert self.rpki_port is not None
- d = { "my_name" : self.name,
- "irdb_db_name" : self.irdb_db_name,
- "irdb_db_pass" : irdb_db_pass,
- "irdb_port" : self.irdb_port,
- "rpki_db_name" : self.rpki_db_name,
- "rpki_db_pass" : rpki_db_pass,
- "rpki_port" : self.rpki_port }
- f = open(self.name + ".conf", "w")
- f.write(conf_fmt_1 % d)
- for line in self.extra_conf:
- f.write(line + "\n")
+
+ logger.info("Config file for rcynic")
+ d = dict(rcynic_name = rcynic_name,
+ rootd_name = rootd_name,
+ rootd_sia = rootd_sia)
+ f = open(rcynic_name + ".conf", "w")
+ f.write(rcynic_fmt_1 % d)
f.close()
- def setup_sql(self, rpki_sql, irdb_sql):
+def setup_rsyncd():
"""
- Set up this entity's IRDB.
+ Write the config file for rsyncd.
"""
- logger.info("Setting up MySQL for %s", self.name)
- db = MySQLdb.connect(user = "rpki", db = self.rpki_db_name, passwd = rpki_db_pass,
- conv = sql_conversions)
- cur = db.cursor()
- db.autocommit(True)
- for sql in rpki_sql:
- try:
- cur.execute(sql)
- except Exception:
- if "DROP TABLE IF EXISTS" not in sql.upper():
- raise
- db.close()
- db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass,
- conv = sql_conversions)
- cur = db.cursor()
- db.autocommit(True)
- for sql in irdb_sql:
- try:
- cur.execute(sql)
- except Exception:
- if "DROP TABLE IF EXISTS" not in sql.upper():
- raise
- for s in [self] + self.hosts:
- for kid in s.kids:
- cur.execute("INSERT registrant (registrant_handle, registry_handle, valid_until) VALUES (%s, %s, %s)",
- (kid.name, s.name, kid.resources.valid_until))
- db.close()
- def sync_sql(self):
+ logger.info("Config file for rsyncd")
+ d = dict(rsyncd_name = rsyncd_name,
+ rsyncd_port = rsyncd_port,
+ rsyncd_module = rsyncd_module,
+ rsyncd_dir = rsyncd_dir)
+ f = open(rsyncd_name + ".conf", "w")
+ f.write(rsyncd_fmt_1 % d)
+ f.close()
+
+def setup_publication(pubd_sql, irdb_db_name):
"""
- Whack this entity's IRDB to match our master database. We do this
- once during setup, then do it again every time we apply a delta to
- this entity.
+ Set up publication daemon.
"""
- logger.info("Updating MySQL data for IRDB %s", self.name)
- db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass,
+
+ logger.info("Configure publication daemon")
+ publication_dir = os.getcwd() + "/publication"
+ assert rootd_sia.startswith("rsync://")
+ global rsyncd_dir
+ rsyncd_dir = publication_dir + "/".join(rootd_sia.split("/")[4:])
+ if not rsyncd_dir.endswith("/"):
+ rsyncd_dir += "/"
+ os.makedirs(rsyncd_dir + "root/trunk")
+ db = MySQLdb.connect(db = pubd_db_name, user = pubd_db_user, passwd = pubd_db_pass,
conv = sql_conversions)
cur = db.cursor()
db.autocommit(True)
- cur.execute("DELETE FROM registrant_asn")
- cur.execute("DELETE FROM registrant_net")
- cur.execute("DELETE FROM roa_request_prefix")
- cur.execute("DELETE FROM roa_request")
- cur.execute("DELETE FROM ee_certificate_asn")
- cur.execute("DELETE FROM ee_certificate_net")
- cur.execute("DELETE FROM ee_certificate")
-
- for s in [self] + self.hosts:
- for kid in s.kids:
- cur.execute("SELECT registrant_id FROM registrant WHERE registrant_handle = %s AND registry_handle = %s",
- (kid.name, s.name))
- registrant_id = cur.fetchone()[0]
- for as_range in kid.resources.asn:
- cur.execute("INSERT registrant_asn (start_as, end_as, registrant_id) VALUES (%s, %s, %s)",
- (as_range.min, as_range.max, registrant_id))
- for v4_range in kid.resources.v4:
- cur.execute("INSERT registrant_net (start_ip, end_ip, version, registrant_id) VALUES (%s, %s, 4, %s)",
- (v4_range.min, v4_range.max, registrant_id))
- for v6_range in kid.resources.v6:
- cur.execute("INSERT registrant_net (start_ip, end_ip, version, registrant_id) VALUES (%s, %s, 6, %s)",
- (v6_range.min, v6_range.max, registrant_id))
- cur.execute("UPDATE registrant SET valid_until = %s WHERE registrant_id = %s",
- (kid.resources.valid_until, registrant_id))
- for r in s.roa_requests:
- cur.execute("INSERT roa_request (self_handle, asn) VALUES (%s, %s)",
- (s.name, r.asn))
- roa_request_id = cur.lastrowid
- for version, prefix_set in ((4, r.v4), (6, r.v6)):
- if prefix_set:
- cur.executemany("INSERT roa_request_prefix "
- "(roa_request_id, prefix, prefixlen, max_prefixlen, version) "
- "VALUES (%s, %s, %s, %s, %s)",
- ((roa_request_id, x.prefix, x.prefixlen, x.max_prefixlen, version)
- for x in prefix_set))
- for r in s.router_certs:
- cur.execute("INSERT ee_certificate (self_handle, pkcs10, gski, cn, sn, eku, valid_until) "
- "VALUES (%s, %s, %s, %s, %s, %s, %s)",
- (s.name, r.pkcs10.get_DER(), r.gski, r.cn, r.sn, r.eku, s.resources.valid_until))
- ee_certificate_id = cur.lastrowid
- cur.executemany("INSERT ee_certificate_asn (ee_certificate_id, start_as, end_as) VALUES (%s, %s, %s)",
- ((ee_certificate_id, a.min, a.max) for a in r.asn))
+ for sql in pubd_sql:
+ try:
+ cur.execute(sql)
+ except:
+ if "DROP TABLE IF EXISTS" not in sql.upper():
+ raise
db.close()
+ d = dict(pubd_name = pubd_name,
+ pubd_port = pubd_port,
+ pubd_db_name = pubd_db_name,
+ pubd_db_user = pubd_db_user,
+ pubd_db_pass = pubd_db_pass,
+ pubd_dir = rsyncd_dir,
+ irdb_db_name = irdb_db_name,
+ irdb_db_pass = irdb_db_pass)
+ f = open(pubd_name + ".conf", "w")
+ f.write(pubd_fmt_1 % d)
+ f.close()
+ global pubd_ta
+ global pubd_irbe_key
+ global pubd_irbe_cert
+ global pubd_pubd_cert
+ pubd_ta = rpki.x509.X509(Auto_file = pubd_name + "-TA.cer")
+ pubd_irbe_key = rpki.x509.RSA( Auto_file = pubd_name + "-IRBE.key")
+ pubd_irbe_cert = rpki.x509.X509(Auto_file = pubd_name + "-IRBE.cer")
+ pubd_pubd_cert = rpki.x509.X509(Auto_file = pubd_name + "-PUBD.cer")
- def run_daemons(self):
- """
- Run daemons for this entity.
- """
- logger.info("Running daemons for %s", self.name)
- self.rpkid_process = subprocess.Popen((prog_python, prog_rpkid, "--foreground", "--log-stdout", "--log-level", "debug", "--config", self.name + ".conf") +
- (("--profile", self.name + ".prof") if args.profile else ()))
- self.irdbd_process = subprocess.Popen((prog_python, prog_irdbd, "--foreground", "--log-stdout", "--log-level", "debug", "--config", self.name + ".conf"))
-
- def kill_daemons(self):
- """
- Kill daemons for this entity.
- """
- # pylint: disable=E1103
- for proc, name in ((self.rpkid_process, "rpkid"),
- (self.irdbd_process, "irdbd")):
- if proc is not None and proc.poll() is None:
- logger.info("Killing daemon %s pid %s for %s", name, proc.pid, self.name)
- try:
- proc.terminate()
- except OSError:
- pass
- if proc is not None:
- logger.info("Daemon %s pid %s for %s exited with code %s",
- name, proc.pid, self.name, proc.wait())
-
- def call_rpkid(self, pdus, cb):
+def call_pubd(pdus, cb):
"""
- Send a left-right message to this entity's RPKI daemon and return
+ Send a publication control message to publication daemon and return
the response.
-
- If this entity is hosted (does not run its own RPKI daemon), all
- of this happens with the hosting RPKI daemon.
"""
- logger.info("Calling rpkid for %s", self.name)
-
- if self.is_hosted:
- logger.info("rpkid %s is hosted by rpkid %s, switching", self.name, self.hosted_by.name)
- self = self.hosted_by
- assert not self.is_hosted
-
- assert isinstance(pdus, (list, tuple))
- assert self.rpki_port is not None
-
- q_msg = rpki.left_right.msg.query(*pdus)
- q_cms = rpki.left_right.cms_msg()
- q_der = q_cms.wrap(q_msg, self.irbe_key, self.irbe_cert)
- q_url = "http://localhost:%d/left-right" % self.rpki_port
+ logger.info("Calling pubd")
+ q_msg = rpki.publication_control.msg.query(*pdus)
+ q_cms = rpki.publication_control.cms_msg_saxify()
+ q_der = q_cms.wrap(q_msg, pubd_irbe_key, pubd_irbe_cert)
+ q_url = "http://localhost:%d/control" % pubd_port
logger.debug(q_cms.pretty_print_content())
- def done(r_der):
- logger.info("Callback from rpkid %s", self.name)
- r_cms = rpki.left_right.cms_msg(DER = r_der)
- r_msg = r_cms.unwrap((self.rpkid_ta, self.rpkid_cert))
- self.last_cms_time = r_cms.check_replay(self.last_cms_time, q_url)
- logger.debug(r_cms.pretty_print_content())
- assert r_msg.is_reply
- for r_pdu in r_msg:
- assert not isinstance(r_pdu, rpki.left_right.report_error_elt)
- cb(r_msg)
+ def call_pubd_cb(r_der):
+ global pubd_last_cms_time
+ r_cms = rpki.publication_control.cms_msg_saxify(DER = r_der)
+ r_msg = r_cms.unwrap((pubd_ta, pubd_pubd_cert))
+ pubd_last_cms_time = r_cms.check_replay(pubd_last_cms_time, q_url)
+ logger.debug(r_cms.pretty_print_content())
+ assert r_msg.is_reply
+ for r_pdu in r_msg:
+ r_pdu.raise_if_error()
+ cb(r_msg)
- def lose(e):
- raise
+ def call_pubd_eb(e):
+ logger.exception("Problem calling pubd")
rpki.http.client(
- url = q_url,
- msg = q_der,
- callback = done,
- errback = lose)
+ url = q_url,
+ msg = q_der,
+ callback = call_pubd_cb,
+ errback = call_pubd_eb)
+
- def cross_certify(self, certificant, reverse = False):
+def cross_certify(certificant, certifier):
"""
Cross-certify and return the resulting certificate.
"""
- if reverse:
- certifier = certificant
- certificant = self.name + "-SELF"
- else:
- certifier = self.name + "-SELF"
certfile = certifier + "-" + certificant + ".cer"
logger.info("Cross certifying %s into %s's BPKI (%s)", certificant, certifier, certfile)
@@ -938,424 +1320,58 @@ class allocation(object):
notAfter = now + rpki.sundial.timedelta(days = 30)
try:
- f = open(serial_file, "r")
- serial = f.read()
- f.close()
- serial = int(serial.splitlines()[0], 16)
+ with open(serial_file, "r") as f:
+ serial = int(f.read().splitlines()[0], 16)
except IOError:
- serial = 1
+ serial = 1
x = parent.bpki_cross_certify(
- keypair = keypair,
- source_cert = child,
- serial = serial,
- notAfter = notAfter,
- now = now)
-
- f = open(serial_file, "w")
- f.write("%02x\n" % (serial + 1))
- f.close()
+ keypair = keypair,
+ source_cert = child,
+ serial = serial,
+ notAfter = notAfter,
+ now = now)
- f = open(certfile, "w")
- f.write(x.get_PEM())
- f.close()
+ with open(serial_file, "w") as f:
+ f.write("%02x\n" % (serial + 1))
+
+ with open(certfile, "w") as f:
+ f.write(x.get_PEM())
logger.debug("Cross certified %s:", certfile)
logger.debug(" Issuer %s [%s]", x.getIssuer(), x.hAKI())
logger.debug(" Subject %s [%s]", x.getSubject(), x.hSKI())
return x
- def create_rpki_objects(self, cb):
- """
- Create RPKI engine objects for this engine.
-
- Root node of the engine tree is special, it too has a parent but
- that one is the magic self-signed micro engine.
-
- The rest of this is straightforward. There are a lot of objects
- to create, but we can do batch them all into one honking PDU, then
- issue one more PDU to set BSC EE certificates based on the PKCS
- #10 requests we get back when we tell rpkid to generate BSC keys.
- """
-
- assert not self.is_hosted
-
- selves = [self] + self.hosts
-
- for i, s in enumerate(selves):
- logger.info("Creating RPKI objects for [%d] %s", i, s.name)
-
- rpkid_pdus = []
- pubd_pdus = []
-
- for s in selves:
-
- rpkid_pdus.append(rpki.left_right.self_elt.make_pdu(
- action = "create",
- self_handle = s.name,
- crl_interval = s.crl_interval,
- regen_margin = s.regen_margin,
- bpki_cert = (s.cross_certify(s.hosted_by.name + "-TA", reverse = True)
- if s.is_hosted else
- rpki.x509.X509(Auto_file = s.name + "-SELF.cer"))))
-
- rpkid_pdus.append(rpki.left_right.bsc_elt.make_pdu(
- action = "create",
- self_handle = s.name,
- bsc_handle = "b",
- generate_keypair = True))
-
- pubd_pdus.append(rpki.publication.client_elt.make_pdu(
- action = "create",
- client_handle = s.client_handle,
- base_uri = s.sia_base,
- bpki_cert = s.cross_certify(pubd_name + "-TA", reverse = True)))
-
- rpkid_pdus.append(rpki.left_right.repository_elt.make_pdu(
- action = "create",
- self_handle = s.name,
- bsc_handle = "b",
- repository_handle = "r",
- bpki_cert = s.cross_certify(pubd_name + "-TA"),
- peer_contact_uri = "http://localhost:%d/client/%s" % (pubd_port, s.client_handle)))
-
- for k in s.kids:
- rpkid_pdus.append(rpki.left_right.child_elt.make_pdu(
- action = "create",
- self_handle = s.name,
- child_handle = k.name,
- bsc_handle = "b",
- bpki_cert = s.cross_certify(k.name + "-SELF")))
-
- if s.is_root:
- rootd_cert = s.cross_certify(rootd_name + "-TA")
- rpkid_pdus.append(rpki.left_right.parent_elt.make_pdu(
- action = "create",
- self_handle = s.name,
- parent_handle = "rootd",
- bsc_handle = "b",
- repository_handle = "r",
- sia_base = s.sia_base,
- bpki_cms_cert = rootd_cert,
- sender_name = s.name,
- recipient_name = "rootd",
- peer_contact_uri = "http://localhost:%s/" % rootd_port))
- else:
- rpkid_pdus.append(rpki.left_right.parent_elt.make_pdu(
- action = "create",
- self_handle = s.name,
- parent_handle = s.parent.name,
- bsc_handle = "b",
- repository_handle = "r",
- sia_base = s.sia_base,
- bpki_cms_cert = s.cross_certify(s.parent.name + "-SELF"),
- sender_name = s.name,
- recipient_name = s.parent.name,
- peer_contact_uri = "http://localhost:%s/up-down/%s/%s" % (s.parent.get_rpki_port(),
- s.parent.name, s.name)))
-
- def one():
- call_pubd(pubd_pdus, cb = two)
-
- def two(vals):
- self.call_rpkid(rpkid_pdus, cb = three)
-
- def three(vals):
-
- bsc_dict = dict((b.self_handle, b) for b in vals if isinstance(b, rpki.left_right.bsc_elt))
-
- bsc_pdus = []
-
- for s in selves:
- b = bsc_dict[s.name]
-
- logger.info("Issuing BSC EE cert for %s", s.name)
- cmd = (prog_openssl, "x509", "-req", "-sha256", "-extfile", s.name + "-RPKI.conf",
- "-extensions", "req_x509_ext", "-days", "30",
- "-CA", s.name + "-SELF.cer", "-CAkey", s.name + "-SELF.key", "-CAcreateserial", "-text")
- signer = subprocess.Popen(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
- signed = signer.communicate(input = b.pkcs10_request.get_PEM())
- if not signed[0]:
- logger.warning(signed[1])
- raise CouldntIssueBSCEECertificate("Couldn't issue BSC EE certificate")
- s.bsc_ee = rpki.x509.X509(PEM = signed[0])
- s.bsc_crl = rpki.x509.CRL(PEM_file = s.name + "-SELF.crl")
- logger.info("BSC EE cert for %s SKI %s", s.name, s.bsc_ee.hSKI())
-
- bsc_pdus.append(rpki.left_right.bsc_elt.make_pdu(
- action = "set",
- self_handle = s.name,
- bsc_handle = "b",
- signing_cert = s.bsc_ee,
- signing_cert_crl = s.bsc_crl))
-
- self.call_rpkid(bsc_pdus, cb = four)
-
- def four(vals):
- cb()
-
- one()
-
- def setup_yaml_leaf(self):
- """
- Generate certificates and write YAML scripts for leaf nodes.
-
- We're cheating a bit here: properly speaking, we can't generate
- issue or revoke requests without knowing the class, which is
- generated on the fly, but at the moment the test case is
- simplistic enough that the class will always be "1", so we just
- wire in that value for now.
-
- Well, ok, we just broke that assumption. Now we do something even
- nastier, just to eke a bit more life out of this kludge. This
- really needs to be rewritten, but it may require a different tool
- than testpoke.
- """
-
- if not os.path.exists(self.name + ".key"):
- logger.info("Generating RPKI key for %s", self.name)
- subprocess.check_call((prog_openssl, "genrsa", "-out", self.name + ".key", "2048" ),
- stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
- ski = rpki.x509.RSA(PEM_file = self.name + ".key").gSKI()
-
- if self.parent.is_hosted:
- parent_host = self.parent.hosted_by.name
- else:
- parent_host = self.parent.name
-
- self.cross_certify(self.parent.name + "-SELF")
- self.cross_certify(parent_host + "-TA")
-
- logger.info("Writing leaf YAML for %s", self.name)
- f = open(self.name + ".yaml", "w")
- f.write(yaml_fmt_1 % {
- "parent_name" : self.parent.name,
- "parent_host" : parent_host,
- "my_name" : self.name,
- "http_port" : self.parent.get_rpki_port(),
- "class_name" : 2 if self.parent.is_hosted else 1,
- "sia" : self.sia_base,
- "ski" : ski })
- f.close()
+last_rcynic_run = None
- def run_cron(self, cb):
+def run_rcynic():
"""
- Trigger cron run for this engine.
+ Run rcynic to see whether what was published makes sense.
"""
- logger.info("Running cron for %s", self.name)
-
- assert self.rpki_port is not None
+ logger.info("Running rcynic")
+ env = os.environ.copy()
+ env["TZ"] = ""
+ global last_rcynic_run
+ if int(time.time()) == last_rcynic_run:
+ time.sleep(1)
+ subprocess.check_call((prog_rcynic, "-c", rcynic_name + ".conf"), env = env)
+ subprocess.call(rcynic_stats, shell = True, env = env)
+ last_rcynic_run = int(time.time())
+ os.link("%s.xml" % rcynic_name, "%s.%s.xml" % (rcynic_name, last_rcynic_run))
- def done(result):
- assert result == "OK", 'Expected "OK" result from cronjob, got %r' % result
- cb()
-
- rpki.http.client(
- url = "http://localhost:%d/cronjob" % self.rpki_port,
- msg = "Run cron now, please",
- callback = done,
- errback = done)
-
- def run_yaml(self):
+def mangle_sql(filename):
"""
- Run YAML scripts for this leaf entity. Since we're not bothering
- to check the class list returned by the list command, the issue
- command may fail, so we treat failure of the list command as an
- error, but only issue a warning when issue fails.
+ Mangle an SQL file into a sequence of SQL statements.
"""
- logger.info("Running YAML for %s", self.name)
- subprocess.check_call((prog_python, prog_poke, "-y", self.name + ".yaml", "-r", "list"))
- if subprocess.call((prog_python, prog_poke, "-y", self.name + ".yaml", "-r", "issue")) != 0:
- logger.warning("YAML issue command failed for %s, continuing", self.name)
-
-def setup_bpki_cert_chain(name, ee = (), ca = ()):
- """
- Build a set of BPKI certificates.
- """
- s = "exec >/dev/null 2>&1\n"
- #s = "set -x\n"
- for kind in ("TA",) + ee + ca:
- d = { "name" : name,
- "kind" : kind,
- "ca" : "false" if kind in ee else "true",
- "openssl" : prog_openssl }
- f = open("%(name)s-%(kind)s.conf" % d, "w")
- f.write(bpki_cert_fmt_1 % d)
+ words = []
+ f = open(filename)
+ for line in f:
+ words.extend(line.partition("--")[0].split())
f.close()
- if not os.path.exists("%(name)s-%(kind)s.key" % d):
- s += bpki_cert_fmt_2 % d
- s += bpki_cert_fmt_3 % d
- d = { "name" : name, "openssl" : prog_openssl }
- s += bpki_cert_fmt_4 % d
- for kind in ee + ca:
- d["kind"] = kind
- s += bpki_cert_fmt_5 % d
- for kind in ("TA",) + ca:
- d["kind"] = kind
- s += bpki_cert_fmt_6 % d
- subprocess.check_call(s, shell = True)
-
-def setup_rootd(rpkid, rootd_yaml):
- """
- Write the config files for rootd.
- """
- rpkid.cross_certify(rootd_name + "-TA", reverse = True)
- logger.info("Writing config files for %s", rootd_name)
- d = { "rootd_name" : rootd_name,
- "rootd_port" : rootd_port,
- "rpkid_name" : rpkid.name,
- "rootd_sia" : rootd_sia,
- "rsyncd_dir" : rsyncd_dir,
- "openssl" : prog_openssl,
- "lifetime" : rootd_yaml.get("lifetime", "30d") }
- f = open(rootd_name + ".conf", "w")
- f.write(rootd_fmt_1 % d)
- f.close()
- s = "exec >/dev/null 2>&1\n"
- #s = "set -x\n"
- if not os.path.exists("root.key"):
- s += rootd_fmt_2 % d
- s += rootd_fmt_3 % d
- subprocess.check_call(s, shell = True)
-
-def setup_rcynic():
- """
- Write the config file for rcynic.
- """
- logger.info("Config file for rcynic")
- d = { "rcynic_name" : rcynic_name,
- "rootd_name" : rootd_name,
- "rootd_sia" : rootd_sia }
- f = open(rcynic_name + ".conf", "w")
- f.write(rcynic_fmt_1 % d)
- f.close()
-
-def setup_rsyncd():
- """
- Write the config file for rsyncd.
- """
- logger.info("Config file for rsyncd")
- d = { "rsyncd_name" : rsyncd_name,
- "rsyncd_port" : rsyncd_port,
- "rsyncd_module" : rsyncd_module,
- "rsyncd_dir" : rsyncd_dir }
- f = open(rsyncd_name + ".conf", "w")
- f.write(rsyncd_fmt_1 % d)
- f.close()
-
-def setup_publication(pubd_sql):
- """
- Set up publication daemon.
- """
- logger.info("Configure publication daemon")
- publication_dir = os.getcwd() + "/publication"
- assert rootd_sia.startswith("rsync://")
- global rsyncd_dir
- rsyncd_dir = publication_dir + "/".join(rootd_sia.split("/")[4:])
- if not rsyncd_dir.endswith("/"):
- rsyncd_dir += "/"
- os.makedirs(rsyncd_dir + "root/trunk")
- db = MySQLdb.connect(db = pubd_db_name, user = pubd_db_user, passwd = pubd_db_pass,
- conv = sql_conversions)
- cur = db.cursor()
- db.autocommit(True)
- for sql in pubd_sql:
- try:
- cur.execute(sql)
- except Exception:
- if "DROP TABLE IF EXISTS" not in sql.upper():
- raise
- db.close()
- d = { "pubd_name" : pubd_name,
- "pubd_port" : pubd_port,
- "pubd_db_name" : pubd_db_name,
- "pubd_db_user" : pubd_db_user,
- "pubd_db_pass" : pubd_db_pass,
- "pubd_dir" : rsyncd_dir }
- f = open(pubd_name + ".conf", "w")
- f.write(pubd_fmt_1 % d)
- f.close()
- global pubd_ta
- global pubd_irbe_key
- global pubd_irbe_cert
- global pubd_pubd_cert
- pubd_ta = rpki.x509.X509(Auto_file = pubd_name + "-TA.cer")
- pubd_irbe_key = rpki.x509.RSA( Auto_file = pubd_name + "-IRBE.key")
- pubd_irbe_cert = rpki.x509.X509(Auto_file = pubd_name + "-IRBE.cer")
- pubd_pubd_cert = rpki.x509.X509(Auto_file = pubd_name + "-PUBD.cer")
-
-def call_pubd(pdus, cb):
- """
- Send a publication message to publication daemon and return the
- response.
- """
- logger.info("Calling pubd")
- q_msg = rpki.publication.msg.query(*pdus)
- q_cms = rpki.publication.cms_msg()
- q_der = q_cms.wrap(q_msg, pubd_irbe_key, pubd_irbe_cert)
- q_url = "http://localhost:%d/control" % pubd_port
-
- logger.debug(q_cms.pretty_print_content())
-
- def call_pubd_cb(r_der):
- global pubd_last_cms_time
- r_cms = rpki.publication.cms_msg(DER = r_der)
- r_msg = r_cms.unwrap((pubd_ta, pubd_pubd_cert))
- pubd_last_cms_time = r_cms.check_replay(pubd_last_cms_time, q_url)
- logger.debug(r_cms.pretty_print_content())
- assert r_msg.is_reply
- for r_pdu in r_msg:
- assert not isinstance(r_pdu, rpki.publication.report_error_elt)
- cb(r_msg)
-
- def call_pubd_eb(e):
- logger.exception("Problem calling pubd")
-
- rpki.http.client(
- url = q_url,
- msg = q_der,
- callback = call_pubd_cb,
- errback = call_pubd_eb)
-
-def set_pubd_crl(cb):
- """
- Whack publication daemon's bpki_crl. This must be configured before
- publication daemon starts talking to its clients, and must be
- updated whenever we update the CRL.
- """
- logger.info("Setting pubd's BPKI CRL")
- crl = rpki.x509.CRL(Auto_file = pubd_name + "-TA.crl")
- call_pubd([rpki.publication.config_elt.make_pdu(action = "set", bpki_crl = crl)], cb = lambda ignored: cb())
-
-last_rcynic_run = None
-
-def run_rcynic():
- """
- Run rcynic to see whether what was published makes sense.
- """
- logger.info("Running rcynic")
- env = os.environ.copy()
- env["TZ"] = ""
- global last_rcynic_run
- if int(time.time()) == last_rcynic_run:
- time.sleep(1)
- subprocess.check_call((prog_rcynic, "-c", rcynic_name + ".conf"), env = env)
- subprocess.call(rcynic_stats, shell = True, env = env)
- last_rcynic_run = int(time.time())
- os.link("%s.xml" % rcynic_name, "%s.%s.xml" % (rcynic_name, last_rcynic_run))
-
-def mangle_sql(filename):
- """
- Mangle an SQL file into a sequence of SQL statements.
- """
- words = []
- f = open(filename)
- for line in f:
- words.extend(line.partition("--")[0].split())
- f.close()
- return " ".join(words).strip(";").split(";")
+ return " ".join(words).strip(";").split(";")
bpki_cert_fmt_1 = '''\
[req]
@@ -1432,88 +1448,57 @@ bpki_cert_fmt_6 = ''' && \
-config %(name)s-%(kind)s.conf \
'''
-yaml_fmt_1 = '''---
-version: 1
-posturl: http://localhost:%(http_port)s/up-down/%(parent_name)s/%(my_name)s
-recipient-id: "%(parent_name)s"
-sender-id: "%(my_name)s"
-
-cms-cert-file: %(my_name)s-RPKI.cer
-cms-key-file: %(my_name)s-RPKI.key
-cms-ca-cert-file: %(my_name)s-TA.cer
-cms-crl-file: %(my_name)s-TA.crl
-cms-ca-certs-file:
- - %(my_name)s-TA-%(parent_name)s-SELF.cer
-
-ssl-cert-file: %(my_name)s-RPKI.cer
-ssl-key-file: %(my_name)s-RPKI.key
-ssl-ca-cert-file: %(my_name)s-TA.cer
-ssl-ca-certs-file:
- - %(my_name)s-TA-%(parent_host)s-TA.cer
-
-# We're cheating here by hardwiring the class name
-
-requests:
- list:
- type: list
- issue:
- type: issue
- class: %(class_name)s
- sia:
- - %(sia)s
- cert-request-key-file: %(my_name)s.key
- revoke:
- type: revoke
- class: %(class_name)s
- ski: %(ski)s
-'''
-
conf_fmt_1 = '''\
[irdbd]
-startup-message = This is %(my_name)s irdbd
+startup-message = This is %(my_name)s irdbd
-sql-database = %(irdb_db_name)s
-sql-username = irdb
-sql-password = %(irdb_db_pass)s
-bpki-ta = %(my_name)s-TA.cer
-rpkid-cert = %(my_name)s-RPKI.cer
-irdbd-cert = %(my_name)s-IRDB.cer
-irdbd-key = %(my_name)s-IRDB.key
-http-url = http://localhost:%(irdb_port)d/
-enable_tracebacks = yes
+sql-database = %(irdb_db_name)s
+sql-username = irdb
+sql-password = %(irdb_db_pass)s
+bpki-ta = %(my_name)s-TA.cer
+rpkid-cert = %(my_name)s-RPKI.cer
+irdbd-cert = %(my_name)s-IRDB.cer
+irdbd-key = %(my_name)s-IRDB.key
+http-url = http://localhost:%(irdb_port)d/
+enable_tracebacks = yes
[irbe_cli]
-rpkid-bpki-ta = %(my_name)s-TA.cer
-rpkid-cert = %(my_name)s-RPKI.cer
-rpkid-irbe-cert = %(my_name)s-IRBE.cer
-rpkid-irbe-key = %(my_name)s-IRBE.key
-rpkid-url = http://localhost:%(rpki_port)d/left-right
-enable_tracebacks = yes
+rpkid-bpki-ta = %(my_name)s-TA.cer
+rpkid-cert = %(my_name)s-RPKI.cer
+rpkid-irbe-cert = %(my_name)s-IRBE.cer
+rpkid-irbe-key = %(my_name)s-IRBE.key
+rpkid-url = http://localhost:%(rpki_port)d/left-right
+enable_tracebacks = yes
[rpkid]
-startup-message = This is %(my_name)s rpkid
+startup-message = This is %(my_name)s rpkid
-sql-database = %(rpki_db_name)s
-sql-username = rpki
-sql-password = %(rpki_db_pass)s
+sql-database = %(rpki_db_name)s
+sql-username = rpki
+sql-password = %(rpki_db_pass)s
-bpki-ta = %(my_name)s-TA.cer
-rpkid-key = %(my_name)s-RPKI.key
-rpkid-cert = %(my_name)s-RPKI.cer
-irdb-cert = %(my_name)s-IRDB.cer
-irbe-cert = %(my_name)s-IRBE.cer
+bpki-ta = %(my_name)s-TA.cer
+rpkid-key = %(my_name)s-RPKI.key
+rpkid-cert = %(my_name)s-RPKI.cer
+irdb-cert = %(my_name)s-IRDB.cer
+irbe-cert = %(my_name)s-IRBE.cer
-irdb-url = http://localhost:%(irdb_port)d/
+irdb-url = http://localhost:%(irdb_port)d/
+
+server-host = localhost
+server-port = %(rpki_port)d
-server-host = localhost
-server-port = %(rpki_port)d
+use-internal-cron = false
+enable_tracebacks = yes
-use-internal-cron = false
-enable_tracebacks = yes
+[myrpki]
+start_rpkid = yes
+start_irdbd = yes
+start_pubd = no
'''
rootd_fmt_1 = '''\
@@ -1525,24 +1510,28 @@ rootd-bpki-cert = %(rootd_name)s-RPKI.cer
rootd-bpki-key = %(rootd_name)s-RPKI.key
rootd-bpki-crl = %(rootd_name)s-TA.crl
child-bpki-cert = %(rootd_name)s-TA-%(rpkid_name)s-SELF.cer
+pubd-bpki-cert = %(rootd_name)s-TA-%(pubd_name)s-TA.cer
server-port = %(rootd_port)s
-rpki-root-dir = %(rsyncd_dir)sroot
-rpki-base-uri = %(rootd_sia)sroot/
-rpki-root-cert-uri = %(rootd_sia)sroot.cer
+rpki-class-name = trunk
-rpki-root-key = root.key
-rpki-root-cert = root.cer
+pubd-contact-uri = http://localhost:%(pubd_port)d/client/%(rootd_handle)s
+
+rpki-root-cert-file = root.cer
+rpki-root-cert-uri = %(rootd_sia)sroot.cer
+rpki-root-key-file = root.key
-rpki-subject-pkcs10 = %(rootd_name)s.subject.pkcs10
+rpki-subject-cert-file = trunk.cer
+rpki-subject-cert-uri = %(rootd_sia)sroot/trunk.cer
+rpki-subject-pkcs10-file= trunk.p10
rpki-subject-lifetime = %(lifetime)s
-rpki-root-crl = root.crl
-rpki-root-manifest = root.mft
+rpki-root-crl-file = root.crl
+rpki-root-crl-uri = %(rootd_sia)sroot/root.crl
-rpki-class-name = trunk
-rpki-subject-cert = trunk.cer
+rpki-root-manifest-file = root.mft
+rpki-root-manifest-uri = %(rootd_sia)sroot/root.mft
include-bpki-crl = yes
enable_tracebacks = yes
@@ -1579,7 +1568,7 @@ certificatePolicies = critical, @rpki_certificate_policy
[rpki_certificate_policy]
-policyIdentifier = 1.3.6.1.5.5.7.14.2
+policyIdentifier = 1.3.6.1.5.5.7.14.2
'''
rootd_fmt_2 = '''\
@@ -1602,8 +1591,7 @@ awk '!/-----(BEGIN|END)/' >>%(rootd_name)s.tal &&
-outform DER \
-extfile %(rootd_name)s.conf \
-extensions req_x509_rpki_ext \
- -signkey root.key &&
-ln -f root.cer %(rsyncd_dir)s
+ -signkey root.key
'''
rcynic_fmt_1 = '''\
@@ -1636,6 +1624,7 @@ sql-database = %(pubd_db_name)s
sql-username = %(pubd_db_user)s
sql-password = %(pubd_db_pass)s
bpki-ta = %(pubd_name)s-TA.cer
+pubd-crl = %(pubd_name)s-TA.crl
pubd-cert = %(pubd_name)s-PUBD.cer
pubd-key = %(pubd_name)s-PUBD.key
irbe-cert = %(pubd_name)s-IRBE.cer
@@ -1643,6 +1632,17 @@ server-host = localhost
server-port = %(pubd_port)d
publication-base = %(pubd_dir)s
enable_tracebacks = yes
+
+[irdbd]
+
+sql-database = %(irdb_db_name)s
+sql-username = irdb
+sql-password = %(irdb_db_pass)s
+
+[myrpki]
+start_rpkid = no
+start_irdbd = no
+start_pubd = yes
'''
main()
diff --git a/ca/tests/sql-cleaner.py b/ca/tests/sql-cleaner.py
index ca88d456..c518b77b 100644
--- a/ca/tests/sql-cleaner.py
+++ b/ca/tests/sql-cleaner.py
@@ -19,43 +19,33 @@
"""
import rpki.config
-import rpki.sql_schemas
from rpki.mysql_import import MySQLdb
-cfg = rpki.config.parser(None, "yamltest", allow_missing = True)
+cfg = rpki.config.parser(section = "yamltest", allow_missing = True)
for name in ("rpkid", "irdbd", "pubd"):
- username = cfg.get("%s_sql_username" % name, name[:4])
- password = cfg.get("%s_sql_password" % name, "fnord")
+ username = cfg.get("%s_sql_username" % name, name[:4])
+ password = cfg.get("%s_sql_password" % name, "fnord")
- schema = []
- for line in getattr(rpki.sql_schemas, name, "").splitlines():
- schema.extend(line.partition("--")[0].split())
- schema = " ".join(schema).strip(";").split(";")
- schema = [statement.strip() for statement in schema if statement and "DROP TABLE" not in statement]
+ db = MySQLdb.connect(user = username, passwd = password)
+ cur = db.cursor()
- db = MySQLdb.connect(user = username, passwd = password)
- cur = db.cursor()
+ cur.execute("SHOW DATABASES")
- cur.execute("SHOW DATABASES")
+ databases = [r[0] for r in cur.fetchall() if r[0][:4] == name[:4] and r[0][4:].isdigit()]
- databases = [r[0] for r in cur.fetchall() if r[0][:4] == name[:4] and r[0][4:].isdigit()]
+ for database in databases:
- for database in databases:
+ cur.execute("USE " + database)
- cur.execute("USE " + database)
+ cur.execute("SHOW TABLES")
+ tables = [r[0] for r in cur.fetchall()]
- cur.execute("SHOW TABLES")
- tables = [r[0] for r in cur.fetchall()]
+ cur.execute("SET foreign_key_checks = 0")
+ for table in tables:
+ cur.execute("DROP TABLE %s" % table)
+ cur.execute("SET foreign_key_checks = 1")
- cur.execute("SET foreign_key_checks = 0")
- for table in tables:
- cur.execute("DROP TABLE %s" % table)
- cur.execute("SET foreign_key_checks = 1")
-
- for statement in schema:
- cur.execute(statement)
-
- cur.close()
- db.close()
+ cur.close()
+ db.close()
diff --git a/ca/tests/sql-dumper.py b/ca/tests/sql-dumper.py
index 19cc1b34..af24f2d4 100644
--- a/ca/tests/sql-dumper.py
+++ b/ca/tests/sql-dumper.py
@@ -22,22 +22,22 @@ import subprocess
import rpki.config
from rpki.mysql_import import MySQLdb
-cfg = rpki.config.parser(None, "yamltest", allow_missing = True)
+cfg = rpki.config.parser(section = "yamltest", allow_missing = True)
for name in ("rpkid", "irdbd", "pubd"):
- username = cfg.get("%s_sql_username" % name, name[:4])
- password = cfg.get("%s_sql_password" % name, "fnord")
+ username = cfg.get("%s_sql_username" % name, name[:4])
+ password = cfg.get("%s_sql_password" % name, "fnord")
- cmd = ["mysqldump", "-u", username, "-p" + password, "--databases"]
+ cmd = ["mysqldump", "-u", username, "-p" + password, "--databases"]
- db = MySQLdb.connect(user = username, passwd = password)
- cur = db.cursor()
+ db = MySQLdb.connect(user = username, passwd = password)
+ cur = db.cursor()
- cur.execute("SHOW DATABASES")
- cmd.extend(r[0] for r in cur.fetchall() if r[0][:4] == name[:4] and r[0][4:].isdigit())
+ cur.execute("SHOW DATABASES")
+ cmd.extend(r[0] for r in cur.fetchall() if r[0][:4] == name[:4] and r[0][4:].isdigit())
- cur.close()
- db.close()
+ cur.close()
+ db.close()
- subprocess.check_call(cmd, stdout = open("backup.%s.sql" % name, "w"))
+ subprocess.check_call(cmd, stdout = open("backup.%s.sql" % name, "w"))
diff --git a/ca/tests/test-rrdp.py b/ca/tests/test-rrdp.py
new file mode 100755
index 00000000..97797444
--- /dev/null
+++ b/ca/tests/test-rrdp.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+# $Id$
+#
+# Copyright (C) 2013 Dragon Research Labs ("DRL")
+#
+# 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 DRL DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL DRL 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.
+
+"""
+Proof-of-concept test driver for RRDP code. Still fairly kludgy in places.
+"""
+
+import os
+import sys
+import glob
+import time
+import signal
+import textwrap
+import argparse
+import subprocess
+
+parser = argparse.ArgumentParser(description = __doc__)
+parser.add_argument("--use-smoketest", action = "store_true")
+parser.add_argument("--yaml-file", default = "smoketest.2.yaml")
+parser.add_argument("--delay", type = int, default = 30)
+parser.add_argument("--exhaustive", action = "store_true")
+parser.add_argument("--skip-daemons", action = "store_true")
+parser.add_argument("--dry-run", action = "store_true")
+args = parser.parse_args()
+
+def log(msg):
+ sys.stdout.write(msg + "\n")
+ sys.stdout.flush()
+
+def run(*argv):
+ log("Running: " + " ".join(argv))
+ if not args.dry_run:
+ subprocess.check_call(argv)
+
+def dataglob(pattern):
+ return glob.iglob(os.path.join(("smoketest.dir" if args.use_smoketest else "yamltest.dir/RIR"), pattern))
+
+def snapshot_to_serial(fn):
+ return int(os.path.splitext(os.path.basename(fn))[0])
+
+def delta_to_serial(fn):
+ return int(os.path.splitext(os.path.basename(fn))[0])
+
+top = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..", ".."))
+
+rrdp_test_tool = os.path.join(top, "potpourri/rrdp-test-tool")
+rcynic = os.path.join(top, "rp/rcynic/rcynic")
+rcynic_text = os.path.join(top, "rp/rcynic/rcynic-text")
+
+with open("rcynic-rrdp.conf", "w") as f:
+ f.write(textwrap.dedent('''# Automatically generated for RRDP tests, do not edit.
+ [rcynic]
+ xml-summary = rcynic.xml
+ jitter = 0
+ use-links = yes
+ use-syslog = no
+ use-stderr = yes
+ log-level = log_debug
+ run-rsync = no
+ '''))
+ if args.use_smoketest:
+ f.write("trust-anchor = smoketest.dir/root.cer\n")
+ else:
+ f.write("trust-anchor = yamltest.dir/RIR/publication/RIR-root/root.cer\n")
+
+if args.skip_daemons:
+ log("--skip-daemons specified, so running neither smoketest nor yamltest")
+elif args.use_smoketest:
+ run("python", "smoketest.py", args.yaml_file)
+else:
+ run("python", "sql-cleaner.py")
+ class GotSIGUSR1(Exception):
+ pass
+ def handle_sigusr1(signum, frame):
+ raise GotSIGUSR1
+ old_sigusr1 = signal.signal(signal.SIGUSR1, handle_sigusr1)
+ cmd = ("python", "yamltest.py", args.yaml_file, "--notify-when-startup-complete", str(os.getpid()))
+ log("Running: " + " ".join(cmd))
+ yamltest = subprocess.Popen(cmd)
+ log("Waiting for SIGUSR1 from yamltest")
+ try:
+ while True:
+ signal.pause()
+ except GotSIGUSR1:
+ signal.signal(signal.SIGUSR1, old_sigusr1)
+ log("Sleeping %s" % args.delay)
+ time.sleep(args.delay)
+ yamltest.terminate()
+
+snapshots = dict((snapshot_to_serial(fn), fn) for fn in dataglob("rrdp-publication/*/snapshot/*.xml"))
+deltas = dict((delta_to_serial(fn), fn) for fn in dataglob("rrdp-publication/*/deltas/*.xml"))
+
+for snapshot in sorted(snapshots):
+
+ time.sleep(1)
+ run("rm", "-rf", "rcynic-data")
+ run(rrdp_test_tool, snapshots[snapshot])
+ run(rcynic, "-c", "rcynic-rrdp.conf")
+ run(rcynic_text, "rcynic.xml")
+
+ for delta in sorted(deltas):
+ if delta > snapshot:
+ time.sleep(1)
+ run(rrdp_test_tool, deltas[delta])
+ run(rcynic, "-c", "rcynic-rrdp.conf")
+ run(rcynic_text, "rcynic.xml")
+
+ if not args.exhaustive:
+ break
diff --git a/ca/tests/testpoke.py b/ca/tests/testpoke.py
index c28ed397..60cc5690 100644
--- a/ca/tests/testpoke.py
+++ b/ca/tests/testpoke.py
@@ -58,80 +58,80 @@ yaml_data = yaml.load(args.yaml)
yaml_cmd = args.request
if yaml_cmd is None and len(yaml_data["requests"]) == 1:
- yaml_cmd = yaml_data["requests"].keys()[0]
+ yaml_cmd = yaml_data["requests"].keys()[0]
yaml_req = yaml_data["requests"][yaml_cmd]
def get_PEM(name, cls, y = yaml_data):
- if name in y:
- return cls(PEM = y[name])
- if name + "-file" in y:
- return cls(PEM_file = y[name + "-file"])
- return None
+ if name in y:
+ return cls(PEM = y[name])
+ if name + "-file" in y:
+ return cls(PEM_file = y[name + "-file"])
+ return None
def get_PEM_chain(name, cert = None):
- chain = []
- if cert is not None:
- chain.append(cert)
- if name in yaml_data:
- chain.extend([rpki.x509.X509(PEM = x) for x in yaml_data[name]])
- elif name + "-file" in yaml_data:
- chain.extend([rpki.x509.X509(PEM_file = x) for x in yaml_data[name + "-file"]])
- return chain
+ chain = []
+ if cert is not None:
+ chain.append(cert)
+ if name in yaml_data:
+ chain.extend(rpki.x509.X509(PEM = x) for x in yaml_data[name])
+ elif name + "-file" in yaml_data:
+ chain.extend(rpki.x509.X509(PEM_file = x) for x in yaml_data[name + "-file"])
+ return chain
def query_up_down(q_pdu):
- q_msg = rpki.up_down.message_pdu.make_query(
- payload = q_pdu,
- sender = yaml_data["sender-id"],
- recipient = yaml_data["recipient-id"])
- q_der = rpki.up_down.cms_msg().wrap(q_msg, cms_key, cms_certs, cms_crl)
-
- def done(r_der):
- global last_cms_timestamp
- r_cms = rpki.up_down.cms_msg(DER = r_der)
- r_msg = r_cms.unwrap([cms_ta] + cms_ca_certs)
- last_cms_timestamp = r_cms.check_replay(last_cms_timestamp)
- print r_cms.pretty_print_content()
- try:
- r_msg.payload.check_response()
- except (rpki.async.ExitNow, SystemExit):
- raise
- except Exception, e:
- fail(e)
-
- rpki.http.want_persistent_client = False
-
- rpki.http.client(
- msg = q_der,
- url = yaml_data["posturl"],
- callback = done,
- errback = fail,
- content_type = rpki.up_down.content_type)
+ q_msg = rpki.up_down.message_pdu.make_query(
+ payload = q_pdu,
+ sender = yaml_data["sender-id"],
+ recipient = yaml_data["recipient-id"])
+ q_der = rpki.up_down.cms_msg_saxify().wrap(q_msg, cms_key, cms_certs, cms_crl)
+
+ def done(r_der):
+ global last_cms_timestamp
+ r_cms = rpki.up_down.cms_msg_saxify(DER = r_der)
+ r_msg = r_cms.unwrap([cms_ta] + cms_ca_certs)
+ last_cms_timestamp = r_cms.check_replay(last_cms_timestamp)
+ print r_cms.pretty_print_content()
+ try:
+ r_msg.payload.check_response()
+ except (rpki.async.ExitNow, SystemExit):
+ raise
+ except Exception, e:
+ fail(e)
+
+ rpki.http.want_persistent_client = False
+
+ rpki.http.client(
+ msg = q_der,
+ url = yaml_data["posturl"],
+ callback = done,
+ errback = fail,
+ content_type = rpki.up_down.content_type)
def do_list():
- query_up_down(rpki.up_down.list_pdu())
+ query_up_down(rpki.up_down.list_pdu())
def do_issue():
- q_pdu = rpki.up_down.issue_pdu()
- req_key = get_PEM("cert-request-key", rpki.x509.RSA, yaml_req) or cms_key
- q_pdu.class_name = yaml_req["class"]
- q_pdu.pkcs10 = rpki.x509.PKCS10.create(
- keypair = req_key,
- is_ca = True,
- caRepository = yaml_req["sia"][0],
- rpkiManifest = yaml_req["sia"][0] + req_key.gSKI() + ".mft")
- query_up_down(q_pdu)
+ q_pdu = rpki.up_down.issue_pdu()
+ req_key = get_PEM("cert-request-key", rpki.x509.RSA, yaml_req) or cms_key
+ q_pdu.class_name = yaml_req["class"]
+ q_pdu.pkcs10 = rpki.x509.PKCS10.create(
+ keypair = req_key,
+ is_ca = True,
+ caRepository = yaml_req["sia"][0],
+ rpkiManifest = yaml_req["sia"][0] + req_key.gSKI() + ".mft")
+ query_up_down(q_pdu)
def do_revoke():
- q_pdu = rpki.up_down.revoke_pdu()
- q_pdu.class_name = yaml_req["class"]
- q_pdu.ski = yaml_req["ski"]
- query_up_down(q_pdu)
+ q_pdu = rpki.up_down.revoke_pdu()
+ q_pdu.class_name = yaml_req["class"]
+ q_pdu.ski = yaml_req["ski"]
+ query_up_down(q_pdu)
dispatch = { "list" : do_list, "issue" : do_issue, "revoke" : do_revoke }
def fail(e): # pylint: disable=W0621
- sys.exit("Testpoke failed: %s" % e)
+ sys.exit("Testpoke failed: %s" % e)
cms_ta = get_PEM("cms-ca-cert", rpki.x509.X509)
cms_cert = get_PEM("cms-cert", rpki.x509.X509)
@@ -143,7 +143,7 @@ cms_ca_certs = get_PEM_chain("cms-ca-certs")
last_cms_timestamp = None
try:
- dispatch[yaml_req["type"]]()
- rpki.async.event_loop()
+ dispatch[yaml_req["type"]]()
+ rpki.async.event_loop()
except Exception, e:
- fail(e)
+ fail(e)
diff --git a/ca/tests/xml-parse-test.py b/ca/tests/xml-parse-test.py
index 5ea25492..f24d5683 100644
--- a/ca/tests/xml-parse-test.py
+++ b/ca/tests/xml-parse-test.py
@@ -28,92 +28,102 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-import glob, lxml.etree, lxml.sax
-import rpki.up_down, rpki.left_right, rpki.publication, rpki.relaxng
+import glob
+import lxml.etree
+import rpki.up_down
+import rpki.left_right
+import rpki.publication
+import rpki.publication_control
+import rpki.relaxng
verbose = False
-def test(fileglob, rng, sax_handler, encoding, tester = None):
- files = glob.glob(fileglob)
- files.sort()
- for f in files:
- print "<!--", f, "-->"
- handler = sax_handler()
- elt_in = lxml.etree.parse(f).getroot()
- if verbose:
- print "<!-- Input -->"
- print lxml.etree.tostring(elt_in, pretty_print = True, encoding = encoding, xml_declaration = True)
- rng.assertValid(elt_in)
- lxml.sax.saxify(elt_in, handler)
- elt_out = handler.result.toXML()
- if verbose:
- print "<!-- Output -->"
- print lxml.etree.tostring(elt_out, pretty_print = True, encoding = encoding, xml_declaration = True)
- rng.assertValid(elt_out)
- if tester:
- tester(elt_in, elt_out, handler.result)
- if verbose:
- print
+def test(fileglob, rng, parser, encoding, tester = None):
+ files = glob.glob(fileglob)
+ files.sort()
+ for f in files:
+ print "<!--", f, "-->"
+ elt_in = lxml.etree.parse(f).getroot()
+ if verbose:
+ print "<!-- Input -->"
+ print lxml.etree.tostring(elt_in, pretty_print = True, encoding = encoding, xml_declaration = True)
+ rng.assertValid(elt_in)
+ parsed = parser.fromXML(elt_in)
+ elt_out = parsed.toXML()
+ if verbose:
+ print "<!-- Output -->"
+ print lxml.etree.tostring(elt_out, pretty_print = True, encoding = encoding, xml_declaration = True)
+ rng.assertValid(elt_out)
+ if tester:
+ tester(elt_in, elt_out, parsed)
+ if verbose:
+ print
def pprint(pairs):
- if verbose:
- for thing, name in pairs:
- if thing is not None:
- print "[%s]" % name
- print thing.get_POW().pprint()
+ if verbose:
+ for thing, name in pairs:
+ if thing is not None:
+ print "[%s]" % name
+ print thing.get_POW().pprint()
def ud_tester(elt_in, elt_out, msg):
- assert isinstance(msg, rpki.up_down.message_pdu)
- if isinstance(msg.payload, rpki.up_down.list_response_pdu):
- for c in msg.payload.classes:
- pprint([(c.certs[i].cert, ("%s certificate #%d" % (c.class_name, i))) for i in xrange(len(c.certs))] + [(c.issuer, ("%s issuer" % c.class_name))])
+ assert isinstance(msg, rpki.up_down.message_pdu)
+ if isinstance(msg.payload, rpki.up_down.list_response_pdu):
+ for c in msg.payload.classes:
+ pprint([(c.certs[i].cert, ("%s certificate #%d" % (c.class_name, i))) for i in xrange(len(c.certs))] + [(c.issuer, ("%s issuer" % c.class_name))])
def lr_tester(elt_in, elt_out, msg):
- assert isinstance(msg, rpki.left_right.msg)
- for obj in msg:
- if isinstance(obj, rpki.left_right.self_elt):
- pprint(((obj.bpki_cert, "BPKI cert"),
- (obj.bpki_glue, "BPKI glue")))
- if isinstance(obj, rpki.left_right.bsc_elt):
- pprint(((obj.signing_cert, "Signing certificate"),
- (obj.signing_cert_crl, "Signing certificate CRL")))
- # (obj.pkcs10_request, "PKCS #10 request")
- if isinstance(obj, rpki.left_right.parent_elt):
- pprint(((obj.bpki_cms_cert, "CMS certificate"),
- (obj.bpki_cms_glue, "CMS glue")))
- if isinstance(obj, (rpki.left_right.child_elt, rpki.left_right.repository_elt)):
- pprint(((obj.bpki_cert, "Certificate"),
- (obj.bpki_glue, "Glue")))
+ assert isinstance(msg, rpki.left_right.msg)
+ for obj in msg:
+ if isinstance(obj, rpki.left_right.self_elt):
+ pprint(((obj.bpki_cert, "BPKI cert"),
+ (obj.bpki_glue, "BPKI glue")))
+ if isinstance(obj, rpki.left_right.bsc_elt):
+ pprint(((obj.signing_cert, "Signing certificate"),
+ (obj.signing_cert_crl, "Signing certificate CRL")))
+ # (obj.pkcs10_request, "PKCS #10 request")
+ if isinstance(obj, rpki.left_right.parent_elt):
+ pprint(((obj.bpki_cert, "BPKI certificate"),
+ (obj.bpki_glue, "BPKI glue")))
+ if isinstance(obj, (rpki.left_right.child_elt, rpki.left_right.repository_elt)):
+ pprint(((obj.bpki_cert, "BPKI certificate"),
+ (obj.bpki_glue, "BPKI glue")))
def pp_tester(elt_in, elt_out, msg):
- assert isinstance(msg, rpki.publication.msg)
- for obj in msg:
- if isinstance(obj, rpki.publication.client_elt):
- pprint(((obj.bpki_cert, "BPKI cert"),
- (obj.bpki_glue, "BPKI glue")))
- if isinstance(obj, rpki.publication.certificate_elt):
- pprint(((obj.payload, "RPKI cert"),))
- if isinstance(obj, rpki.publication.crl_elt):
- pprint(((obj.payload, "RPKI CRL"),))
- if isinstance(obj, rpki.publication.manifest_elt):
- pprint(((obj.payload, "RPKI manifest"),))
- if isinstance(obj, rpki.publication.roa_elt):
- pprint(((obj.payload, "ROA"),))
+ assert isinstance(msg, rpki.publication.msg)
+ for obj in msg:
+ if isinstance(obj, rpki.publication.publish_elt):
+ pprint(((obj.payload, "Publish object"),))
+ if isinstance(obj, rpki.publication.withdraw_elt):
+ pprint(((None, "Withdraw object"),))
+
+def pc_tester(elt_in, elt_out, msg):
+ assert isinstance(msg, rpki.publication_control.msg)
+ for obj in msg:
+ if isinstance(obj, rpki.publication_control.client_elt):
+ pprint(((obj.bpki_cert, "BPKI cert"),
+ (obj.bpki_glue, "BPKI glue")))
test(fileglob = "up-down-protocol-samples/*.xml",
rng = rpki.relaxng.up_down,
- sax_handler = rpki.up_down.sax_handler,
+ parser = rpki.up_down.msg,
encoding = "utf-8",
tester = ud_tester)
test(fileglob = "left-right-protocol-samples/*.xml",
rng = rpki.relaxng.left_right,
- sax_handler = rpki.left_right.sax_handler,
+ parser = rpki.left_right.msg,
encoding = "us-ascii",
tester = lr_tester)
test(fileglob = "publication-protocol-samples/*.xml",
rng = rpki.relaxng.publication,
- sax_handler = rpki.publication.sax_handler,
+ parser = rpki.publication.msg,
encoding = "us-ascii",
tester = pp_tester)
+
+test(fileglob = "publication-control-protocol-samples/*.xml",
+ rng = rpki.relaxng.publication_control,
+ parser = rpki.publication_control.msg,
+ encoding = "us-ascii",
+ tester = pc_tester)
diff --git a/ca/tests/yamlconf.py b/ca/tests/yamlconf.py
index f1073c92..52c4da26 100644
--- a/ca/tests/yamlconf.py
+++ b/ca/tests/yamlconf.py
@@ -75,786 +75,787 @@ config_overrides = {
"pubd_sql_username" : "pubd", "pubd_sql_password" : "fnord" }
def cleanpath(*names):
- return os.path.normpath(os.path.join(*names))
+ return os.path.normpath(os.path.join(*names))
this_dir = os.getcwd()
test_dir = None
rpki_conf = None
class roa_request(object):
- """
- Representation of a ROA request.
- """
-
- def __init__(self, asn, ipv4, ipv6):
- self.asn = asn
- self.v4 = rpki.resource_set.roa_prefix_set_ipv4("".join(ipv4.split())) if ipv4 else None
- self.v6 = rpki.resource_set.roa_prefix_set_ipv6("".join(ipv6.split())) if ipv6 else None
-
- def __eq__(self, other):
- return self.asn == other.asn and self.v4 == other.v4 and self.v6 == other.v6
-
- def __hash__(self):
- v4 = tuple(self.v4) if self.v4 is not None else None
- v6 = tuple(self.v6) if self.v6 is not None else None
- return self.asn.__hash__() + v4.__hash__() + v6.__hash__()
-
- def __str__(self):
- if self.v4 and self.v6:
- return "%s: %s,%s" % (self.asn, self.v4, self.v6)
- else:
- return "%s: %s" % (self.asn, self.v4 or self.v6)
+ """
+ Representation of a ROA request.
+ """
- @classmethod
- def parse(cls, y):
- return cls(y.get("asn"), y.get("ipv4"), y.get("ipv6"))
+ def __init__(self, asn, ipv4, ipv6):
+ self.asn = asn
+ self.v4 = rpki.resource_set.roa_prefix_set_ipv4("".join(ipv4.split())) if ipv4 else None
+ self.v6 = rpki.resource_set.roa_prefix_set_ipv6("".join(ipv6.split())) if ipv6 else None
-class router_cert(object):
- """
- Representation for a router_cert object.
- """
+ def __eq__(self, other):
+ return self.asn == other.asn and self.v4 == other.v4 and self.v6 == other.v6
- _ecparams = None
+ def __hash__(self):
+ v4 = tuple(self.v4) if self.v4 is not None else None
+ v6 = tuple(self.v6) if self.v6 is not None else None
+ return self.asn.__hash__() + v4.__hash__() + v6.__hash__()
- @classmethod
- def ecparams(cls):
- if cls._ecparams is None:
- cls._ecparams = rpki.x509.KeyParams.generateEC()
- return cls._ecparams
+ def __str__(self):
+ if self.v4 and self.v6:
+ return "%s: %s,%s" % (self.asn, self.v4, self.v6)
+ else:
+ return "%s: %s" % (self.asn, self.v4 or self.v6)
- def __init__(self, asn, router_id):
- self.asn = rpki.resource_set.resource_set_as("".join(str(asn).split()))
- self.router_id = router_id
- self.keypair = rpki.x509.ECDSA.generate(self.ecparams())
- self.pkcs10 = rpki.x509.PKCS10.create(keypair = self.keypair)
- self.gski = self.pkcs10.gSKI()
+ @classmethod
+ def parse(cls, y):
+ return cls(y.get("asn"), y.get("ipv4"), y.get("ipv6"))
- def __eq__(self, other):
- return self.asn == other.asn and self.router_id == other.router_id and self.gski == other.gski
+class router_cert(object):
+ """
+ Representation for a router_cert object.
+ """
- def __hash__(self):
- return tuple(self.asn).__hash__() + self.router_id.__hash__() + self.gski.__hash__()
+ _ecparams = None
- def __str__(self):
- return "%s: %s: %s" % (self.asn, self.router_id, self.gski)
+ @classmethod
+ def ecparams(cls):
+ if cls._ecparams is None:
+ cls._ecparams = rpki.x509.KeyParams.generateEC()
+ return cls._ecparams
- @classmethod
- def parse(cls, yaml):
- return cls(yaml.get("asn"), yaml.get("router_id"))
+ def __init__(self, asn, router_id):
+ self.asn = rpki.resource_set.resource_set_as("".join(str(asn).split()))
+ self.router_id = router_id
+ self.keypair = rpki.x509.ECDSA.generate(params = self.ecparams(), quiet = True)
+ self.pkcs10 = rpki.x509.PKCS10.create(keypair = self.keypair)
+ self.gski = self.pkcs10.gSKI()
+ def __eq__(self, other):
+ return self.asn == other.asn and self.router_id == other.router_id and self.gski == other.gski
-class allocation_db(list):
- """
- Allocation database.
- """
-
- def __init__(self, y):
- list.__init__(self)
- self.root = allocation(y, self)
- assert self.root.is_root
- if self.root.crl_interval is None:
- self.root.crl_interval = 60 * 60
- if self.root.regen_margin is None:
- self.root.regen_margin = 24 * 60 * 60
- if self.root.base.valid_until is None:
- self.root.base.valid_until = rpki.sundial.now() + rpki.sundial.timedelta(days = 2)
- for a in self:
- if a.base.valid_until is None:
- a.base.valid_until = a.parent.base.valid_until
- if a.crl_interval is None:
- a.crl_interval = a.parent.crl_interval
- if a.regen_margin is None:
- a.regen_margin = a.parent.regen_margin
- self.root.closure()
- self.map = dict((a.name, a) for a in self)
- for a in self:
- if a.is_hosted:
- a.hosted_by = self.map[a.hosted_by]
- a.hosted_by.hosts.append(a)
- assert not a.is_root and not a.hosted_by.is_hosted
-
- def dump(self):
- for a in self:
- a.dump()
+ def __hash__(self):
+ return tuple(self.asn).__hash__() + self.router_id.__hash__() + self.gski.__hash__()
+ def __str__(self):
+ return "%s: %s: %s" % (self.asn, self.router_id, self.gski)
-class allocation(object):
- """
- One entity in our allocation database. Every entity in the database
- is assumed to hold resources. Entities that don't have the
- hosted_by property run their own copies of rpkid, irdbd, and pubd.
- """
-
- base_port = 4400
- base_engine = -1
- parent = None
- crl_interval = None
- regen_margin = None
- engine = -1
- rpkid_port = 4404
- irdbd_port = 4403
- pubd_port = 4402
- rootd_port = 4401
- rsync_port = 873
-
- @classmethod
- def allocate_port(cls):
- cls.base_port += 1
- return cls.base_port
-
- @classmethod
- def allocate_engine(cls):
- cls.base_engine += 1
- return cls.base_engine
-
- def __init__(self, y, db, parent = None):
- db.append(self)
- self.name = y["name"]
- self.parent = parent
- self.kids = [allocation(k, db, self) for k in y.get("kids", ())]
- valid_until = None
- if "valid_until" in y:
- valid_until = rpki.sundial.datetime.from_datetime(y.get("valid_until"))
- if valid_until is None and "valid_for" in y:
- valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(y["valid_for"])
- self.base = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(y.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(y.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(y.get("ipv6")),
- valid_until = valid_until)
- if "crl_interval" in y:
- self.crl_interval = rpki.sundial.timedelta.parse(y["crl_interval"]).convert_to_seconds()
- if "regen_margin" in y:
- self.regen_margin = rpki.sundial.timedelta.parse(y["regen_margin"]).convert_to_seconds()
- if "ghostbusters" in y:
- self.ghostbusters = y.get("ghostbusters")
- elif "ghostbuster" in y:
- self.ghostbusters = [y.get("ghostbuster")]
- else:
- self.ghostbusters = []
- self.roa_requests = [roa_request.parse(r) for r in y.get("roa_request", ())]
- self.router_certs = [router_cert.parse(r) for r in y.get("router_cert", ())]
- for r in self.roa_requests:
- if r.v4:
- self.base.v4 |= r.v4.to_resource_set()
- if r.v6:
- self.base.v6 |= r.v6.to_resource_set()
- for r in self.router_certs:
- self.base.asn |= r.asn
- self.hosted_by = y.get("hosted_by")
- self.hosts = []
- if not self.is_hosted:
- self.engine = self.allocate_engine()
- if loopback and not self.is_hosted:
- self.rpkid_port = self.allocate_port()
- self.irdbd_port = self.allocate_port()
- if loopback and self.runs_pubd:
- self.pubd_port = self.allocate_port()
- self.rsync_port = self.allocate_port()
- if loopback and self.is_root:
- self.rootd_port = self.allocate_port()
-
- def closure(self):
- resources = self.base
- for kid in self.kids:
- resources |= kid.closure()
- self.resources = resources
- return resources
-
- @property
- def hostname(self):
- if loopback:
- return "localhost"
- elif dns_suffix:
- return self.name + "." + dns_suffix.lstrip(".")
- else:
- return self.name
+ @classmethod
+ def parse(cls, yaml):
+ return cls(yaml.get("asn"), yaml.get("router_id"))
- @property
- def rsync_server(self):
- if loopback:
- return "%s:%s" % (self.pubd.hostname, self.pubd.rsync_port)
- else:
- return self.pubd.hostname
- def dump(self):
- if not quiet:
- print str(self)
-
- def __str__(self):
- s = self.name + ":\n"
- if self.resources.asn: s += " ASNs: %s\n" % self.resources.asn
- if self.resources.v4: s += " IPv4: %s\n" % self.resources.v4
- if self.resources.v6: s += " IPv6: %s\n" % self.resources.v6
- if self.kids: s += " Kids: %s\n" % ", ".join(k.name for k in self.kids)
- if self.parent: s += " Up: %s\n" % self.parent.name
- if self.is_hosted: s += " Host: %s\n" % self.hosted_by.name
- if self.hosts: s += " Hosts: %s\n" % ", ".join(h.name for h in self.hosts)
- for r in self.roa_requests: s += " ROA: %s\n" % r
- if not self.is_hosted: s += " IPort: %s\n" % self.irdbd_port
- if self.runs_pubd: s += " PPort: %s\n" % self.pubd_port
- if not self.is_hosted: s += " RPort: %s\n" % self.rpkid_port
- if self.runs_pubd: s += " SPort: %s\n" % self.rsync_port
- if self.is_root: s += " TPort: %s\n" % self.rootd_port
- return s + " Until: %s\n" % self.resources.valid_until
-
- @property
- def is_root(self):
- return self.parent is None
-
- @property
- def is_hosted(self):
- return self.hosted_by is not None
-
- @property
- def runs_pubd(self):
- return self.is_root or not (self.is_hosted or only_one_pubd)
-
- def path(self, *names):
- return cleanpath(test_dir, self.host.name, *names)
-
- def csvout(self, fn):
- path = self.path(fn)
- if not quiet:
- print "Writing", path
- return rpki.csv_utils.csv_writer(path)
-
- def up_down_url(self):
- return "http://%s:%d/up-down/%s/%s" % (self.parent.host.hostname,
- self.parent.host.rpkid_port,
- self.parent.name,
- self.name)
-
- def dump_asns(self, fn):
- with self.csvout(fn) as f:
- for k in self.kids:
- f.writerows((k.name, a) for a in k.resources.asn)
-
- def dump_prefixes(self, fn):
- with self.csvout(fn) as f:
- for k in self.kids:
- f.writerows((k.name, p) for p in (k.resources.v4 + k.resources.v6))
-
- def dump_roas(self, fn):
- with self.csvout(fn) as f:
- for g1, r in enumerate(self.roa_requests):
- f.writerows((p, r.asn, "G%08d%08d" % (g1, g2))
- for g2, p in enumerate((r.v4 + r.v6 if r.v4 and r.v6 else r.v4 or r.v6 or ())))
-
- def dump_ghostbusters(self, fn):
- if self.ghostbusters:
- path = self.path(fn)
- if not quiet:
- print "Writing", path
- with open(path, "w") as f:
- for i, g in enumerate(self.ghostbusters):
- if i > 0:
- f.write("\n")
- f.write(g)
-
- def dump_router_certificates(self, fn):
- if self.router_certs:
- path = self.path(fn)
- if not quiet:
- print "Writing", path
- xmlns = rpki.relaxng.router_certificate.xmlns
- xml = lxml.etree.Element(xmlns + "router_certificate_requests",
- version = rpki.relaxng.router_certificate.version,
- nsmap = rpki.relaxng.router_certificate.nsmap)
- for r in self.router_certs:
- x = lxml.etree.SubElement(xml, xmlns + "router_certificate_request",
- router_id = str(r.router_id),
- asn = str(r.asn),
- valid_until = str(self.resources.valid_until))
- x.text = r.pkcs10.get_Base64()
- rpki.relaxng.router_certificate.assertValid(xml)
- lxml.etree.ElementTree(xml).write(path, pretty_print = True)
-
- @property
- def pubd(self):
- s = self
- while not s.runs_pubd:
- s = s.parent
- return s
-
- @property
- def client_handle(self):
- path = []
- s = self
- if not flat_publication:
- while not s.runs_pubd:
- path.append(s)
- s = s.parent
- path.append(s)
- return ".".join(i.name for i in reversed(path))
-
- @property
- def host(self):
- return self.hosted_by or self
-
- @property
- def publication_base_directory(self):
- if not loopback and publication_base is not None:
- return publication_base
- else:
- return self.path("publication")
+class allocation_db(list):
+ """
+ Allocation database.
+ """
+
+ def __init__(self, y):
+ list.__init__(self)
+ self.root = allocation(y, self)
+ assert self.root.is_root
+ if self.root.crl_interval is None:
+ self.root.crl_interval = 60 * 60
+ if self.root.regen_margin is None:
+ self.root.regen_margin = 24 * 60 * 60
+ if self.root.base.valid_until is None:
+ self.root.base.valid_until = rpki.sundial.now() + rpki.sundial.timedelta(days = 2)
+ for a in self:
+ if a.base.valid_until is None:
+ a.base.valid_until = a.parent.base.valid_until
+ if a.crl_interval is None:
+ a.crl_interval = a.parent.crl_interval
+ if a.regen_margin is None:
+ a.regen_margin = a.parent.regen_margin
+ self.root.closure()
+ self.map = dict((a.name, a) for a in self)
+ for a in self:
+ if a.is_hosted:
+ a.hosted_by = self.map[a.hosted_by]
+ a.hosted_by.hosts.append(a)
+ assert not a.is_root and not a.hosted_by.is_hosted
+
+ def dump(self):
+ for a in self:
+ a.dump()
- @property
- def publication_root_directory(self):
- if not loopback and publication_root is not None:
- return publication_root
- else:
- return self.path("publication.root")
-
- def dump_conf(self):
-
- r = dict(
- handle = self.name,
- run_rpkid = str(not self.is_hosted),
- run_pubd = str(self.runs_pubd),
- run_rootd = str(self.is_root),
- irdbd_sql_username = "irdb",
- rpkid_sql_username = "rpki",
- rpkid_server_host = self.hostname,
- rpkid_server_port = str(self.rpkid_port),
- irdbd_server_host = "localhost",
- irdbd_server_port = str(self.irdbd_port),
- rootd_server_port = str(self.rootd_port),
- pubd_sql_username = "pubd",
- pubd_server_host = self.pubd.hostname,
- pubd_server_port = str(self.pubd.pubd_port),
- publication_rsync_server = self.rsync_server)
-
- if loopback:
- r.update(
- irdbd_sql_database = self.irdb_name,
- rpkid_sql_database = "rpki%d" % self.engine,
- pubd_sql_database = "pubd%d" % self.engine,
- bpki_servers_directory = self.path(),
- publication_base_directory = self.publication_base_directory)
-
- r.update(config_overrides)
-
- with open(self.path("rpki.conf"), "w") as f:
- f.write("# Automatically generated, do not edit\n")
- if not quiet:
- print "Writing", f.name
-
- section = None
- for line in open(rpki_conf):
- 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 and option in r:
- line = "%s = %s\n" % (option, r[option])
- f.write(line)
-
- def dump_rsyncd(self):
- lines = []
- if self.runs_pubd:
- lines.extend((
- "# Automatically generated, do not edit",
- "port = %d" % self.rsync_port,
- "address = %s" % self.hostname,
- "log file = rsyncd.log",
- "read only = yes",
- "use chroot = no",
- "[rpki]",
- "path = %s" % self.publication_base_directory,
- "comment = RPKI test"))
- if self.is_root:
- assert self.runs_pubd
- lines.extend((
- "[root]",
- "path = %s" % self.publication_root_directory,
- "comment = RPKI test root"))
- if lines:
- with open(self.path("rsyncd.conf"), "w") as f:
- if not quiet:
- print "Writing", f.name
- f.writelines(line + "\n" for line in lines)
-
- @property
- def irdb_name(self):
- return "irdb%d" % self.host.engine
-
- @property
- def irdb(self):
- prior_name = self.zoo.handle
- return rpki.irdb.database(
- self.irdb_name,
- on_entry = lambda: self.zoo.reset_identity(self.name),
- on_exit = lambda: self.zoo.reset_identity(prior_name))
-
- def syncdb(self):
- import django.core.management
- assert not self.is_hosted
- django.core.management.call_command("syncdb",
- database = self.irdb_name,
- load_initial_data = False,
- interactive = False,
- verbosity = 0)
-
- def hire_zookeeper(self):
- assert not self.is_hosted
- self._zoo = rpki.irdb.Zookeeper(
- cfg = rpki.config.parser(self.path("rpki.conf")),
- logstream = None if quiet else sys.stdout)
-
- @property
- def zoo(self):
- return self.host._zoo
-
- def dump_root(self):
-
- assert self.is_root and not self.is_hosted
-
- root_resources = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as("0-4294967295"),
- v4 = rpki.resource_set.resource_set_ipv4("0.0.0.0/0"),
- v6 = rpki.resource_set.resource_set_ipv6("::/0"))
-
- root_key = rpki.x509.RSA.generate(quiet = True)
-
- root_uri = "rsync://%s/rpki/" % self.rsync_server
-
- root_sia = (root_uri, root_uri + "root.mft", None)
-
- root_cert = rpki.x509.X509.self_certify(
- keypair = root_key,
- subject_key = root_key.get_public(),
- serial = 1,
- sia = root_sia,
- notAfter = rpki.sundial.now() + rpki.sundial.timedelta(days = 365),
- resources = root_resources)
-
- with open(self.path("publication.root", "root.cer"), "wb") as f:
- f.write(root_cert.get_DER())
-
- with open(self.path("root.key"), "wb") as f:
- f.write(root_key.get_DER())
-
- with open(cleanpath(test_dir, "root.tal"), "w") as f:
- f.write("rsync://%s/root/root.cer\n\n%s" % (
- self.rsync_server, root_key.get_public().get_Base64()))
-
- def mkdir(self, *path):
- path = self.path(*path)
- if not quiet:
- print "Creating directory", path
- os.makedirs(path)
- def dump_sql(self):
- if not self.is_hosted:
- with open(self.path("rpkid.sql"), "w") as f:
+class allocation(object):
+ """
+ One entity in our allocation database. Every entity in the database
+ is assumed to hold resources. Entities that don't have the
+ hosted_by property run their own copies of rpkid, irdbd, and pubd.
+ """
+
+ base_port = 4400
+ base_engine = -1
+ parent = None
+ crl_interval = None
+ regen_margin = None
+ engine = -1
+ rpkid_port = 4404
+ irdbd_port = 4403
+ pubd_port = 4402
+ rootd_port = 4401
+ rsync_port = 873
+
+ @classmethod
+ def allocate_port(cls):
+ cls.base_port += 1
+ return cls.base_port
+
+ @classmethod
+ def allocate_engine(cls):
+ cls.base_engine += 1
+ return cls.base_engine
+
+ def __init__(self, y, db, parent = None):
+ db.append(self)
+ self.name = y["name"]
+ self.parent = parent
+ self.kids = [allocation(k, db, self) for k in y.get("kids", ())]
+ valid_until = None
+ if "valid_until" in y:
+ valid_until = rpki.sundial.datetime.from_datetime(y.get("valid_until"))
+ if valid_until is None and "valid_for" in y:
+ valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(y["valid_for"])
+ self.base = rpki.resource_set.resource_bag(
+ asn = rpki.resource_set.resource_set_as(y.get("asn")),
+ v4 = rpki.resource_set.resource_set_ipv4(y.get("ipv4")),
+ v6 = rpki.resource_set.resource_set_ipv6(y.get("ipv6")),
+ valid_until = valid_until)
+ if "crl_interval" in y:
+ self.crl_interval = rpki.sundial.timedelta.parse(y["crl_interval"]).convert_to_seconds()
+ if "regen_margin" in y:
+ self.regen_margin = rpki.sundial.timedelta.parse(y["regen_margin"]).convert_to_seconds()
+ if "ghostbusters" in y:
+ self.ghostbusters = y.get("ghostbusters")
+ elif "ghostbuster" in y:
+ self.ghostbusters = [y.get("ghostbuster")]
+ else:
+ self.ghostbusters = []
+ self.roa_requests = [roa_request.parse(r) for r in y.get("roa_request", ())]
+ self.router_certs = [router_cert.parse(r) for r in y.get("router_cert", ())]
+ for r in self.roa_requests:
+ if r.v4:
+ self.base.v4 |= r.v4.to_resource_set()
+ if r.v6:
+ self.base.v6 |= r.v6.to_resource_set()
+ for r in self.router_certs:
+ self.base.asn |= r.asn
+ self.hosted_by = y.get("hosted_by")
+ self.hosts = []
+ if not self.is_hosted:
+ self.engine = self.allocate_engine()
+ if loopback and not self.is_hosted:
+ self.rpkid_port = self.allocate_port()
+ self.irdbd_port = self.allocate_port()
+ if loopback and self.runs_pubd:
+ self.pubd_port = self.allocate_port()
+ self.rsync_port = self.allocate_port()
+ if loopback and self.is_root:
+ self.rootd_port = self.allocate_port()
+
+ def closure(self):
+ resources = self.base
+ for kid in self.kids:
+ resources |= kid.closure()
+ self.resources = resources
+ return resources
+
+ @property
+ def hostname(self):
+ if loopback:
+ return "localhost"
+ elif dns_suffix:
+ return self.name + "." + dns_suffix.lstrip(".")
+ else:
+ return self.name
+
+ @property
+ def rsync_server(self):
+ if loopback:
+ return "%s:%s" % (self.pubd.hostname, self.pubd.rsync_port)
+ else:
+ return self.pubd.hostname
+
+ def dump(self):
if not quiet:
- print "Writing", f.name
- f.write(rpki.sql_schemas.rpkid)
- if self.runs_pubd:
- with open(self.path("pubd.sql"), "w") as f:
+ print str(self)
+
+ def __str__(self):
+ s = self.name + ":\n"
+ if self.resources.asn: s += " ASNs: %s\n" % self.resources.asn
+ if self.resources.v4: s += " IPv4: %s\n" % self.resources.v4
+ if self.resources.v6: s += " IPv6: %s\n" % self.resources.v6
+ if self.kids: s += " Kids: %s\n" % ", ".join(k.name for k in self.kids)
+ if self.parent: s += " Up: %s\n" % self.parent.name
+ if self.is_hosted: s += " Host: %s\n" % self.hosted_by.name
+ if self.hosts: s += " Hosts: %s\n" % ", ".join(h.name for h in self.hosts)
+ for r in self.roa_requests: s += " ROA: %s\n" % r
+ if not self.is_hosted: s += " IPort: %s\n" % self.irdbd_port
+ if self.runs_pubd: s += " PPort: %s\n" % self.pubd_port
+ if not self.is_hosted: s += " RPort: %s\n" % self.rpkid_port
+ if self.runs_pubd: s += " SPort: %s\n" % self.rsync_port
+ if self.is_root: s += " TPort: %s\n" % self.rootd_port
+ return s + " Until: %s\n" % self.resources.valid_until
+
+ @property
+ def is_root(self):
+ return self.parent is None
+
+ @property
+ def is_hosted(self):
+ return self.hosted_by is not None
+
+ @property
+ def runs_pubd(self):
+ return self.is_root or not (self.is_hosted or only_one_pubd)
+
+ def path(self, *names):
+ return cleanpath(test_dir, self.host.name, *names)
+
+ def csvout(self, fn):
+ path = self.path(fn)
if not quiet:
- print "Writing", f.name
- f.write(rpki.sql_schemas.pubd)
- if not self.is_hosted:
- username = config_overrides["irdbd_sql_username"]
- password = config_overrides["irdbd_sql_password"]
- cmd = ("mysqldump", "-u", username, "-p" + password, self.irdb_name)
- with open(self.path("irdbd.sql"), "w") as f:
+ print "Writing", path
+ return rpki.csv_utils.csv_writer(path)
+
+ def up_down_url(self):
+ return "http://%s:%d/up-down/%s/%s" % (self.parent.host.hostname,
+ self.parent.host.rpkid_port,
+ self.parent.name,
+ self.name)
+
+ def dump_asns(self, fn):
+ with self.csvout(fn) as f:
+ for k in self.kids:
+ f.writerows((k.name, a) for a in k.resources.asn)
+
+ def dump_prefixes(self, fn):
+ with self.csvout(fn) as f:
+ for k in self.kids:
+ f.writerows((k.name, p) for p in (k.resources.v4 + k.resources.v6))
+
+ def dump_roas(self, fn):
+ with self.csvout(fn) as f:
+ for g1, r in enumerate(self.roa_requests):
+ f.writerows((p, r.asn, "G%08d%08d" % (g1, g2))
+ for g2, p in enumerate((r.v4 + r.v6 if r.v4 and r.v6 else r.v4 or r.v6 or ())))
+
+ def dump_ghostbusters(self, fn):
+ if self.ghostbusters:
+ path = self.path(fn)
+ if not quiet:
+ print "Writing", path
+ with open(path, "w") as f:
+ for i, g in enumerate(self.ghostbusters):
+ if i > 0:
+ f.write("\n")
+ f.write(g)
+
+ def dump_router_certificates(self, fn):
+ if self.router_certs:
+ path = self.path(fn)
+ if not quiet:
+ print "Writing", path
+ xmlns = rpki.relaxng.router_certificate.xmlns
+ xml = lxml.etree.Element(xmlns + "router_certificate_requests",
+ version = rpki.relaxng.router_certificate.version,
+ nsmap = rpki.relaxng.router_certificate.nsmap)
+ for r in self.router_certs:
+ x = lxml.etree.SubElement(xml, xmlns + "router_certificate_request",
+ router_id = str(r.router_id),
+ asn = str(r.asn),
+ valid_until = str(self.resources.valid_until))
+ x.text = r.pkcs10.get_Base64()
+ rpki.relaxng.router_certificate.assertValid(xml)
+ lxml.etree.ElementTree(xml).write(path, pretty_print = True)
+
+ @property
+ def pubd(self):
+ s = self
+ while not s.runs_pubd:
+ s = s.parent
+ return s
+
+ @property
+ def client_handle(self):
+ path = []
+ s = self
+ if not flat_publication:
+ while not s.runs_pubd:
+ path.append(s)
+ s = s.parent
+ path.append(s)
+ return ".".join(i.name for i in reversed(path))
+
+ @property
+ def host(self):
+ return self.hosted_by or self
+
+ @property
+ def publication_base_directory(self):
+ if not loopback and publication_base is not None:
+ return publication_base
+ else:
+ return self.path("publication")
+
+ @property
+ def publication_root_directory(self):
+ if not loopback and publication_root is not None:
+ return publication_root
+ else:
+ return self.path("publication.root")
+
+ def dump_conf(self):
+
+ r = dict(
+ handle = self.name,
+ run_rpkid = str(not self.is_hosted),
+ run_pubd = str(self.runs_pubd),
+ run_rootd = str(self.is_root),
+ irdbd_sql_username = "irdb",
+ rpkid_sql_username = "rpki",
+ rpkid_server_host = self.hostname,
+ rpkid_server_port = str(self.rpkid_port),
+ irdbd_server_host = "localhost",
+ irdbd_server_port = str(self.irdbd_port),
+ rootd_server_port = str(self.rootd_port),
+ pubd_sql_username = "pubd",
+ pubd_server_host = self.pubd.hostname,
+ pubd_server_port = str(self.pubd.pubd_port),
+ publication_rsync_server = self.rsync_server)
+
+ if loopback:
+ r.update(
+ irdbd_sql_database = self.irdb_name,
+ rpkid_sql_database = "rpki%d" % self.engine,
+ pubd_sql_database = "pubd%d" % self.engine,
+ bpki_servers_directory = self.path(),
+ publication_base_directory = self.publication_base_directory)
+
+ r.update(config_overrides)
+
+ with open(self.path("rpki.conf"), "w") as f:
+ f.write("# Automatically generated, do not edit\n")
+ if not quiet:
+ print "Writing", f.name
+
+ section = None
+ for line in open(rpki_conf):
+ 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 and option in r:
+ line = "%s = %s\n" % (option, r[option])
+ f.write(line)
+
+ def dump_rsyncd(self):
+ lines = []
+ if self.runs_pubd:
+ lines.extend((
+ "# Automatically generated, do not edit",
+ "port = %d" % self.rsync_port,
+ "address = %s" % self.hostname,
+ "log file = rsyncd.log",
+ "read only = yes",
+ "use chroot = no",
+ "[rpki]",
+ "path = %s" % self.publication_base_directory,
+ "comment = RPKI test"))
+ if self.is_root:
+ assert self.runs_pubd
+ lines.extend((
+ "[root]",
+ "path = %s" % self.publication_root_directory,
+ "comment = RPKI test root"))
+ if lines:
+ with open(self.path("rsyncd.conf"), "w") as f:
+ if not quiet:
+ print "Writing", f.name
+ f.writelines(line + "\n" for line in lines)
+
+ @property
+ def irdb_name(self):
+ return "irdb%d" % self.host.engine
+
+ @property
+ def irdb(self):
+ prior_name = self.zoo.handle
+ return rpki.irdb.database(
+ self.irdb_name,
+ on_entry = lambda: self.zoo.reset_identity(self.name),
+ on_exit = lambda: self.zoo.reset_identity(prior_name))
+
+ def syncdb(self):
+ import django.core.management
+ assert not self.is_hosted
+ django.core.management.call_command(
+ "syncdb",
+ verbosity = 0,
+ database = self.irdb_name,
+ migrate = True,
+ load_initial_data = False,
+ interactive = False)
+
+ def hire_zookeeper(self):
+ assert not self.is_hosted
+ self._zoo = rpki.irdb.Zookeeper(
+ cfg = rpki.config.parser(filename = self.path("rpki.conf")),
+ logstream = None if quiet else sys.stdout)
+
+ @property
+ def zoo(self):
+ return self.host._zoo
+
+ def dump_root(self):
+
+ assert self.is_root and not self.is_hosted
+
+ root_resources = rpki.resource_set.resource_bag(
+ asn = rpki.resource_set.resource_set_as("0-4294967295"),
+ v4 = rpki.resource_set.resource_set_ipv4("0.0.0.0/0"),
+ v6 = rpki.resource_set.resource_set_ipv6("::/0"))
+
+ root_key = rpki.x509.RSA.generate(quiet = True)
+
+ root_uri = "rsync://%s/rpki/" % self.rsync_server
+
+ root_sia = (root_uri, root_uri + "root.mft", None, rpki.publication.rrdp_sia_uri_kludge)
+
+ root_cert = rpki.x509.X509.self_certify(
+ keypair = root_key,
+ subject_key = root_key.get_public(),
+ serial = 1,
+ sia = root_sia,
+ notAfter = rpki.sundial.now() + rpki.sundial.timedelta(days = 365),
+ resources = root_resources)
+
+ with open(self.path("root.cer"), "wb") as f:
+ f.write(root_cert.get_DER())
+
+ with open(self.path("root.key"), "wb") as f:
+ f.write(root_key.get_DER())
+
+ with open(cleanpath(test_dir, "root.tal"), "w") as f:
+ f.write("rsync://%s/root/root.cer\n\n" % self.rsync_server)
+ f.write(root_key.get_public().get_Base64())
+
+ def mkdir(self, *path):
+ path = self.path(*path)
if not quiet:
- print "Writing", f.name
- subprocess.check_call(cmd, stdout = f)
+ print "Creating directory", path
+ os.makedirs(path)
+
+ def dump_sql(self):
+ if not self.is_hosted:
+ with open(self.path("rpkid.sql"), "w") as f:
+ if not quiet:
+ print "Writing", f.name
+ f.write(rpki.sql_schemas.rpkid)
+ if self.runs_pubd:
+ with open(self.path("pubd.sql"), "w") as f:
+ if not quiet:
+ print "Writing", f.name
+ f.write(rpki.sql_schemas.pubd)
+ if not self.is_hosted:
+ username = config_overrides["irdbd_sql_username"]
+ password = config_overrides["irdbd_sql_password"]
+ cmd = ("mysqldump", "-u", username, "-p" + password, self.irdb_name)
+ with open(self.path("irdbd.sql"), "w") as f:
+ if not quiet:
+ print "Writing", f.name
+ subprocess.check_call(cmd, stdout = f)
def pre_django_sql_setup(needed):
- username = config_overrides["irdbd_sql_username"]
- password = config_overrides["irdbd_sql_password"]
-
- # If we have the MySQL root password, just blow away and recreate
- # the required databases. Otherwise, check for missing databases,
- # then blow away all tables in the required databases. In either
- # case, we assume that the Django syncdb code will populate
- # databases as necessary, all we need to do here is provide empty
- # databases for the Django code to fill in.
+ username = config_overrides["irdbd_sql_username"]
+ password = config_overrides["irdbd_sql_password"]
+
+ # If we have the MySQL root password, just blow away and recreate
+ # the required databases. Otherwise, check for missing databases,
+ # then blow away all tables in the required databases. In either
+ # case, we assume that the Django syncdb code will populate
+ # databases as necessary, all we need to do here is provide empty
+ # databases for the Django code to fill in.
+
+ if mysql_rootpass is not None:
+ if mysql_rootpass:
+ db = MySQLdb.connect(user = mysql_rootuser, passwd = mysql_rootpass)
+ else:
+ db = MySQLdb.connect(user = mysql_rootuser)
+ cur = db.cursor()
+ for database in needed:
+ try:
+ cur.execute("DROP DATABASE IF EXISTS %s" % database)
+ except:
+ pass
+ cur.execute("CREATE DATABASE %s" % database)
+ cur.execute("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY %%s" % (
+ database, username), (password,))
- if mysql_rootpass is not None:
- if mysql_rootpass:
- db = MySQLdb.connect(user = mysql_rootuser, passwd = mysql_rootpass)
else:
- db = MySQLdb.connect(user = mysql_rootuser)
- cur = db.cursor()
- for database in needed:
- try:
- cur.execute("DROP DATABASE IF EXISTS %s" % database)
- except:
- pass
- cur.execute("CREATE DATABASE %s" % database)
- cur.execute("GRANT ALL ON %s.* TO %s@localhost IDENTIFIED BY %%s" % (
- database, username), (password,))
-
- else:
- db = MySQLdb.connect(user = username, passwd = password)
- cur = db.cursor()
- cur.execute("SHOW DATABASES")
- existing = set(r[0] for r in cur.fetchall())
- if needed - existing:
- sys.stderr.write("The following databases are missing:\n")
- for database in sorted(needed - existing):
- sys.stderr.write(" %s\n" % database)
- sys.stderr.write("Please create them manually or put MySQL root password in my config file\n")
- sys.exit("Missing databases and MySQL root password not known, can't continue")
- for database in needed:
- db.select_db(database)
- cur.execute("SHOW TABLES")
- tables = [r[0] for r in cur.fetchall()]
- cur.execute("SET foreign_key_checks = 0")
- for table in tables:
- cur.execute("DROP TABLE %s" % table)
- cur.execute("SET foreign_key_checks = 1")
-
- cur.close()
- db.commit()
- db.close()
+ db = MySQLdb.connect(user = username, passwd = password)
+ cur = db.cursor()
+ cur.execute("SHOW DATABASES")
+ existing = set(r[0] for r in cur.fetchall())
+ if needed - existing:
+ sys.stderr.write("The following databases are missing:\n")
+ for database in sorted(needed - existing):
+ sys.stderr.write(" %s\n" % database)
+ sys.stderr.write("Please create them manually or put MySQL root password in my config file\n")
+ sys.exit("Missing databases and MySQL root password not known, can't continue")
+ for database in needed:
+ db.select_db(database)
+ cur.execute("SHOW TABLES")
+ tables = [r[0] for r in cur.fetchall()]
+ cur.execute("SET foreign_key_checks = 0")
+ for table in tables:
+ cur.execute("DROP TABLE %s" % table)
+ cur.execute("SET foreign_key_checks = 1")
+
+ cur.close()
+ db.commit()
+ db.close()
class timestamp(object):
- def __init__(self, *args):
- self.count = 0
- self.start = self.tick = rpki.sundial.now()
+ def __init__(self, *args):
+ self.count = 0
+ self.start = self.tick = rpki.sundial.now()
- def __call__(self, *args):
- now = rpki.sundial.now()
- if not quiet:
- print "[Count %s last %s total %s now %s]" % (
- self.count, now - self.tick, now - self.start, now)
- self.tick = now
- self.count += 1
+ def __call__(self, *args):
+ now = rpki.sundial.now()
+ if not quiet:
+ print "[Count %s last %s total %s now %s]" % (
+ self.count, now - self.tick, now - self.start, now)
+ self.tick = now
+ self.count += 1
def main():
- global flat_publication
- global config_overrides
- global only_one_pubd
- global loopback
- global dns_suffix
- global mysql_rootuser
- global mysql_rootpass
- global yaml_file
- global test_dir
- global rpki_conf
- global publication_base
- global publication_root
- global quiet
-
- os.environ["TZ"] = "UTC"
- time.tzset()
-
- parser = argparse.ArgumentParser(description = "yamlconf")
- parser.add_argument("-c", "--config", help = "configuration file")
- parser.add_argument("--dns_suffix",
- help = "DNS suffix to add to hostnames")
- parser.add_argument("-l", "--loopback", action = "store_true",
- help = "Configure for use with yamltest on localhost")
- parser.add_argument("-f", "--flat_publication", action = "store_true",
- help = "Use flat publication model")
- parser.add_argument("-q", "--quiet", action = "store_true",
- help = "Work more quietly")
- parser.add_argument("--profile",
- help = "Filename for profile output")
- parser.add_argument("yaml_file", type = argparse.FileType("r"),
- help = "YAML file describing network to build")
- args = parser.parse_args()
-
- dns_suffix = args.dns_suffix
- loopback = args.loopback
- flat_publication = args.flat_publication
- quiet = args.quiet
- yaml_file = args.yaml_file
-
- rpki.log.init("yamlconf", argparse.Namespace(log_level = logging.DEBUG,
- log_handler = lambda: logging.StreamHandler(sys.stdout)))
-
- # Allow optional config file for this tool to override default
- # passwords: this is mostly so that I can show a complete working
- # example without publishing my own server's passwords.
-
- cfg = rpki.config.parser(args.config, "yamlconf", allow_missing = True)
- try:
- cfg.set_global_flags()
- except:
- pass
-
- # Use of "yamltest.dir" is deliberate: intent is for what we write to
- # be usable with "yamltest --skip_config".
-
- only_one_pubd = cfg.getboolean("only_one_pubd", True)
- test_dir = cfg.get("test_directory", cleanpath(this_dir, "yamltest.dir"))
- rpki_conf = cfg.get("rpki_conf", cleanpath(this_dir, "..", "examples/rpki.conf"))
- mysql_rootuser = cfg.get("mysql_rootuser", "root")
-
- try:
- mysql_rootpass = cfg.get("mysql_rootpass")
- except:
- pass
-
- try:
- publication_base = cfg.get("publication_base")
- except:
- pass
-
- try:
- publication_root = cfg.get("publication_root")
- except:
- pass
-
- for k in ("rpkid_sql_password", "irdbd_sql_password", "pubd_sql_password",
- "rpkid_sql_username", "irdbd_sql_username", "pubd_sql_username"):
- if cfg.has_option(k):
- config_overrides[k] = cfg.get(k)
-
- if args.profile:
- import cProfile
- prof = cProfile.Profile()
+ global flat_publication
+ global config_overrides
+ global only_one_pubd
+ global loopback
+ global dns_suffix
+ global mysql_rootuser
+ global mysql_rootpass
+ global yaml_file
+ global test_dir
+ global rpki_conf
+ global publication_base
+ global publication_root
+ global quiet
+
+ os.environ["TZ"] = "UTC"
+ time.tzset()
+
+ parser = argparse.ArgumentParser(description = "yamlconf")
+ parser.add_argument("-c", "--config", help = "configuration file")
+ parser.add_argument("--dns_suffix",
+ help = "DNS suffix to add to hostnames")
+ parser.add_argument("-l", "--loopback", action = "store_true",
+ help = "Configure for use with yamltest on localhost")
+ parser.add_argument("-f", "--flat_publication", action = "store_true",
+ help = "Use flat publication model")
+ parser.add_argument("-q", "--quiet", action = "store_true",
+ help = "Work more quietly")
+ parser.add_argument("--profile",
+ help = "Filename for profile output")
+ parser.add_argument("yaml_file", type = argparse.FileType("r"),
+ help = "YAML file describing network to build")
+ args = parser.parse_args()
+
+ dns_suffix = args.dns_suffix
+ loopback = args.loopback
+ flat_publication = args.flat_publication
+ quiet = args.quiet
+ yaml_file = args.yaml_file
+
+ rpki.log.init("yamlconf", argparse.Namespace(log_level = logging.DEBUG,
+ log_handler = lambda: logging.StreamHandler(sys.stdout)))
+
+ # Allow optional config file for this tool to override default
+ # passwords: this is mostly so that I can show a complete working
+ # example without publishing my own server's passwords.
+
+ cfg = rpki.config.parser(set_filename = args.config, section = "yamlconf", allow_missing = True)
try:
- prof.runcall(body)
- finally:
- prof.dump_stats(args.profile)
- if not quiet:
- print
- print "Dumped profile data to %s" % args.profile
- else:
- body()
-
-def body():
+ cfg.set_global_flags()
+ except:
+ pass
- global rpki
+ # Use of "yamltest.dir" is deliberate: intent is for what we write to
+ # be usable with "yamltest --skip_config".
- ts = timestamp()
+ only_one_pubd = cfg.getboolean("only_one_pubd", True)
+ test_dir = cfg.get("test_directory", cleanpath(this_dir, "yamltest.dir"))
+ rpki_conf = cfg.get("rpki_conf", cleanpath(this_dir, "..", "examples/rpki.conf"))
+ mysql_rootuser = cfg.get("mysql_rootuser", "root")
- for root, dirs, files in os.walk(test_dir, topdown = False):
- for fn in files:
- os.unlink(os.path.join(root, fn))
- for d in dirs:
- os.rmdir(os.path.join(root, d))
+ try:
+ mysql_rootpass = cfg.get("mysql_rootpass")
+ except:
+ pass
- if not quiet:
- print
- print "Reading YAML", yaml_file.name
+ try:
+ publication_base = cfg.get("publication_base")
+ except:
+ pass
- db = allocation_db(yaml.safe_load_all(yaml_file).next())
+ try:
+ publication_root = cfg.get("publication_root")
+ except:
+ pass
- # Show what we loaded
+ for k in ("rpkid_sql_password", "irdbd_sql_password", "pubd_sql_password",
+ "rpkid_sql_username", "irdbd_sql_username", "pubd_sql_username"):
+ if cfg.has_option(k):
+ config_overrides[k] = cfg.get(k)
+
+ if args.profile:
+ import cProfile
+ prof = cProfile.Profile()
+ try:
+ prof.runcall(body)
+ finally:
+ prof.dump_stats(args.profile)
+ if not quiet:
+ print
+ print "Dumped profile data to %s" % args.profile
+ else:
+ body()
- #db.dump()
+def body():
- # Do pre-Django SQL setup
+ global rpki
- pre_django_sql_setup(set(d.irdb_name for d in db if not d.is_hosted))
+ ts = timestamp()
- # Now ready for fun with multiple databases in Django!
+ for root, dirs, files in os.walk(test_dir, topdown = False):
+ for fn in files:
+ os.unlink(os.path.join(root, fn))
+ for d in dirs:
+ os.rmdir(os.path.join(root, d))
- # https://docs.djangoproject.com/en/1.4/topics/db/multi-db/
- # https://docs.djangoproject.com/en/1.4/topics/db/sql/
+ if not quiet:
+ print
+ print "Reading YAML", yaml_file.name
- database_template = {
- "ENGINE" : "django.db.backends.mysql",
- "USER" : config_overrides["irdbd_sql_username"],
- "PASSWORD" : config_overrides["irdbd_sql_password"],
- "HOST" : "",
- "PORT" : "",
- "OPTIONS" : { "init_command": "SET storage_engine=INNODB" }}
+ db = allocation_db(yaml.safe_load_all(yaml_file).next())
- databases = dict((d.irdb_name,
- dict(database_template, NAME = d.irdb_name))
- for d in db if not d.is_hosted)
+ # Show what we loaded
- databases["default"] = databases[db.root.irdb_name]
+ #db.dump()
- import django
+ # Do pre-Django SQL setup
- from django.conf import settings
+ pre_django_sql_setup(set(d.irdb_name for d in db if not d.is_hosted))
- settings.configure(
- DATABASES = databases,
- DATABASE_ROUTERS = ["rpki.irdb.router.DBContextRouter"],
- MIDDLEWARE_CLASSES = (),
- INSTALLED_APPS = ("rpki.irdb",))
+ # Now ready for fun with multiple databases in Django!
+ #
+ # https://docs.djangoproject.com/en/1.4/topics/db/multi-db/
+ # https://docs.djangoproject.com/en/1.4/topics/db/sql/
+ #
+ # This program's use of the ORM is sufficiently different that it's
+ # not worth straining to use rpki.django_settings, so we just use
+ # Django's settings API directly.
- if django.VERSION >= (1, 7): # API change, feh
- from django.apps import apps
- apps.populate(settings.INSTALLED_APPS)
+ database_template = {
+ "ENGINE" : "django.db.backends.mysql",
+ "USER" : config_overrides["irdbd_sql_username"],
+ "PASSWORD" : config_overrides["irdbd_sql_password"],
+ "HOST" : "",
+ "PORT" : "",
+ "OPTIONS" : { "init_command": "SET storage_engine=INNODB" }}
- import rpki.irdb
+ databases = dict((d.irdb_name, dict(database_template, NAME = d.irdb_name))
+ for d in db if not d.is_hosted)
- rpki.irdb.models.ca_certificate_lifetime = rpki.sundial.timedelta(days = 3652 * 2)
- rpki.irdb.models.ee_certificate_lifetime = rpki.sundial.timedelta(days = 3652)
+ databases["default"] = databases[db.root.irdb_name]
- ts()
+ import django
+ django.setup()
- for d in db:
- if not quiet:
- print
- print "Configuring", d.name
-
- if not d.is_hosted:
- d.mkdir()
- if d.runs_pubd:
- d.mkdir("publication")
- if d.is_root:
- d.mkdir("publication.root")
-
- if not d.is_hosted:
- d.dump_conf()
- d.dump_rsyncd()
-
- d.dump_asns("%s.asns.csv" % d.name)
- d.dump_prefixes("%s.prefixes.csv" % d.name)
- d.dump_roas("%s.roas.csv" % d.name)
- d.dump_ghostbusters("%s.ghostbusters.vcard" % d.name)
- d.dump_router_certificates("%s.routercerts.xml" % d.name)
-
- if not d.is_hosted:
- if not quiet:
- print "Initializing SQL"
- d.syncdb()
- if not quiet:
- print "Hiring zookeeper"
- d.hire_zookeeper()
-
- with d.irdb:
- if not quiet:
- print "Creating identity"
- x = d.zoo.initialize()
-
- if d.is_root:
- if not quiet:
- print "Creating RPKI root certificate and TAL"
- d.dump_root()
- x = d.zoo.configure_rootd()
+ from django.conf import settings
- else:
- with d.parent.irdb:
- x = d.parent.zoo.configure_child(x.file)[0]
- x = d.zoo.configure_parent(x.file)[0]
+ settings.configure(
+ DATABASES = databases,
+ DATABASE_ROUTERS = ["rpki.irdb.router.DBContextRouter"],
+ INSTALLED_APPS = ["rpki.irdb"])
- with d.pubd.irdb:
- x = d.pubd.zoo.configure_publication_client(x.file, flat = flat_publication)[0]
- d.zoo.configure_repository(x.file)
+ import rpki.irdb
- if loopback and not d.is_hosted:
- with d.irdb:
- d.zoo.write_bpki_files()
+ rpki.irdb.models.ca_certificate_lifetime = rpki.sundial.timedelta(days = 3652 * 2)
+ rpki.irdb.models.ee_certificate_lifetime = rpki.sundial.timedelta(days = 3652)
ts()
- if not loopback:
- if not quiet:
- print
for d in db:
- d.dump_sql()
+ if not quiet:
+ print
+ print "Configuring", d.name
+
+ if not d.is_hosted:
+ d.mkdir()
+ if d.runs_pubd:
+ d.mkdir("publication")
+ if d.is_root:
+ d.mkdir("publication.root")
+
+ if not d.is_hosted:
+ d.dump_conf()
+ d.dump_rsyncd()
+
+ d.dump_asns("%s.asns.csv" % d.name)
+ d.dump_prefixes("%s.prefixes.csv" % d.name)
+ d.dump_roas("%s.roas.csv" % d.name)
+ d.dump_ghostbusters("%s.ghostbusters.vcard" % d.name)
+ d.dump_router_certificates("%s.routercerts.xml" % d.name)
+
+ if not d.is_hosted:
+ if not quiet:
+ print "Initializing SQL"
+ d.syncdb()
+ if not quiet:
+ print "Hiring zookeeper"
+ d.hire_zookeeper()
+
+ with d.irdb:
+ if not quiet:
+ print "Creating identity"
+ x = d.zoo.initialize()
+
+ if d.is_root:
+ if not quiet:
+ print "Creating RPKI root certificate and TAL"
+ d.dump_root()
+ x = d.zoo.configure_rootd()
+
+ else:
+ with d.parent.irdb:
+ x = d.parent.zoo.configure_child(x.file)[0]
+ x = d.zoo.configure_parent(x.file)[0]
+
+ with d.pubd.irdb:
+ x = d.pubd.zoo.configure_publication_client(x.file, flat = flat_publication)[0]
+ d.zoo.configure_repository(x.file)
+
+ if loopback and not d.is_hosted:
+ with d.irdb:
+ d.zoo.write_bpki_files()
+
+ ts()
+
+ if not loopback:
+ if not quiet:
+ print
+ for d in db:
+ d.dump_sql()
if __name__ == "__main__":
- main()
+ main()
diff --git a/ca/tests/yamltest-test-all.sh b/ca/tests/yamltest-test-all.sh
index 4bd5c560..54224d5e 100644
--- a/ca/tests/yamltest-test-all.sh
+++ b/ca/tests/yamltest-test-all.sh
@@ -24,24 +24,28 @@ test -z "$STY" && exec screen -L sh $0
screen -X split
screen -X focus
-: ${runtime=900}
+# Timers
+: ${startup=600} ${runtime=900} ${poll=30} ${shutdown=30}
+
+# Once upon a time we had a settitle program. Noop for now.
+: ${settitle=":"}
for yaml in smoketest.*.yaml
do
- settitle "$yaml: Starting"
+ $settitle "$yaml: Starting"
rm -rf test rcynic-data
python sql-cleaner.py
now=$(date +%s)
- finish=$(($now + $runtime))
+ finish=$(($now + $startup + $runtime))
title="$yaml: will finish at $(date -r $finish)"
- settitle "$title"
- screen sh -c "settitle '$title'; exec python yamltest.py -p yamltest.pid $yaml"
+ $settitle "$title"
+ screen sh -c "$settitle '$title'; exec python yamltest.py -p yamltest.pid $yaml"
date
- sleep 180
+ sleep $startup
date
while test $(date +%s) -lt $finish
do
- sleep 30
+ sleep $poll
date
../../rp/rcynic/rcynic
../../rp/rcynic/rcynic-text rcynic.xml
@@ -49,10 +53,8 @@ do
date
echo "$title"
done
- if test -r yamltest.pid
- then
- kill -INT $(cat yamltest.pid)
- sleep 30
- fi
+ if test -r yamltest.pid; then kill -INT $(cat yamltest.pid); sleep ${shutdown}; fi
+ if test -r yamltest.pid; then kill -INT $(cat yamltest.pid); sleep ${shutdown}; fi
+ if test -r yamltest.pid; then kill -KILL $(cat yamltest.pid); sleep ${shutdown}; fi
make backup
done
diff --git a/ca/tests/yamltest.py b/ca/tests/yamltest.py
index 0932049b..a7ee6540 100644..100755
--- a/ca/tests/yamltest.py
+++ b/ca/tests/yamltest.py
@@ -36,13 +36,12 @@ and waits for one of them to exit.
# running daemons.
#
-# pylint: disable=W0702,W0621
-
import subprocess
import re
import os
import logging
import argparse
+import webbrowser
import sys
import yaml
import signal
@@ -56,6 +55,8 @@ import rpki.csv_utils
import rpki.x509
import rpki.relaxng
+# pylint: disable=W0621
+
# Nasty regular expressions for parsing config files. Sadly, while
# the Python ConfigParser supports writing config files, it does so in
# such a limited way that it's easier just to hack this ourselves.
@@ -64,585 +65,680 @@ section_regexp = re.compile(r"\s*\[\s*(.+?)\s*\]\s*$")
variable_regexp = re.compile(r"\s*([-a-zA-Z0-9_]+)\s*=\s*(.+?)\s*$")
def cleanpath(*names):
- """
- Construct normalized pathnames.
- """
- return os.path.normpath(os.path.join(*names))
+ """
+ Construct normalized pathnames.
+ """
+
+ return os.path.normpath(os.path.join(*names))
# Pathnames for various things we need
this_dir = os.getcwd()
test_dir = cleanpath(this_dir, "yamltest.dir")
-rpkid_dir = cleanpath(this_dir, "..")
+ca_dir = cleanpath(this_dir, "..")
-prog_rpkic = cleanpath(rpkid_dir, "rpkic")
-prog_rpkid = cleanpath(rpkid_dir, "rpkid")
-prog_irdbd = cleanpath(rpkid_dir, "irdbd")
-prog_pubd = cleanpath(rpkid_dir, "pubd")
-prog_rootd = cleanpath(rpkid_dir, "rootd")
+prog_rpkic = cleanpath(ca_dir, "rpkic")
+prog_rpkid = cleanpath(ca_dir, "rpkid")
+prog_irdbd = cleanpath(ca_dir, "irdbd")
+prog_pubd = cleanpath(ca_dir, "pubd")
+prog_rootd = cleanpath(ca_dir, "rootd")
+prog_rpki_manage = cleanpath(ca_dir, "rpki-manage")
class roa_request(object):
- """
- Representation of a ROA request.
- """
-
- def __init__(self, asn, ipv4, ipv6):
- self.asn = asn
- self.v4 = rpki.resource_set.roa_prefix_set_ipv4("".join(ipv4.split())) if ipv4 else None
- self.v6 = rpki.resource_set.roa_prefix_set_ipv6("".join(ipv6.split())) if ipv6 else None
-
- def __eq__(self, other):
- return self.asn == other.asn and self.v4 == other.v4 and self.v6 == other.v6
-
- def __hash__(self):
- v4 = tuple(self.v4) if self.v4 is not None else None
- v6 = tuple(self.v6) if self.v6 is not None else None
- return self.asn.__hash__() + v4.__hash__() + v6.__hash__()
-
- def __str__(self):
- if self.v4 and self.v6:
- return "%s: %s,%s" % (self.asn, self.v4, self.v6)
- else:
- return "%s: %s" % (self.asn, self.v4 or self.v6)
-
- @classmethod
- def parse(cls, y):
"""
- Parse a ROA request from YAML format.
+ Representation of a ROA request.
"""
- return cls(y.get("asn"), y.get("ipv4"), y.get("ipv6"))
+ def __init__(self, asn, ipv4, ipv6):
+ self.asn = asn
+ self.v4 = rpki.resource_set.roa_prefix_set_ipv4("".join(ipv4.split())) if ipv4 else None
+ self.v6 = rpki.resource_set.roa_prefix_set_ipv6("".join(ipv6.split())) if ipv6 else None
-class router_cert(object):
- """
- Representation for a router_cert object.
- """
-
- _ecparams = None
-
- @classmethod
- def ecparams(cls):
- if cls._ecparams is None:
- cls._ecparams = rpki.x509.KeyParams.generateEC()
- return cls._ecparams
+ def __eq__(self, other):
+ return self.asn == other.asn and self.v4 == other.v4 and self.v6 == other.v6
- def __init__(self, asn, router_id):
- self.asn = rpki.resource_set.resource_set_as("".join(str(asn).split()))
- self.router_id = router_id
- self.keypair = rpki.x509.ECDSA.generate(self.ecparams())
- self.pkcs10 = rpki.x509.PKCS10.create(keypair = self.keypair)
- self.gski = self.pkcs10.gSKI()
+ def __hash__(self):
+ v4 = tuple(self.v4) if self.v4 is not None else None
+ v6 = tuple(self.v6) if self.v6 is not None else None
+ return self.asn.__hash__() + v4.__hash__() + v6.__hash__()
- def __eq__(self, other):
- return self.asn == other.asn and self.router_id == other.router_id and self.gski == other.gski
+ def __str__(self):
+ if self.v4 and self.v6:
+ return "%s: %s,%s" % (self.asn, self.v4, self.v6)
+ else:
+ return "%s: %s" % (self.asn, self.v4 or self.v6)
- def __hash__(self):
- return tuple(self.asn).__hash__() + self.router_id.__hash__() + self.gski.__hash__()
+ @classmethod
+ def parse(cls, y):
+ """
+ Parse a ROA request from YAML format.
+ """
- def __str__(self):
- return "%s: %s: %s" % (self.asn, self.router_id, self.gski)
+ return cls(y.get("asn"), y.get("ipv4"), y.get("ipv6"))
- @classmethod
- def parse(cls, yaml):
- return cls(yaml.get("asn"), yaml.get("router_id"))
-class allocation_db(list):
- """
- Our allocation database.
- """
-
- def __init__(self, yaml):
- list.__init__(self)
- self.root = allocation(yaml, self)
- assert self.root.is_root
- if self.root.crl_interval is None:
- self.root.crl_interval = 60 * 60
- if self.root.regen_margin is None:
- self.root.regen_margin = 24 * 60 * 60
- if self.root.base.valid_until is None:
- self.root.base.valid_until = rpki.sundial.now() + rpki.sundial.timedelta(days = 2)
- for a in self:
- if a.base.valid_until is None:
- a.base.valid_until = a.parent.base.valid_until
- if a.crl_interval is None:
- a.crl_interval = a.parent.crl_interval
- if a.regen_margin is None:
- a.regen_margin = a.parent.regen_margin
- self.root.closure()
- self.map = dict((a.name, a) for a in self)
- for a in self:
- if a.is_hosted:
- a.hosted_by = self.map[a.hosted_by]
- a.hosted_by.hosts.append(a)
- assert not a.is_root and not a.hosted_by.is_hosted
-
- def dump(self):
+class router_cert(object):
"""
- Show contents of allocation database.
+ Representation for a router_cert object.
"""
- for a in self:
- a.dump()
+ _ecparams = None
-class allocation(object):
- """
- One entity in our allocation database. Every entity in the database
- is assumed to hold resources, so needs at least rpkic services.
- Entities that don't have the hosted_by property run their own copies
- of rpkid, irdbd, and pubd, so they also need myirbe services.
- """
-
- base_port = None
- parent = None
- crl_interval = None
- regen_margin = None
- rootd_port = None
- engine = -1
- rpkid_port = -1
- irdbd_port = -1
- pubd_port = -1
- rsync_port = -1
- rootd_port = -1
- rpkic_counter = 0L
-
- @classmethod
- def allocate_port(cls):
- """
- Allocate a TCP port.
- """
- cls.base_port += 1
- return cls.base_port
+ @classmethod
+ def ecparams(cls):
+ if cls._ecparams is None:
+ cls._ecparams = rpki.x509.KeyParams.generateEC()
+ return cls._ecparams
- base_engine = -1
+ def __init__(self, asn, router_id):
+ self.asn = rpki.resource_set.resource_set_as("".join(str(asn).split()))
+ self.router_id = router_id
+ self.keypair = rpki.x509.ECDSA.generate(params = self.ecparams(), quiet = True)
+ self.pkcs10 = rpki.x509.PKCS10.create(keypair = self.keypair)
+ self.gski = self.pkcs10.gSKI()
- @classmethod
- def allocate_engine(cls):
- """
- Allocate an engine number, mostly used to construct MySQL database
- names.
- """
- cls.base_engine += 1
- return cls.base_engine
-
- def __init__(self, yaml, db, parent = None):
- db.append(self)
- self.name = yaml["name"]
- self.parent = parent
- self.kids = [allocation(k, db, self) for k in yaml.get("kids", ())]
- valid_until = None
- if "valid_until" in yaml:
- valid_until = rpki.sundial.datetime.from_datetime(yaml.get("valid_until"))
- if valid_until is None and "valid_for" in yaml:
- valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"])
- self.base = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as(yaml.get("asn")),
- v4 = rpki.resource_set.resource_set_ipv4(yaml.get("ipv4")),
- v6 = rpki.resource_set.resource_set_ipv6(yaml.get("ipv6")),
- valid_until = valid_until)
- if "crl_interval" in yaml:
- self.crl_interval = rpki.sundial.timedelta.parse(yaml["crl_interval"]).convert_to_seconds()
- if "regen_margin" in yaml:
- self.regen_margin = rpki.sundial.timedelta.parse(yaml["regen_margin"]).convert_to_seconds()
- self.roa_requests = [roa_request.parse(y) for y in yaml.get("roa_request", yaml.get("route_origin", ()))]
- self.router_certs = [router_cert.parse(y) for y in yaml.get("router_cert", ())]
- if "ghostbusters" in yaml:
- self.ghostbusters = yaml.get("ghostbusters")
- elif "ghostbuster" in yaml:
- self.ghostbusters = [yaml.get("ghostbuster")]
- else:
- self.ghostbusters = []
- for r in self.roa_requests:
- if r.v4:
- self.base.v4 |= r.v4.to_resource_set()
- if r.v6:
- self.base.v6 |= r.v6.to_resource_set()
- for r in self.router_certs:
- self.base.asn |= r.asn
- self.hosted_by = yaml.get("hosted_by")
- self.hosts = []
- if not self.is_hosted:
- self.engine = self.allocate_engine()
- self.rpkid_port = self.allocate_port()
- self.irdbd_port = self.allocate_port()
- if self.runs_pubd:
- self.pubd_port = self.allocate_port()
- self.rsync_port = self.allocate_port()
- if self.is_root:
- self.rootd_port = self.allocate_port()
-
- def closure(self):
- """
- Compute resource closure of this node and its children, to avoid a
- lot of tedious (and error-prone) duplication in the YAML file.
- """
- resources = self.base
- for kid in self.kids:
- resources |= kid.closure()
- self.resources = resources
- return resources
+ def __eq__(self, other):
+ return self.asn == other.asn and self.router_id == other.router_id and self.gski == other.gski
- def dump(self):
- """
- Show content of this allocation node.
- """
- print str(self)
-
- def __str__(self):
- s = self.name + ":\n"
- if self.resources.asn: s += " ASNs: %s\n" % self.resources.asn
- if self.resources.v4: s += " IPv4: %s\n" % self.resources.v4
- if self.resources.v6: s += " IPv6: %s\n" % self.resources.v6
- if self.kids: s += " Kids: %s\n" % ", ".join(k.name for k in self.kids)
- if self.parent: s += " Up: %s\n" % self.parent.name
- if self.is_hosted: s += " Host: %s\n" % self.hosted_by.name
- if self.hosts: s += " Hosts: %s\n" % ", ".join(h.name for h in self.hosts)
- for r in self.roa_requests: s += " ROA: %s\n" % r
- if not self.is_hosted: s += " IPort: %s\n" % self.irdbd_port
- if self.runs_pubd: s += " PPort: %s\n" % self.pubd_port
- if not self.is_hosted: s += " RPort: %s\n" % self.rpkid_port
- if self.runs_pubd: s += " SPort: %s\n" % self.rsync_port
- if self.is_root: s += " TPort: %s\n" % self.rootd_port
- return s + " Until: %s\n" % self.resources.valid_until
-
- @property
- def is_root(self):
- """
- Is this the root node?
- """
- return self.parent is None
+ def __hash__(self):
+ return tuple(self.asn).__hash__() + self.router_id.__hash__() + self.gski.__hash__()
- @property
- def is_hosted(self):
- """
- Is this entity hosted?
- """
- return self.hosted_by is not None
+ def __str__(self):
+ return "%s: %s: %s" % (self.asn, self.router_id, self.gski)
- @property
- def runs_pubd(self):
- """
- Does this entity run a pubd?
- """
- return self.is_root or not (self.is_hosted or only_one_pubd)
+ @classmethod
+ def parse(cls, yaml):
+ return cls(yaml.get("asn"), yaml.get("router_id"))
- def path(self, *names):
+class allocation_db(list):
"""
- Construct pathnames in this entity's test directory.
+ Our allocation database.
"""
- return cleanpath(test_dir, self.host.name, *names)
- def csvout(self, fn):
- """
- Open and log a CSV output file.
- """
- path = self.path(fn)
- print "Writing", path
- return rpki.csv_utils.csv_writer(path)
+ def __init__(self, yaml):
+ list.__init__(self)
+ self.root = allocation(yaml, self)
+ assert self.root.is_root and not any(a.is_root for a in self if a is not self.root) and self[0] is self.root
+ if self.root.crl_interval is None:
+ self.root.crl_interval = 60 * 60
+ if self.root.regen_margin is None:
+ self.root.regen_margin = 24 * 60 * 60
+ if self.root.base.valid_until is None:
+ self.root.base.valid_until = rpki.sundial.now() + rpki.sundial.timedelta(days = 2)
+ for a in self:
+ if a.base.valid_until is None:
+ a.base.valid_until = a.parent.base.valid_until
+ if a.crl_interval is None:
+ a.crl_interval = a.parent.crl_interval
+ if a.regen_margin is None:
+ a.regen_margin = a.parent.regen_margin
+ self.root.closure()
+ self.map = dict((a.name, a) for a in self)
+ for a in self:
+ if a.is_hosted:
+ a.hosted_by = self.map[a.hosted_by]
+ a.hosted_by.hosts.append(a)
+ assert not a.is_root and not a.hosted_by.is_hosted
+
+ def dump(self):
+ """
+ Show contents of allocation database.
+ """
+
+ for a in self:
+ a.dump()
- def up_down_url(self):
- """
- Construct service URL for this node's parent.
- """
- return "http://localhost:%d/up-down/%s/%s" % (self.parent.host.rpkid_port,
- self.parent.name,
- self.name)
- def dump_asns(self):
- """
- Write Autonomous System Numbers CSV file.
- """
- fn = "%s.asns.csv" % d.name
- if not args.skip_config:
- f = self.csvout(fn)
- for k in self.kids:
- f.writerows((k.name, a) for a in k.resources.asn)
- f.close()
- if not args.stop_after_config:
- self.run_rpkic("load_asns", fn)
-
- def dump_prefixes(self):
- """
- Write prefixes CSV file.
- """
- fn = "%s.prefixes.csv" % d.name
- if not args.skip_config:
- f = self.csvout(fn)
- for k in self.kids:
- f.writerows((k.name, p) for p in (k.resources.v4 + k.resources.v6))
- f.close()
- if not args.stop_after_config:
- self.run_rpkic("load_prefixes", fn)
-
- def dump_roas(self):
- """
- Write ROA CSV file.
- """
- fn = "%s.roas.csv" % d.name
- if not args.skip_config:
- f = self.csvout(fn)
- for g1, r in enumerate(self.roa_requests):
- f.writerows((p, r.asn, "G%08d%08d" % (g1, g2))
- for g2, p in enumerate((r.v4 + r.v6 if r.v4 and r.v6 else r.v4 or r.v6 or ())))
- f.close()
- if not args.stop_after_config:
- self.run_rpkic("load_roa_requests", fn)
-
- def dump_ghostbusters(self):
- """
- Write Ghostbusters vCard file.
- """
- if self.ghostbusters:
- fn = "%s.ghostbusters.vcard" % d.name
- if not args.skip_config:
- path = self.path(fn)
- print "Writing", path
- f = open(path, "w")
- for i, g in enumerate(self.ghostbusters):
- if i:
- f.write("\n")
- f.write(g)
- f.close()
- if not args.stop_after_config:
- self.run_rpkic("load_ghostbuster_requests", fn)
-
- def dump_router_certificates(self):
+class allocation(object):
"""
- Write EE certificates (router certificates, etc).
+ One entity in our allocation database. Every entity in the database
+ is assumed to hold resources, so needs at least rpkic services.
+ Entities that don't have the hosted_by property run their own copies
+ of rpkid, irdbd, and pubd, so they also need myirbe services.
"""
- if self.router_certs:
- fn = "%s.routercerts.xml" % d.name
- if not args.skip_config:
- path = self.path(fn)
- print "Writing", path
- xmlns = rpki.relaxng.router_certificate.xmlns
- xml = lxml.etree.Element(xmlns + "router_certificate_requests",
- version = rpki.relaxng.router_certificate.version,
- nsmap = rpki.relaxng.router_certificate.nsmap)
- for r in self.router_certs:
- x = lxml.etree.SubElement(xml, xmlns + "router_certificate_request",
- router_id = str(r.router_id),
- asn = str(r.asn),
- valid_until = str(self.resources.valid_until))
- x.text = r.pkcs10.get_Base64()
- rpki.relaxng.router_certificate.assertValid(xml)
- lxml.etree.ElementTree(xml).write(path, pretty_print = True)
- if not args.stop_after_config:
- self.run_rpkic("add_router_certificate_request", fn)
- if not args.skip_config and args.store_router_private_keys:
- path = self.path("%s.routercerts.keys" % d.name)
- print "Writing", path
- with open(path, "w") as f:
- for r in self.router_certs:
- f.write(r.keypair.get_PEM())
- @property
- def pubd(self):
- """
- Walk up tree until we find somebody who runs pubd.
- """
- s = self
- while not s.runs_pubd:
- s = s.parent
- return s
+ base_port = None
+ parent = None
+ crl_interval = None
+ regen_margin = None
+ rootd_port = None
+ engine = -1
+ rpkid_port = -1
+ irdbd_port = -1
+ pubd_port = -1
+ rsync_port = -1
+ rootd_port = -1
+ rrdp_port = -1
+ rpkic_counter = 0L
+
+ @classmethod
+ def allocate_port(cls):
+ """
+ Allocate a TCP port.
+ """
+
+ cls.base_port += 1
+ return cls.base_port
+
+ base_engine = -1
+
+ @classmethod
+ def allocate_engine(cls):
+ """
+ Allocate an engine number, mostly used to construct SQL database
+ names.
+ """
+
+ cls.base_engine += 1
+ return cls.base_engine
+
+ def __init__(self, yaml, db, parent = None):
+ db.append(self)
+ self.name = yaml["name"]
+ self.parent = parent
+ self.kids = [allocation(k, db, self) for k in yaml.get("kids", ())]
+ valid_until = None
+ if "valid_until" in yaml:
+ valid_until = rpki.sundial.datetime.from_datetime(yaml.get("valid_until"))
+ if valid_until is None and "valid_for" in yaml:
+ valid_until = rpki.sundial.now() + rpki.sundial.timedelta.parse(yaml["valid_for"])
+ self.base = rpki.resource_set.resource_bag(
+ asn = rpki.resource_set.resource_set_as(yaml.get("asn")),
+ v4 = rpki.resource_set.resource_set_ipv4(yaml.get("ipv4")),
+ v6 = rpki.resource_set.resource_set_ipv6(yaml.get("ipv6")),
+ valid_until = valid_until)
+ if "crl_interval" in yaml:
+ self.crl_interval = rpki.sundial.timedelta.parse(yaml["crl_interval"]).convert_to_seconds()
+ if "regen_margin" in yaml:
+ self.regen_margin = rpki.sundial.timedelta.parse(yaml["regen_margin"]).convert_to_seconds()
+ self.roa_requests = [roa_request.parse(y) for y in yaml.get("roa_request", yaml.get("route_origin", ()))]
+ self.router_certs = [router_cert.parse(y) for y in yaml.get("router_cert", ())]
+ if "ghostbusters" in yaml:
+ self.ghostbusters = yaml.get("ghostbusters")
+ elif "ghostbuster" in yaml:
+ self.ghostbusters = [yaml.get("ghostbuster")]
+ else:
+ self.ghostbusters = []
+ for r in self.roa_requests:
+ if r.v4:
+ self.base.v4 |= r.v4.to_resource_set()
+ if r.v6:
+ self.base.v6 |= r.v6.to_resource_set()
+ for r in self.router_certs:
+ self.base.asn |= r.asn
+ self.hosted_by = yaml.get("hosted_by")
+ self.hosts = []
+ if not self.is_hosted:
+ self.engine = self.allocate_engine()
+ self.rpkid_port = self.allocate_port()
+ self.irdbd_port = self.allocate_port()
+ if self.runs_pubd:
+ self.pubd_port = self.allocate_port()
+ self.rsync_port = self.allocate_port()
+ self.rrdp_port = self.allocate_port()
+ if self.is_root:
+ self.rootd_port = self.allocate_port()
+
+ def closure(self):
+ """
+ Compute resource closure of this node and its children, to avoid a
+ lot of tedious (and error-prone) duplication in the YAML file.
+ """
+
+ resources = self.base
+ for kid in self.kids:
+ resources |= kid.closure()
+ self.resources = resources # pylint: disable=W0201
+ return resources
+
+ def dump(self):
+ """
+ Show content of this allocation node.
+ """
+
+ print str(self)
+
+ def __str__(self):
+ # pylint: disable=C0321
+ s = self.name + ":\n"
+ if self.resources.asn: s += " ASNs: %s\n" % self.resources.asn
+ if self.resources.v4: s += " IPv4: %s\n" % self.resources.v4
+ if self.resources.v6: s += " IPv6: %s\n" % self.resources.v6
+ if self.kids: s += " Kids: %s\n" % ", ".join(k.name for k in self.kids)
+ if self.parent: s += " Up: %s\n" % self.parent.name
+ if self.is_hosted: s += " Host: %s\n" % self.hosted_by.name
+ if self.hosts: s += " Hosts: %s\n" % ", ".join(h.name for h in self.hosts)
+ for r in self.roa_requests: s += " ROA: %s\n" % r
+ if not self.is_hosted: s += " IPort: %s\n" % self.irdbd_port
+ if self.runs_pubd: s += " PPort: %s\n" % self.pubd_port
+ if not self.is_hosted: s += " RPort: %s\n" % self.rpkid_port
+ if self.runs_pubd: s += " SPort: %s\n" % self.rsync_port
+ if self.is_root: s += " TPort: %s\n" % self.rootd_port
+ return s + " Until: %s\n" % self.resources.valid_until
+
+ @property
+ def is_root(self):
+ """
+ Is this the root node?
+ """
+
+ return self.parent is None
+
+ @property
+ def is_hosted(self):
+ """
+ Is this entity hosted?
+ """
+
+ return self.hosted_by is not None
+
+ @property
+ def runs_pubd(self):
+ """
+ Does this entity run a pubd?
+ """
+
+ return self.is_root or not (self.is_hosted or only_one_pubd)
+
+ def path(self, *names):
+ """
+ Construct pathnames in this entity's test directory.
+ """
+
+ return cleanpath(test_dir, self.host.name, *names)
+
+ def csvout(self, fn):
+ """
+ Open and log a CSV output file.
+ """
- @property
- def client_handle(self):
- """
- Work out what pubd configure_publication_client will call us.
- """
- path = []
- s = self
- if not args.flat_publication:
- while not s.runs_pubd:
+ path = self.path(fn)
+ print "Writing", path
+ return rpki.csv_utils.csv_writer(path)
+
+ def up_down_url(self):
+ """
+ Construct service URL for this node's parent.
+ """
+
+ return "http://localhost:%d/up-down/%s/%s" % (self.parent.host.rpkid_port,
+ self.parent.name,
+ self.name)
+
+ def dump_asns(self):
+ """
+ Write Autonomous System Numbers CSV file.
+ """
+
+ fn = "%s.asns.csv" % d.name
+ if not args.skip_config:
+ with self.csvout(fn) as f:
+ for k in self.kids:
+ f.writerows((k.name, a) for a in k.resources.asn)
+ if not args.stop_after_config:
+ self.run_rpkic("load_asns", fn)
+
+ def dump_prefixes(self):
+ """
+ Write prefixes CSV file.
+ """
+
+ fn = "%s.prefixes.csv" % d.name
+ if not args.skip_config:
+ with self.csvout(fn) as f:
+ for k in self.kids:
+ f.writerows((k.name, p) for p in (k.resources.v4 + k.resources.v6))
+ if not args.stop_after_config:
+ self.run_rpkic("load_prefixes", fn)
+
+ def dump_roas(self):
+ """
+ Write ROA CSV file.
+ """
+
+ fn = "%s.roas.csv" % d.name
+ if not args.skip_config:
+ with self.csvout(fn) as f:
+ for g1, r in enumerate(self.roa_requests):
+ f.writerows((p, r.asn, "G%08d%08d" % (g1, g2))
+ for g2, p in enumerate((r.v4 + r.v6 if r.v4 and r.v6 else r.v4 or r.v6 or ())))
+ if not args.stop_after_config:
+ self.run_rpkic("load_roa_requests", fn)
+
+ def dump_ghostbusters(self):
+ """
+ Write Ghostbusters vCard file.
+ """
+
+ if self.ghostbusters:
+ fn = "%s.ghostbusters.vcard" % d.name
+ if not args.skip_config:
+ path = self.path(fn)
+ print "Writing", path
+ with open(path, "w") as f:
+ f.write("\n".join(self.ghostbusters))
+ if not args.stop_after_config:
+ self.run_rpkic("load_ghostbuster_requests", fn)
+
+ def dump_router_certificates(self):
+ """
+ Write EE certificates (router certificates, etc).
+ """
+
+ if self.router_certs:
+ fn = "%s.routercerts.xml" % d.name
+ if not args.skip_config:
+ path = self.path(fn)
+ print "Writing", path
+ xmlns = rpki.relaxng.router_certificate.xmlns
+ xml = lxml.etree.Element(xmlns + "router_certificate_requests",
+ version = rpki.relaxng.router_certificate.version,
+ nsmap = rpki.relaxng.router_certificate.nsmap)
+ for r in self.router_certs:
+ x = lxml.etree.SubElement(xml, xmlns + "router_certificate_request",
+ router_id = str(r.router_id),
+ asn = str(r.asn),
+ valid_until = str(self.resources.valid_until))
+ x.text = r.pkcs10.get_Base64()
+ rpki.relaxng.router_certificate.assertValid(xml)
+ lxml.etree.ElementTree(xml).write(path, pretty_print = True)
+ if not args.stop_after_config:
+ self.run_rpkic("add_router_certificate_request", fn)
+ if not args.skip_config and args.store_router_private_keys:
+ path = self.path("%s.routercerts.keys" % d.name)
+ print "Writing", path
+ with open(path, "w") as f:
+ for r in self.router_certs:
+ f.write(r.keypair.get_PEM())
+
+ @property
+ def pubd(self):
+ """
+ Walk up tree until we find somebody who runs pubd.
+ """
+
+ s = self
+ while not s.runs_pubd:
+ s = s.parent
+ return s
+
+ @property
+ def client_handle(self):
+ """
+ Work out what pubd configure_publication_client will call us.
+ """
+
+ path = []
+ s = self
+ if not args.flat_publication:
+ while not s.runs_pubd:
+ path.append(s)
+ s = s.parent
path.append(s)
- s = s.parent
- path.append(s)
- return ".".join(i.name for i in reversed(path))
-
- @property
- def host(self):
- return self.hosted_by or self
-
- def dump_conf(self):
- """
- Write configuration file for OpenSSL and RPKI tools.
- """
-
- r = dict(
- handle = self.name,
- run_rpkid = str(not self.is_hosted),
- run_pubd = str(self.runs_pubd),
- run_rootd = str(self.is_root),
- irdbd_sql_database = "irdb%d" % self.engine,
- irdbd_sql_username = "irdb",
- rpkid_sql_database = "rpki%d" % self.engine,
- rpkid_sql_username = "rpki",
- rpkid_server_host = "localhost",
- rpkid_server_port = str(self.rpkid_port),
- irdbd_server_host = "localhost",
- irdbd_server_port = str(self.irdbd_port),
- rootd_server_port = str(self.rootd_port),
- pubd_sql_database = "pubd%d" % self.engine,
- pubd_sql_username = "pubd",
- pubd_server_host = "localhost",
- pubd_server_port = str(self.pubd.pubd_port),
- publication_rsync_server = "localhost:%s" % self.pubd.rsync_port,
- bpki_servers_directory = self.path(),
- publication_base_directory = self.path("publication"),
- shared_sql_password = "fnord")
-
- r.update(config_overrides)
-
- f = open(self.path("rpki.conf"), "w")
- f.write("# Automatically generated, do not edit\n")
- print "Writing", f.name
-
- section = None
- for line in open(cleanpath(rpkid_dir, "examples/rpki.conf")):
- 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 and option in r:
- line = "%s = %s\n" % (option, r[option])
- f.write(line)
-
- f.close()
-
- def dump_rsyncd(self):
- """
- Write rsyncd configuration file.
- """
-
- if self.runs_pubd:
- f = open(self.path("rsyncd.conf"), "w")
- print "Writing", f.name
- f.writelines(s + "\n" for s in
- ("# Automatically generated, do not edit",
- "port = %d" % self.rsync_port,
- "address = localhost",
- "[rpki]",
- "log file = rsyncd.log",
- "read only = yes",
- "use chroot = no",
- "path = %s" % self.path("publication"),
- "comment = RPKI test",
- "[root]",
- "log file = rsyncd_root.log",
- "read only = yes",
- "use chroot = no",
- "path = %s" % self.path("publication.root"),
- "comment = RPKI test root"))
- f.close()
-
- @classmethod
- def next_rpkic_counter(cls):
- cls.rpkic_counter += 10000
- return str(cls.rpkic_counter)
-
- def run_rpkic(self, *argv):
- """
- Run rpkic for this entity.
- """
- cmd = [prog_rpkic, "-i", self.name, "-c", self.path("rpki.conf")]
- if args.profile:
- cmd.append("--profile")
- cmd.append(self.path("rpkic.%s.prof" % rpki.sundial.now()))
- cmd.extend(str(a) for a in argv if a is not None)
- print 'Running "%s"' % " ".join(cmd)
- env = os.environ.copy()
- env["YAMLTEST_RPKIC_COUNTER"] = self.next_rpkic_counter()
- subprocess.check_call(cmd, cwd = self.host.path(), env = env)
-
- def run_python_daemon(self, prog):
- """
- Start a Python daemon and return a subprocess.Popen object
- representing the running daemon.
- """
- basename = os.path.splitext(os.path.basename(prog))[0]
- cmd = [prog, "--foreground", "--log-level", "debug",
- "--log-file", self.path(basename + ".log"),
- "--config", self.path("rpki.conf")]
- if args.profile and basename != "rootd":
- cmd.extend((
- "--profile", self.path(basename + ".prof")))
- p = subprocess.Popen(cmd, cwd = self.path())
- print 'Running %s for %s: pid %d process %r' % (" ".join(cmd), self.name, p.pid, p)
- return p
-
- def run_rpkid(self):
- """
- Run rpkid.
- """
- return self.run_python_daemon(prog_rpkid)
-
- def run_irdbd(self):
- """
- Run irdbd.
- """
- return self.run_python_daemon(prog_irdbd)
+ return ".".join(i.name for i in reversed(path))
+
+ @property
+ def host(self):
+ return self.hosted_by or self
+
+ def dump_conf(self):
+ """
+ Write configuration file for OpenSSL and RPKI tools.
+ """
+
+ r = dict(
+ handle = self.name,
+ run_rpkid = str(not self.is_hosted),
+ run_pubd = str(self.runs_pubd),
+ run_rootd = str(self.is_root),
+ rpkid_server_host = "localhost",
+ rpkid_server_port = str(self.rpkid_port),
+ irdbd_server_host = "localhost",
+ irdbd_server_port = str(self.irdbd_port),
+ rootd_server_port = str(self.rootd_port),
+ pubd_server_host = "localhost",
+ pubd_server_port = str(self.pubd.pubd_port),
+ publication_rsync_server = "localhost:%s" % self.pubd.rsync_port,
+ publication_rrdp_notification_uri = "http://localhost:%s/rrdp/notify.xml" % self.pubd.rrdp_port,
+ bpki_servers_directory = self.path(),
+ publication_base_directory = self.path("publication"),
+ rrdp_publication_base_directory = self.path("rrdp-publication"),
+ shared_sql_engine = args.sql_engine,
+ shared_sql_password = "fnord",
+ irdbd_sql_username = "irdb",
+ rpkid_sql_username = "rpki",
+ pubd_sql_username = "pubd")
+
+ if args.sql_engine == "sqlite3":
+ r.update(
+ irdbd_sql_database = self.path("irdb.sqlite3"),
+ rpkid_sql_database = self.path("rpkidb.sqlite3"),
+ pubd_sql_database = self.path("pubdb.sqlite3"))
+ else:
+ r.update(
+ irdbd_sql_database = "irdb%d" % self.engine,
+ rpkid_sql_database = "rpki%d" % self.engine,
+ pubd_sql_database = "pubd%d" % self.engine)
+
+ r.update(config_overrides)
+
+ with open(self.path("rpki.conf"), "w") as f:
+ f.write("# Automatically generated, do not edit\n")
+ print "Writing", f.name
+
+ section = None
+ for line in open(cleanpath(ca_dir, "examples/rpki.conf")):
+ 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 and option in r:
+ line = "%s = %s\n" % (option, r[option])
+ f.write(line)
+
+ def dump_rsyncd(self):
+ """
+ Write rsyncd configuration file.
+ """
+
+ if self.runs_pubd:
+ with open(self.path("rsyncd.conf"), "w") as f:
+ print "Writing", f.name
+ f.writelines(s + "\n" for s in
+ ("# Automatically generated, do not edit",
+ "port = %d" % self.rsync_port,
+ "address = localhost",
+ "[rpki]",
+ "log file = rsyncd.log",
+ "read only = yes",
+ "use chroot = no",
+ "path = %s" % self.path("publication"),
+ "comment = RPKI test",
+ "[root]",
+ "log file = rsyncd_root.log",
+ "read only = yes",
+ "use chroot = no",
+ "path = %s" % self.path("publication.root"),
+ "comment = RPKI test root"))
+
+ @classmethod
+ def next_rpkic_counter(cls):
+ cls.rpkic_counter += 10000
+ return str(cls.rpkic_counter)
+
+ def run_rpkic(self, *argv):
+ """
+ Run rpkic for this entity.
+ """
+
+ cmd = [prog_rpkic, "-i", self.name]
+ if args.profile:
+ cmd.append("--profile")
+ cmd.append(self.path("rpkic.%s.prof" % rpki.sundial.now()))
+ cmd.extend(str(a) for a in argv if a is not None)
+ print 'Running "%s"' % " ".join(cmd)
+ env = dict(os.environ,
+ YAMLTEST_RPKIC_COUNTER = self.next_rpkic_counter(),
+ RPKI_CONF = self.path("rpki.conf"))
+ subprocess.check_call(cmd, cwd = self.host.path(), env = env)
+
+ def syncdb(self):
+ """
+ Run whatever Django ORM commands are necessary to set up the
+ database this week.
+ """
+
+ # Fork a sub-process for each syncdb/migrate run, because it's
+ # easier than figuring out how to change Django settings after
+ # initialization.
+
+ def sync_settings(settings, verbosity = 1):
+
+ if verbosity > 0:
+ print "Running Django setup for", self.name
+
+ pid = os.fork()
+
+ if pid == 0:
+ logging.getLogger().setLevel(logging.WARNING)
+
+ os.environ.update(RPKI_CONF = self.path("rpki.conf"),
+ DJANGO_SETTINGS_MODULE = "rpki.django_settings." + settings)
+
+ import django
+ django.setup()
+
+ import django.core.management
+ django.core.management.call_command("migrate", verbosity = verbosity, no_color = True,
+ load_initial_data = False, interactive = False)
+
+ if settings in ("gui", "irdb"):
+ from django.contrib.auth.models import User
+ User.objects.create_superuser("root", "root@example.org", "fnord")
+
+ sys.exit(0)
+
+ elif os.waitpid(pid, 0)[1]:
+ raise RuntimeError("Django setup failed for %s %s" % (self.name, settings))
+
+ for settings in ("rpkid", "pubd", "gui"):
+ sync_settings(settings)
+
+ def run_python_daemon(self, prog):
+ """
+ Start a Python daemon and return a subprocess.Popen object
+ representing the running daemon.
+ """
+
+ basename = os.path.splitext(os.path.basename(prog))[0]
+ cmd = [prog, "--foreground", "--log-level", "debug",
+ "--log-file", self.path(basename + ".log")]
+ if args.profile and basename != "rootd":
+ cmd.extend((
+ "--profile", self.path(basename + ".prof")))
+ env = dict(os.environ, RPKI_CONF = self.path("rpki.conf"))
+ p = subprocess.Popen(cmd, cwd = self.path(), env = env)
+ print "Running %s for %s: pid %d process %r" % (" ".join(cmd), self.name, p.pid, p)
+ return p
+
+ def run_rpkid(self):
+ """
+ Run rpkid.
+ """
+
+ return self.run_python_daemon(prog_rpkid)
+
+ def run_irdbd(self):
+ """
+ Run irdbd.
+ """
+
+ return self.run_python_daemon(prog_irdbd)
+
+ def run_pubd(self):
+ """
+ Run pubd.
+ """
+
+ return self.run_python_daemon(prog_pubd)
+
+ def run_rootd(self):
+ """
+ Run rootd.
+ """
+
+ return self.run_python_daemon(prog_rootd)
+
+ def run_rsyncd(self):
+ """
+ Run rsyncd.
+ """
+
+ p = subprocess.Popen(("rsync", "--daemon", "--no-detach", "--config", "rsyncd.conf"),
+ cwd = self.path())
+ print "Running rsyncd for %s: pid %d process %r" % (self.name, p.pid, p)
+ return p
+
+ def run_gui(self):
+ """
+ Start an instance of the RPKI GUI under the Django test server and
+ return a subprocess.Popen object representing the running daemon.
+ """
- def run_pubd(self):
- """
- Run pubd.
- """
- return self.run_python_daemon(prog_pubd)
+ port = 8000 + self.engine
+ cmd = (prog_rpki_manage, "runserver", str(port))
+ env = dict(os.environ,
+ RPKI_CONF = self.path("rpki.conf"),
+ DJANGO_SETTINGS_MODULE = "rpki.django_settings.gui",
+ RPKI_DJANGO_DEBUG = "yes",
+ ALLOW_PLAIN_HTTP_FOR_TESTING = "I solemnly swear that I am not running this in production")
+ p = subprocess.Popen(cmd, cwd = self.path(), env = env,
+ stdout = open(self.path("gui.log"), "w"), stderr = subprocess.STDOUT)
+ print "Running %s for %s: pid %d process %r" % (" ".join(cmd), self.name, p.pid, p)
+ return p
- def run_rootd(self):
- """
- Run rootd.
- """
- return self.run_python_daemon(prog_rootd)
-
- def run_rsyncd(self):
- """
- Run rsyncd.
- """
- p = subprocess.Popen(("rsync", "--daemon", "--no-detach", "--config", "rsyncd.conf"),
- cwd = self.path())
- print "Running rsyncd for %s: pid %d process %r" % (self.name, p.pid, p)
- return p
def create_root_certificate(db_root):
- print "Creating rootd RPKI root certificate"
+ print "Creating rootd RPKI root certificate"
+
+ root_resources = rpki.resource_set.resource_bag(
+ asn = rpki.resource_set.resource_set_as("0-4294967295"),
+ v4 = rpki.resource_set.resource_set_ipv4("0.0.0.0/0"),
+ v6 = rpki.resource_set.resource_set_ipv6("::/0"))
- root_resources = rpki.resource_set.resource_bag(
- asn = rpki.resource_set.resource_set_as("0-4294967295"),
- v4 = rpki.resource_set.resource_set_ipv4("0.0.0.0/0"),
- v6 = rpki.resource_set.resource_set_ipv6("::/0"))
+ root_key = rpki.x509.RSA.generate(quiet = True)
- root_key = rpki.x509.RSA.generate(quiet = True)
+ root_uri = "rsync://localhost:%d/rpki/%s-root/root" % (db_root.pubd.rsync_port, db_root.name)
- root_uri = "rsync://localhost:%d/rpki/" % db_root.pubd.rsync_port
+ rrdp_uri = "http://localhost:%s/rrdp/notify.xml" % db.root.pubd.rrdp_port
- root_sia = (root_uri, root_uri + "root.mft", None)
+ root_sia = (root_uri + "/", root_uri + "/root.mft", None, rrdp_uri)
- root_cert = rpki.x509.X509.self_certify(
- keypair = root_key,
- subject_key = root_key.get_public(),
- serial = 1,
- sia = root_sia,
- notAfter = rpki.sundial.now() + rpki.sundial.timedelta(days = 365),
- resources = root_resources)
+ root_cert = rpki.x509.X509.self_certify(
+ keypair = root_key,
+ subject_key = root_key.get_public(),
+ serial = 1,
+ sia = root_sia,
+ notAfter = rpki.sundial.now() + rpki.sundial.timedelta(days = 365),
+ resources = root_resources)
- f = open(db_root.path("publication.root/root.cer"), "wb")
- f.write(root_cert.get_DER())
- f.close()
+ with open(db_root.path("root.cer"), "wb") as f:
+ f.write(root_cert.get_DER())
- f = open(db_root.path("root.key"), "wb")
- f.write(root_key.get_DER())
- f.close()
+ with open(db_root.path("root.key"), "wb") as f:
+ f.write(root_key.get_DER())
- f = open(os.path.join(test_dir, "root.tal"), "w")
- f.write("rsync://localhost:%d/root/root.cer\n\n" % db_root.pubd.rsync_port)
- f.write(root_key.get_public().get_Base64())
- f.close()
+ with open(os.path.join(test_dir, "root.tal"), "w") as f:
+ f.write(root_uri + ".cer\n\n")
+ f.write(root_key.get_public().get_Base64())
+logger = logging.getLogger(__name__)
-os.environ["TZ"] = "UTC"
+os.environ.update(DJANGO_SETTINGS_MODULE = "rpki.django_settings.irdb",
+ TZ = "UTC")
time.tzset()
parser = argparse.ArgumentParser(description = __doc__)
@@ -662,223 +758,268 @@ parser.add_argument("--synchronize", action = "store_true",
help = "synchronize IRDB with daemons")
parser.add_argument("--profile", action = "store_true",
help = "enable profiling")
+parser.add_argument("-g", "--run_gui", "--gui", action = "store_true",
+ help = "enable GUI using django-admin runserver")
+parser.add_argument("--no-browser", action = "store_true",
+ help = "don't create web browser tabs for GUI")
+parser.add_argument("--notify-when-startup-complete", type = int,
+ help = "send SIGUSR1 to this process when startup is complete")
parser.add_argument("--store-router-private-keys", action = "store_true",
help = "write generate router private keys to disk")
+parser.add_argument("--sql-engine", choices = ("mysql", "sqlite3", "postgresql"), default = "sqlite3",
+ help = "select SQL engine to use")
parser.add_argument("yaml_file", type = argparse.FileType("r"),
help = "YAML description of test network")
args = parser.parse_args()
try:
- if args.pidfile is not None:
- open(args.pidfile, "w").write("%s\n" % os.getpid())
+ if args.pidfile is not None:
+ with open(args.pidfile, "w") as f:
+ print "Writing pidfile", f.name
+ f.write("%s\n" % os.getpid())
- rpki.log.init("yamltest", argparse.Namespace(log_level = logging.DEBUG,
- log_handler = lambda: logging.StreamHandler(sys.stdout)))
+ rpki.log.init("yamltest", argparse.Namespace(log_level = logging.DEBUG,
+ log_handler = lambda: logging.StreamHandler(sys.stdout)))
- # Allow optional config file for this tool to override default
- # passwords: this is mostly so that I can show a complete working
- # example without publishing my own server's passwords.
+ # Allow optional config file for this tool to override default
+ # passwords: this is mostly so that I can show a complete working
+ # example without publishing my own server's passwords.
- cfg = rpki.config.parser(args.config, "yamltest", allow_missing = True)
+ cfg = rpki.config.parser(set_filename = args.config, section = "yamltest", allow_missing = True)
- only_one_pubd = cfg.getboolean("only_one_pubd", True)
- allocation.base_port = cfg.getint("base_port", 4400)
+ only_one_pubd = cfg.getboolean("only_one_pubd", True)
+ allocation.base_port = cfg.getint("base_port", 4400)
- config_overrides = dict(
- (k, cfg.get(k))
- for k in ("rpkid_sql_password", "irdbd_sql_password", "pubd_sql_password",
- "rpkid_sql_username", "irdbd_sql_username", "pubd_sql_username")
- if cfg.has_option(k))
+ config_overrides = dict(
+ (k, cfg.get(k))
+ for k in ("rpkid_sql_password", "irdbd_sql_password", "pubd_sql_password",
+ "rpkid_sql_username", "irdbd_sql_username", "pubd_sql_username")
+ if cfg.has_option(k))
- # Start clean, maybe
+ # Start clean, maybe
- if not args.skip_config:
- for root, dirs, files in os.walk(test_dir, topdown = False):
- for fn in files:
- os.unlink(os.path.join(root, fn))
- for d in dirs:
- os.rmdir(os.path.join(root, d))
+ if not args.skip_config:
+ for root, dirs, files in os.walk(test_dir, topdown = False):
+ for fn in files:
+ os.unlink(os.path.join(root, fn))
+ for d in dirs:
+ os.rmdir(os.path.join(root, d))
- # Read first YAML doc in file and process as compact description of
- # test layout and resource allocations. Ignore subsequent YAML docs,
- # they're for smoketest.py, not this script.
+ # Read first YAML doc in file and process as compact description of
+ # test layout and resource allocations. Ignore subsequent YAML docs,
+ # they're for smoketest.py, not this script.
- db = allocation_db(yaml.safe_load_all(args.yaml_file).next())
+ db = allocation_db(yaml.safe_load_all(args.yaml_file).next())
- # Show what we loaded
+ # Show what we loaded
- #db.dump()
+ #db.dump()
- if args.skip_config:
+ if args.skip_config:
- print "Skipping pre-daemon configuration, assuming you already did that"
+ print "Skipping pre-daemon configuration, assuming you already did that"
- else:
+ else:
- # Set up each entity in our test, create publication directories,
- # and initialize server BPKI.
+ # Set up each entity in our test, create publication directories,
+ # and initialize server BPKI.
- for d in db:
- if not d.is_hosted:
- os.makedirs(d.path())
- d.dump_conf()
- if d.runs_pubd:
- os.makedirs(d.path("publication"))
- d.dump_rsyncd()
- if d.is_root:
- os.makedirs(d.path("publication.root"))
- d.run_rpkic("initialize_server_bpki")
+ for d in db:
+ if not d.is_hosted:
+ print "Initializing", d.name
+ os.makedirs(d.path())
+ d.dump_conf()
+ if d.runs_pubd:
+ os.makedirs(d.path("publication"))
+ d.dump_rsyncd()
+ if d.is_root:
+ os.makedirs(d.path("publication.root"))
+ d.syncdb()
+ d.run_rpkic("initialize_server_bpki")
+ print
- # Initialize resource holding BPKI and generate self-descriptor
- # for each entity.
+ # Initialize resource holding BPKI and generate self-descriptor
+ # for each entity.
- for d in db:
- d.run_rpkic("create_identity", d.name)
+ for d in db:
+ d.run_rpkic("create_identity", d.name)
- # Create RPKI root certificate.
+ # Create RPKI root certificate.
- create_root_certificate(db.root)
+ create_root_certificate(db.root)
- # Set up rootd.
+ # Set up rootd.
- db.root.run_rpkic("configure_root")
+ db.root.run_rpkic("configure_root")
- # From here on we need to pay attention to initialization order. We
- # used to do all the pre-configure_daemons stuff before running any
- # of the daemons, but that doesn't work right in hosted cases, so we
- # have to interleave configuration with starting daemons, just as
- # one would in the real world for this sort of thing.
+ # From here on we need to pay attention to initialization order. We
+ # used to do all the pre-configure_daemons stuff before running any
+ # of the daemons, but that doesn't work right in hosted cases, so we
+ # have to interleave configuration with starting daemons, just as
+ # one would in the real world for this sort of thing.
- progs = []
+ progs = []
- try:
+ try:
- for d in db:
+ for d in db:
- if not d.is_hosted:
- print
- print "Running daemons for", d.name
- if d.is_root:
- progs.append(d.run_rootd())
- progs.append(d.run_irdbd())
- progs.append(d.run_rpkid())
- if d.runs_pubd:
- progs.append(d.run_pubd())
- progs.append(d.run_rsyncd())
-
- if args.synchronize or not args.skip_config:
-
- print
- print "Giving daemons time to start up"
- time.sleep(20)
- assert all(p.poll() is None for p in progs)
+ if not d.is_hosted:
+ print
+ print "Running daemons for", d.name
+ if d.is_root:
+ progs.append(d.run_rootd())
+ progs.append(d.run_irdbd())
+ progs.append(d.run_rpkid())
+ if d.runs_pubd:
+ progs.append(d.run_pubd())
+ progs.append(d.run_rsyncd())
+ if args.run_gui:
+ progs.append(d.run_gui())
- if args.skip_config:
+ if args.synchronize or not args.skip_config:
- print
- print "Skipping configure_*, you'll have to do that yourself if needed"
+ print
+ print "Giving daemons time to start up"
+ time.sleep(20)
+ assert all(p.poll() is None for p in progs)
- else:
+ if args.skip_config:
+
+ print
+ print "Skipping configure_*, you'll have to do that yourself if needed"
- for d in db:
+ else:
+
+ for d in db:
+
+ print
+ print "Configuring", d.name
+ print
+ if d.is_root:
+ assert not d.is_hosted
+ d.run_rpkic("configure_publication_client",
+ "--flat" if args.flat_publication else None,
+ d.path("%s.%s.repository-request.xml" % (d.name, d.name)))
+ print
+ d.run_rpkic("configure_repository",
+ d.path("%s.repository-response.xml" % d.client_handle))
+ print
+ else:
+ d.parent.run_rpkic("configure_child",
+ "--valid_until", d.resources.valid_until,
+ d.path("%s.identity.xml" % d.name))
+ print
+ d.run_rpkic("configure_parent",
+ d.parent.path("%s.%s.parent-response.xml" % (d.parent.name, d.name)))
+ print
+ d.pubd.run_rpkic("configure_publication_client",
+ "--flat" if args.flat_publication else None,
+ d.path("%s.%s.repository-request.xml" % (d.name, d.parent.name)))
+ print
+ d.run_rpkic("configure_repository",
+ d.pubd.path("%s.repository-response.xml" % d.client_handle))
+ print
+
+ print
+ print "Done with initial configuration"
+ print
+
+ if args.synchronize:
+ print
+ print "Synchronizing"
+ print
+ for d in db:
+ if not d.is_hosted:
+ d.run_rpkic("synchronize")
+
+ if args.synchronize or not args.skip_config:
+ print
+ print "Loading CSV files"
+ print
+ for d in db:
+ d.dump_asns()
+ d.dump_prefixes()
+ d.dump_roas()
+ d.dump_ghostbusters()
+ d.dump_router_certificates()
+
+ if args.run_gui:
+ print
+ print 'GUI user "root", password "fnord"'
+ gui_count = 0
+ for d in db:
+ if not d.is_hosted:
+ url = "http://127.0.0.1:%d/rpki/" % (8000 + d.engine)
+ print "GUI URL", url, "for", d.name
+ if not args.no_browser:
+ gui_count += 1
+ if d is db.root:
+ webbrowser.open_new(url)
+ else:
+ webbrowser.open_new_tab(url)
+ time.sleep(2)
+ if gui_count > 1:
+ print "Warning: Logging into more than one GUI instance at once will probably fail due to CSRF protection"
+
+ # Wait until something terminates.
+
+ if not args.stop_after_config or args.keep_going:
+ if args.notify_when_startup_complete:
+ print
+ print "Sending SIGUSR1 to process", args.notify_when_startup_complete
+ os.kill(args.notify_when_startup_complete, signal.SIGUSR1)
+ print
+ print "Waiting for daemons to exit"
+ signal.signal(signal.SIGCHLD, lambda *dont_care: None)
+ while (any(p.poll() is None for p in progs)
+ if args.keep_going else
+ all(p.poll() is None for p in progs)):
+ signal.pause()
+
+ finally:
print
- print "Configuring", d.name
+ print "Shutting down"
print
- if d.is_root:
- assert not d.is_hosted
- d.run_rpkic("configure_publication_client",
- "--flat" if args.flat_publication else None,
- d.path("%s.%s.repository-request.xml" % (d.name, d.name)))
- print
- d.run_rpkic("configure_repository",
- d.path("%s.repository-response.xml" % d.client_handle))
- print
+
+ signal.signal(signal.SIGCHLD, signal.SIG_DFL)
+
+ if args.profile:
+ how_long = 300
else:
- d.parent.run_rpkic("configure_child",
- "--valid_until", d.resources.valid_until,
- d.path("%s.identity.xml" % d.name))
- print
- d.run_rpkic("configure_parent",
- d.parent.path("%s.%s.parent-response.xml" % (d.parent.name, d.name)))
- print
- d.pubd.run_rpkic("configure_publication_client",
- "--flat" if args.flat_publication else None,
- d.path("%s.%s.repository-request.xml" % (d.name, d.parent.name)))
- print
- d.run_rpkic("configure_repository",
- d.pubd.path("%s.repository-response.xml" % d.client_handle))
- print
-
- print
- print "Done with initial configuration"
- print
-
- if args.synchronize:
- print
- print "Synchronizing"
- print
- for d in db:
- if not d.is_hosted:
- d.run_rpkic("synchronize")
-
- if args.synchronize or not args.skip_config:
- print
- print "Loading CSV files"
- print
- for d in db:
- d.dump_asns()
- d.dump_prefixes()
- d.dump_roas()
- d.dump_ghostbusters()
- d.dump_router_certificates()
-
- # Wait until something terminates.
-
- if not args.stop_after_config or args.keep_going:
- print
- print "Waiting for daemons to exit"
- signal.signal(signal.SIGCHLD, lambda *dont_care: None)
- while (any(p.poll() is None for p in progs)
- if args.keep_going else
- all(p.poll() is None for p in progs)):
- signal.pause()
-
- finally:
-
- print
- print "Shutting down"
- print
-
- signal.signal(signal.SIGCHLD, signal.SIG_DFL)
-
- if args.profile:
- how_long = 300
- else:
- how_long = 30
+ how_long = 30
+
+ how_often = how_long / 2
- how_often = how_long / 2
+ for i in xrange(how_long):
+ if i % how_often == 0:
+ for p in progs:
+ if p.poll() is None:
+ print "Politely nudging pid %d" % p.pid
+ p.terminate()
+ print
+ if all(p.poll() is not None for p in progs):
+ break
+ time.sleep(1)
- for i in xrange(how_long):
- if i % how_often == 0:
for p in progs:
- if p.poll() is None:
- print "Politely nudging pid %d" % p.pid
- p.terminate()
- print
- if all(p.poll() is not None for p in progs):
- break
- time.sleep(1)
+ if p.poll() is None:
+ print "Pulling the plug on pid %d" % p.pid
+ p.kill()
- for p in progs:
- if p.poll() is None:
- print "Pulling the plug on pid %d" % p.pid
- p.kill()
+ for p in progs:
+ print "Program pid %d %r returned %d" % (p.pid, p, p.wait())
- for p in progs:
- print "Program pid %d %r returned %d" % (p.pid, p, p.wait())
+except Exception, e:
+ print "Blowing out on exception", str(e)
+ raise
finally:
- if args.pidfile is not None:
- os.unlink(args.pidfile)
+ if args.pidfile is not None and os.path.exists(args.pidfile):
+ os.unlink(args.pidfile)
+
+# Local Variables:
+# indent-tabs-mode: nil
+# End: