diff options
-rw-r--r-- | myrpki.rototill/Makefile | 20 | ||||
-rw-r--r-- | myrpki.rototill/examples/myrpki.conf | 13 | ||||
-rw-r--r-- | myrpki.rototill/myirbe.py | 37 | ||||
-rw-r--r-- | myrpki.rototill/myrpki.py | 121 | ||||
-rw-r--r-- | myrpki.rototill/myrpki.rnc (renamed from myrpki.rototill/schema.rnc) | 13 | ||||
-rw-r--r-- | myrpki.rototill/myrpki.rng (renamed from myrpki.rototill/schema.rng) | 37 | ||||
-rw-r--r-- | myrpki.rototill/schema.py | 199 | ||||
-rw-r--r-- | myrpki.rototill/xml-parse-test.py | 5 | ||||
-rw-r--r-- | myrpki.rototill/yamltest.py | 2 |
9 files changed, 157 insertions, 290 deletions
diff --git a/myrpki.rototill/Makefile b/myrpki.rototill/Makefile index 73794508..a8306e30 100644 --- a/myrpki.rototill/Makefile +++ b/myrpki.rototill/Makefile @@ -1,20 +1,14 @@ # $Id$ -all: schema.py +all: myrpki.rng -lint: myrpki.xml schema.rng - xmllint --noout --relaxng schema.rng myrpki.xml +lint: myrpki.xml myrpki.rng + xmllint --noout --relaxng myrpki.rng myrpki.xml -schema.rng: schema.rnc - trang schema.rnc schema.rng +myrpki.rng: myrpki.rnc + trang myrpki.rnc myrpki.rng -schema.py: schema.rng - echo >$@ 'import lxml.etree' - echo >>$@ -n "myrpki = lxml.etree.RelaxNG(lxml.etree.fromstring('''" - cat >>$@ schema.rng - echo >>$@ "'''))" - -parse: myrpki.xml schema.py +parse: myrpki.xml all python xml-parse-test.py clean: @@ -35,5 +29,5 @@ backup: tar cvvzf test.$$(TZ='' date +%Y.%m.%d.%H.%M.%S).tgz screenlog.* test backup.*.sql rm backup.*.sql -test: schema.py +test: all python yamltest.py diff --git a/myrpki.rototill/examples/myrpki.conf b/myrpki.rototill/examples/myrpki.conf index 7cf80eb6..79f1d229 100644 --- a/myrpki.rototill/examples/myrpki.conf +++ b/myrpki.rototill/examples/myrpki.conf @@ -26,19 +26,6 @@ handle = Me -# BPKI trust anchor for the repository in which this <self/> will be -# publishing its outputs. You need to set this. - -repository_bpki_certificate = repository-ta.cer - -# Name by which repository will know this <self/>. This may be a -# structured handle, eg, "Grandma/Mom/Me" or might be a simple handle, -# depending on how the repository is set up. Syntax is same as -# "handle", with the addition of "/" characters as an allowed -# delimiter. You need to set this. - -repository_handle = ${myrpki::handle} - # Names of various files and directories. Don't change these without # a good reason. diff --git a/myrpki.rototill/myirbe.py b/myrpki.rototill/myirbe.py index 35de6ff1..d9c75ddc 100644 --- a/myrpki.rototill/myirbe.py +++ b/myrpki.rototill/myirbe.py @@ -65,11 +65,10 @@ def findbase64(tree, name, b64type = rpki.x509.X509): x = tree.findtext(name) return b64type(Base64 = x) if x else None -# For simple cases we don't really care what these value are, so long -# as we're consistant about them, so wiring them in is fine. +# For simple cases we don't really care what this value is, so long as +# we're consistant about it, so wiring this in is fine. bsc_handle = "bsc" -repository_handle = "repository" os.environ["TZ"] = "UTC" time.tzset() @@ -315,18 +314,18 @@ for xmlfile in xmlfiles: if bsc_pdu and bsc_pdu.pkcs10_request: bsc_req = bsc_pdu.pkcs10_request - # In general we need one <repository/> per publication daemon with - # whom this <self/> has a relationship. In practice there is rarely - # (never?) a good reason for a single <self/> to use multiple - # publication services, so in normal use we only need one - # <repository/> object. If for some reason you really need more - # than this, you'll have to hack. + # At present we need one <repository/> per <parent/>, not because + # rpkid requires that, but because pubd does. pubd probably should + # be fixed to support a single client allowed to update multiple + # trees, but for the moment the easiest way forward is just to + # enforce a 1:1 mapping between <parent/> and <repository/> objects - repository_cert = findbase64(tree, "bpki_repository_certificate") - if repository_cert: + for repository in tree.getiterator("repository"): + repository_handle = repository.get("handle") repository_pdu = repository_pdus.pop(repository_handle, None) - repository_uri = pubd_base + "client/" + tree.get("repository_handle") + repository_uri = repository.get("service_uri") + repository_cert = findbase64(repository, "bpki_certificate") if (repository_pdu is None or repository_pdu.bsc_handle != bsc_handle or @@ -344,12 +343,10 @@ for xmlfile in xmlfiles: rpkid_query.extend(rpki.left_right.repository_elt.make_pdu( action = "destroy", self_handle = handle, repository_handle = r) for r in repository_pdus) - # <parent/> setup code here used to be ridiculously complex. Most - # of the insanity was due to a misguided attempt to deduce pubd - # setup from other data; now that pubd setup is driven by - # pubclients.csv, parent setup should be relatively straightforward, - # but beware of lingering excessive cleverness in anything dealing - # with parent objects in this script. + # <parent/> setup code currently assumes 1:1 mapping between + # <repository/> and <parent/>, and further assumes that the handles + # for an associated pair are the identical (that is: + # parent.repository_handle == parent.parent_handle). for parent in tree.getiterator("parent"): @@ -363,7 +360,7 @@ for xmlfile in xmlfiles: if (parent_pdu is None or parent_pdu.bsc_handle != bsc_handle or - parent_pdu.repository_handle != repository_handle or + parent_pdu.repository_handle != parent_handle or parent_pdu.peer_contact_uri != parent_uri or parent_pdu.sia_base != parent_sia_base or parent_pdu.sender_name != parent_myhandle or @@ -376,7 +373,7 @@ for xmlfile in xmlfiles: self_handle = handle, parent_handle = parent_handle, bsc_handle = bsc_handle, - repository_handle = repository_handle, + repository_handle = parent_handle, peer_contact_uri = parent_uri, sia_base = parent_sia_base, sender_name = parent_myhandle, diff --git a/myrpki.rototill/myrpki.py b/myrpki.rototill/myrpki.py index 85546663..5ffbd963 100644 --- a/myrpki.rototill/myrpki.py +++ b/myrpki.rototill/myrpki.py @@ -60,7 +60,7 @@ except ImportError: # Our XML namespace and protocol version. namespace = "http://www.hactrn.net/uris/rpki/myrpki/" -version = "1" +version = "2" namespaceQName = "{" + namespace + "}" # Dialect for our use of CSV files, here to make it easy to change if @@ -374,9 +374,9 @@ class parents(dict): c.xml(e) @classmethod - def from_csv(cls, parents_csv_file, fxcert, entitydb): + def from_csv(cls, fxcert, entitydb): """ - Parse parent data from CSV file. + Parse parent data from entitydb. """ self = cls() for f in entitydb.iterate("parents", "*.xml"): @@ -393,6 +393,83 @@ class parents(dict): sia_base = r.get("sia_base")) return self +class repository(object): + """ + Representation of one repository entity. + """ + + def __init__(self, handle): + self.handle = handle + self.service_uri = None + self.bpki_certificate = None + + def __repr__(self): + s = "<%s %s" % (self.__class__.__name__, self.handle) + if self.service_uri: + s += " uri %s" % self.service_uri + if self.bpki_certificate: + s += " cert %s" % self.bpki_certificate + return s + ">" + + def add(self, service_uri = None, bpki_certificate = None): + """ + Add service URI or BPKI certificates to this repository object. + """ + if service_uri is not None: + self.service_uri = service_uri + if bpki_certificate is not None: + self.bpki_certificate = bpki_certificate + + def xml(self, e): + """ + Render this repository object to XML. + """ + complete = self.bpki_certificate and self.service_uri + if whine and not complete: + print "Incomplete repository entry %s" % self + if complete or allow_incomplete: + e = SubElement(e, "repository", + handle = self.handle, + service_uri = self.service_uri) + e.tail = "\n" + if self.bpki_certificate: + PEMElement(e, "bpki_certificate", self.bpki_certificate) + +class repositories(dict): + """ + Database of repository objects. + """ + + def add(self, handle, + service_uri = None, + bpki_certificate = None): + """ + Add service URI or certificate to repository object, creating it if necessary. + """ + if handle not in self: + self[handle] = repository(handle) + self[handle].add(service_uri = service_uri, + bpki_certificate = bpki_certificate) + + def xml(self, e): + for c in self.itervalues(): + c.xml(e) + + @classmethod + def from_csv(cls, fxcert, entitydb): + """ + Parse repository data from entitydb. + """ + self = cls() + for f in entitydb.iterate("repositories", "*.xml"): + h = os.path.splitext(os.path.split(f)[-1])[0] + r = etree_read(f) + assert r.get("type") == "confirmed" + self.add(handle = h, + service_uri = r.get("service_uri"), + bpki_certificate = fxcert(r.findtext("bpki_server_ta"))) + return self + def csv_open(filename): """ Open a CSV file, with settings that make it a tab-delimited file. @@ -611,14 +688,21 @@ class CA(object): return xcert def etree_validate(e): - try: - import schema - schema.myrpki.assertValid(e) - except lxml.etree.DocumentInvalid: - print lxml.etree.tostring(e, pretty_print = True) - raise - except ImportError: - print "Couldn't import RelaxNG schema, validation disabled" + # This is a kludge, schema should be loaded as module or configured + # in .conf, but it will do as a temporary debugging hack. + schema = os.getenv("MYRPKI_RNG") + if schema: + try: + import lxml.etree + except ImportError: + return + try: + lxml.etree.RelaxNG(file = schema).assertValid(e) + except lxml.etree.RelaxNGParseError: + return + except lxml.etree.DocumentInvalid: + print lxml.etree.tostring(e, pretty_print = True) + raise def etree_write(e, filename, verbose = True, validate = False): """ @@ -680,13 +764,10 @@ def main(argv = ()): my_handle = cfg.get("handle") roa_csv_file = cfg.get("roa_csv") children_csv_file = cfg.get("children_csv") - parents_csv_file = cfg.get("parents_csv") prefix_csv_file = cfg.get("prefix_csv") asn_csv_file = cfg.get("asn_csv") bpki_dir = cfg.get("bpki_resources_directory") xml_filename = cfg.get("xml_filename") - repository_bpki_certificate = cfg.get("repository_bpki_certificate") - repository_handle = cfg.get("repository_handle") global openssl openssl = cfg.get("openssl", "openssl") @@ -700,7 +781,7 @@ def main(argv = ()): except IOError: bsc_req, bsc_cer = None, None - e = Element("myrpki", handle = my_handle, repository_handle = repository_handle) + e = Element("myrpki", handle = my_handle) roa_requests.from_csv(roa_csv_file).xml(e) @@ -711,17 +792,13 @@ def main(argv = ()): fxcert = bpki.fxcert, entitydb = entitydb).xml(e) - parents.from_csv( - parents_csv_file = parents_csv_file, - fxcert = bpki.fxcert, - entitydb = entitydb).xml(e) + parents.from_csv(fxcert = bpki.fxcert,entitydb = entitydb).xml(e) + + repositories.from_csv(fxcert = bpki.fxcert,entitydb = entitydb).xml(e) PEMElement(e, "bpki_ca_certificate", bpki.cer) PEMElement(e, "bpki_crl", bpki.crl) - if os.path.exists(repository_bpki_certificate): - PEMElement(e, "bpki_repository_certificate", bpki.xcert(repository_bpki_certificate)) - if bsc_cer: PEMElement(e, "bpki_bsc_certificate", bsc_cer) diff --git a/myrpki.rototill/schema.rnc b/myrpki.rototill/myrpki.rnc index 8ec48195..29db7a67 100644 --- a/myrpki.rototill/schema.rnc +++ b/myrpki.rototill/myrpki.rnc @@ -16,15 +16,14 @@ ipv4_list = xsd:string { maxLength="512000" pattern="[\-,0-9/.]*" } ipv6_list = xsd:string { maxLength="512000" pattern="[\-,0-9/:a-fA-F]*" } start = element myrpki { - attribute version { xsd:positiveInteger { maxInclusive="1" } }, + attribute version { "2" }, attribute handle { object_handle }, - attribute repository_handle { pubd_handle }, roa_request_elt*, child_elt*, parent_elt*, + repository_elt*, bpki_ca_certificate_elt?, bpki_crl_elt?, - bpki_repository_certificate_elt?, bpki_bsc_certificate_elt?, bpki_bsc_pkcs10_elt? } @@ -53,9 +52,15 @@ parent_elt = element parent { element bpki_https_certificate { base64 }? } +repository_elt = element repository { + attribute handle { object_handle }, + attribute service_uri { uri }?, + element bpki_certificate { base64 }? +} + bpki_ca_certificate_elt = element bpki_ca_certificate { base64 } bpki_crl_elt = element bpki_crl { base64 } -bpki_repository_certificate_elt = element bpki_repository_certificate { base64 } + bpki_bsc_certificate_elt = element bpki_bsc_certificate { base64 } bpki_bsc_pkcs10_elt = element bpki_bsc_pkcs10 { base64 } diff --git a/myrpki.rototill/schema.rng b/myrpki.rototill/myrpki.rng index 0d71e0b7..0ed4123a 100644 --- a/myrpki.rototill/schema.rng +++ b/myrpki.rototill/myrpki.rng @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - $Id: schema.rnc 2958 2010-01-22 21:55:09Z sra $ + $Id: myrpki.rnc -1 $ RelaxNG Schema for MyRPKI XML messages @@ -51,16 +51,11 @@ <start> <element name="myrpki"> <attribute name="version"> - <data type="positiveInteger"> - <param name="maxInclusive">1</param> - </data> + <value>2</value> </attribute> <attribute name="handle"> <ref name="object_handle"/> </attribute> - <attribute name="repository_handle"> - <ref name="pubd_handle"/> - </attribute> <zeroOrMore> <ref name="roa_request_elt"/> </zeroOrMore> @@ -70,6 +65,9 @@ <zeroOrMore> <ref name="parent_elt"/> </zeroOrMore> + <zeroOrMore> + <ref name="repository_elt"/> + </zeroOrMore> <optional> <ref name="bpki_ca_certificate_elt"/> </optional> @@ -77,9 +75,6 @@ <ref name="bpki_crl_elt"/> </optional> <optional> - <ref name="bpki_repository_certificate_elt"/> - </optional> - <optional> <ref name="bpki_bsc_certificate_elt"/> </optional> <optional> @@ -164,6 +159,23 @@ </optional> </element> </define> + <define name="repository_elt"> + <element name="repository"> + <attribute name="handle"> + <ref name="object_handle"/> + </attribute> + <optional> + <attribute name="service_uri"> + <ref name="uri"/> + </attribute> + </optional> + <optional> + <element name="bpki_certificate"> + <ref name="base64"/> + </element> + </optional> + </element> + </define> <define name="bpki_ca_certificate_elt"> <element name="bpki_ca_certificate"> <ref name="base64"/> @@ -174,11 +186,6 @@ <ref name="base64"/> </element> </define> - <define name="bpki_repository_certificate_elt"> - <element name="bpki_repository_certificate"> - <ref name="base64"/> - </element> - </define> <define name="bpki_bsc_certificate_elt"> <element name="bpki_bsc_certificate"> <ref name="base64"/> diff --git a/myrpki.rototill/schema.py b/myrpki.rototill/schema.py deleted file mode 100644 index 651f1938..00000000 --- a/myrpki.rototill/schema.py +++ /dev/null @@ -1,199 +0,0 @@ -import lxml.etree -myrpki = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" encoding="UTF-8"?> -<!-- - $Id: schema.rnc 2958 2010-01-22 21:55:09Z sra $ - - RelaxNG Schema for MyRPKI XML messages - - libxml2 (including xmllint) only groks the XML syntax of RelaxNG, so - run the compact syntax through trang to get XML syntax. ---> -<grammar ns="http://www.hactrn.net/uris/rpki/myrpki/" xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> - <define name="base64"> - <data type="base64Binary"> - <param name="maxLength">512000</param> - </data> - </define> - <define name="object_handle"> - <data type="string"> - <param name="maxLength">255</param> - <param name="pattern">[\-_A-Za-z0-9]*</param> - </data> - </define> - <define name="pubd_handle"> - <data type="string"> - <param name="maxLength">255</param> - <param name="pattern">[\-_A-Za-z0-9/]*</param> - </data> - </define> - <define name="uri"> - <data type="anyURI"> - <param name="maxLength">4096</param> - </data> - </define> - <define name="asn_list"> - <data type="string"> - <param name="maxLength">512000</param> - <param name="pattern">[\-,0-9]*</param> - </data> - </define> - <define name="ipv4_list"> - <data type="string"> - <param name="maxLength">512000</param> - <param name="pattern">[\-,0-9/.]*</param> - </data> - </define> - <define name="ipv6_list"> - <data type="string"> - <param name="maxLength">512000</param> - <param name="pattern">[\-,0-9/:a-fA-F]*</param> - </data> - </define> - <start> - <element name="myrpki"> - <attribute name="version"> - <data type="positiveInteger"> - <param name="maxInclusive">1</param> - </data> - </attribute> - <attribute name="handle"> - <ref name="object_handle"/> - </attribute> - <attribute name="repository_handle"> - <ref name="pubd_handle"/> - </attribute> - <zeroOrMore> - <ref name="roa_request_elt"/> - </zeroOrMore> - <zeroOrMore> - <ref name="child_elt"/> - </zeroOrMore> - <zeroOrMore> - <ref name="parent_elt"/> - </zeroOrMore> - <optional> - <ref name="bpki_ca_certificate_elt"/> - </optional> - <optional> - <ref name="bpki_crl_elt"/> - </optional> - <optional> - <ref name="bpki_repository_certificate_elt"/> - </optional> - <optional> - <ref name="bpki_bsc_certificate_elt"/> - </optional> - <optional> - <ref name="bpki_bsc_pkcs10_elt"/> - </optional> - </element> - </start> - <define name="roa_request_elt"> - <element name="roa_request"> - <attribute name="asn"> - <data type="positiveInteger"/> - </attribute> - <attribute name="v4"> - <ref name="ipv4_list"/> - </attribute> - <attribute name="v6"> - <ref name="ipv6_list"/> - </attribute> - </element> - </define> - <define name="child_elt"> - <element name="child"> - <attribute name="handle"> - <ref name="object_handle"/> - </attribute> - <attribute name="valid_until"> - <data type="dateTime"> - <param name="pattern">.*Z</param> - </data> - </attribute> - <optional> - <attribute name="asns"> - <ref name="asn_list"/> - </attribute> - </optional> - <optional> - <attribute name="v4"> - <ref name="ipv4_list"/> - </attribute> - </optional> - <optional> - <attribute name="v6"> - <ref name="ipv6_list"/> - </attribute> - </optional> - <optional> - <element name="bpki_certificate"> - <ref name="base64"/> - </element> - </optional> - </element> - </define> - <define name="parent_elt"> - <element name="parent"> - <attribute name="handle"> - <ref name="object_handle"/> - </attribute> - <optional> - <attribute name="service_uri"> - <ref name="uri"/> - </attribute> - </optional> - <optional> - <attribute name="myhandle"> - <ref name="object_handle"/> - </attribute> - </optional> - <optional> - <attribute name="sia_base"> - <ref name="uri"/> - </attribute> - </optional> - <optional> - <element name="bpki_cms_certificate"> - <ref name="base64"/> - </element> - </optional> - <optional> - <element name="bpki_https_certificate"> - <ref name="base64"/> - </element> - </optional> - </element> - </define> - <define name="bpki_ca_certificate_elt"> - <element name="bpki_ca_certificate"> - <ref name="base64"/> - </element> - </define> - <define name="bpki_crl_elt"> - <element name="bpki_crl"> - <ref name="base64"/> - </element> - </define> - <define name="bpki_repository_certificate_elt"> - <element name="bpki_repository_certificate"> - <ref name="base64"/> - </element> - </define> - <define name="bpki_bsc_certificate_elt"> - <element name="bpki_bsc_certificate"> - <ref name="base64"/> - </element> - </define> - <define name="bpki_bsc_pkcs10_elt"> - <element name="bpki_bsc_pkcs10"> - <ref name="base64"/> - </element> - </define> -</grammar> -<!-- - Local Variables: - indent-tabs-mode: nil - End: ---> -''')) diff --git a/myrpki.rototill/xml-parse-test.py b/myrpki.rototill/xml-parse-test.py index d5f8e007..e32241ed 100644 --- a/myrpki.rototill/xml-parse-test.py +++ b/myrpki.rototill/xml-parse-test.py @@ -19,14 +19,15 @@ PERFORMANCE OF THIS SOFTWARE. """ import lxml.etree, rpki.resource_set, base64, subprocess -import schema + +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) -schema.myrpki.assertValid(tree) +relaxng.assertValid(tree) def showitems(x): if False: diff --git a/myrpki.rototill/yamltest.py b/myrpki.rototill/yamltest.py index e909053d..7fb7b169 100644 --- a/myrpki.rototill/yamltest.py +++ b/myrpki.rototill/yamltest.py @@ -415,8 +415,6 @@ class allocation(object): s = self.find_pubd() r["myrpki", "pubd_server_host"] = "localhost" r["myrpki", "pubd_server_port"] = str(s.pubd_port) - r["myrpki", "repository_bpki_certificate"] = s.path("bpki/servers/ca.cer") - r["myrpki", "repository_handle"] = self.client_handle r["myrpki", "publication_rsync_server"] = "localhost:%s" % s.rsync_port if rpkid_password: |