diff options
Diffstat (limited to 'rpki/irdb')
-rw-r--r-- | rpki/irdb/__init__.py | 1 | ||||
-rw-r--r-- | rpki/irdb/migrations/0001_initial.py | 375 | ||||
-rw-r--r-- | rpki/irdb/migrations/0002_remove_client_parent_handle.py | 18 | ||||
-rw-r--r-- | rpki/irdb/migrations/0003_repository_rrdp_notification_uri.py | 19 | ||||
-rw-r--r-- | rpki/irdb/migrations/__init__.py | 0 | ||||
-rw-r--r-- | rpki/irdb/models.py | 126 | ||||
-rw-r--r-- | rpki/irdb/router.py | 2 | ||||
-rw-r--r-- | rpki/irdb/zookeeper.py | 1122 |
8 files changed, 1018 insertions, 645 deletions
diff --git a/rpki/irdb/__init__.py b/rpki/irdb/__init__.py index 7f3b880e..25dedfe3 100644 --- a/rpki/irdb/__init__.py +++ b/rpki/irdb/__init__.py @@ -21,6 +21,5 @@ Python package, so humor it. # pylint: disable=W0401 -from rpki.irdb.models import * from rpki.irdb.zookeeper import Zookeeper from rpki.irdb.router import DBContextRouter, database diff --git a/rpki/irdb/migrations/0001_initial.py b/rpki/irdb/migrations/0001_initial.py new file mode 100644 index 00000000..d10b62d3 --- /dev/null +++ b/rpki/irdb/migrations/0001_initial.py @@ -0,0 +1,375 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import rpki.irdb.models +import rpki.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='BSC', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('handle', rpki.irdb.models.HandleField(max_length=120)), + ('pkcs10', rpki.fields.PKCS10Field(default=None, serialize=False, blank=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Child', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('handle', rpki.irdb.models.HandleField(max_length=120)), + ('ta', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('valid_until', rpki.fields.SundialField()), + ('name', models.TextField(null=True, blank=True)), + ], + ), + migrations.CreateModel( + name='ChildASN', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('start_as', models.BigIntegerField()), + ('end_as', models.BigIntegerField()), + ('child', models.ForeignKey(related_name='asns', to='irdb.Child')), + ], + ), + migrations.CreateModel( + name='ChildNet', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('start_ip', models.CharField(max_length=40)), + ('end_ip', models.CharField(max_length=40)), + ('version', rpki.fields.EnumField(choices=[(4, b'IPv4'), (6, b'IPv6')])), + ('child', models.ForeignKey(related_name='address_ranges', to='irdb.Child')), + ], + ), + migrations.CreateModel( + name='Client', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('handle', rpki.irdb.models.HandleField(max_length=120)), + ('ta', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('sia_base', models.TextField()), + ('parent_handle', rpki.irdb.models.HandleField(max_length=120)), + ], + ), + migrations.CreateModel( + name='EECertificateRequest', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('valid_until', rpki.fields.SundialField()), + ('pkcs10', rpki.fields.PKCS10Field(default=None, serialize=False, blank=True)), + ('gski', models.CharField(max_length=27)), + ('cn', models.CharField(max_length=64)), + ('sn', models.CharField(max_length=64)), + ('eku', models.TextField(null=True)), + ], + ), + migrations.CreateModel( + name='EECertificateRequestASN', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('start_as', models.BigIntegerField()), + ('end_as', models.BigIntegerField()), + ('ee_certificate_request', models.ForeignKey(related_name='asns', to='irdb.EECertificateRequest')), + ], + ), + migrations.CreateModel( + name='EECertificateRequestNet', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('start_ip', models.CharField(max_length=40)), + ('end_ip', models.CharField(max_length=40)), + ('version', rpki.fields.EnumField(choices=[(4, b'IPv4'), (6, b'IPv6')])), + ('ee_certificate_request', models.ForeignKey(related_name='address_ranges', to='irdb.EECertificateRequest')), + ], + ), + migrations.CreateModel( + name='GhostbusterRequest', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('vcard', models.TextField()), + ], + ), + migrations.CreateModel( + name='HostedCA', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ], + ), + migrations.CreateModel( + name='Referral', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('private_key', rpki.fields.KeyField(default=None, serialize=False, blank=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Repository', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('handle', rpki.irdb.models.HandleField(max_length=120)), + ('ta', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('client_handle', rpki.irdb.models.HandleField(max_length=120)), + ('service_uri', models.CharField(max_length=255)), + ('sia_base', models.TextField()), + ], + ), + migrations.CreateModel( + name='ResourceHolderCA', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('private_key', rpki.fields.KeyField(default=None, serialize=False, blank=True)), + ('latest_crl', rpki.fields.CRLField(default=None, serialize=False, blank=True)), + ('next_serial', models.BigIntegerField(default=1)), + ('next_crl_number', models.BigIntegerField(default=1)), + ('last_crl_update', rpki.fields.SundialField()), + ('next_crl_update', rpki.fields.SundialField()), + ('handle', rpki.irdb.models.HandleField(unique=True, max_length=120)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ResourceHolderRevocation', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('serial', models.BigIntegerField()), + ('revoked', rpki.fields.SundialField()), + ('expires', rpki.fields.SundialField()), + ('issuer', models.ForeignKey(related_name='revocations', to='irdb.ResourceHolderCA')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ROARequest', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('asn', models.BigIntegerField()), + ('issuer', models.ForeignKey(related_name='roa_requests', to='irdb.ResourceHolderCA')), + ], + ), + migrations.CreateModel( + name='ROARequestPrefix', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('version', rpki.fields.EnumField(choices=[(4, b'IPv4'), (6, b'IPv6')])), + ('prefix', models.CharField(max_length=40)), + ('prefixlen', models.PositiveSmallIntegerField()), + ('max_prefixlen', models.PositiveSmallIntegerField()), + ('roa_request', models.ForeignKey(related_name='prefixes', to='irdb.ROARequest')), + ], + ), + migrations.CreateModel( + name='ServerCA', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('private_key', rpki.fields.KeyField(default=None, serialize=False, blank=True)), + ('latest_crl', rpki.fields.CRLField(default=None, serialize=False, blank=True)), + ('next_serial', models.BigIntegerField(default=1)), + ('next_crl_number', models.BigIntegerField(default=1)), + ('last_crl_update', rpki.fields.SundialField()), + ('next_crl_update', rpki.fields.SundialField()), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='ServerEE', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('private_key', rpki.fields.KeyField(default=None, serialize=False, blank=True)), + ('purpose', rpki.fields.EnumField(choices=[(1, b'rpkid'), (2, b'pubd'), (3, b'irdbd'), (4, b'irbe')])), + ('issuer', models.ForeignKey(related_name='ee_certificates', to='irdb.ServerCA')), + ], + ), + migrations.CreateModel( + name='ServerRevocation', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('serial', models.BigIntegerField()), + ('revoked', rpki.fields.SundialField()), + ('expires', rpki.fields.SundialField()), + ('issuer', models.ForeignKey(related_name='revocations', to='irdb.ServerCA')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Turtle', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('service_uri', models.CharField(max_length=255)), + ], + ), + migrations.CreateModel( + name='Parent', + fields=[ + ('turtle_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='irdb.Turtle')), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('handle', rpki.irdb.models.HandleField(max_length=120)), + ('ta', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('parent_handle', rpki.irdb.models.HandleField(max_length=120)), + ('child_handle', rpki.irdb.models.HandleField(max_length=120)), + ('repository_type', rpki.fields.EnumField(choices=[(1, b'none'), (2, b'offer'), (3, b'referral')])), + ('referrer', rpki.irdb.models.HandleField(max_length=120, null=True, blank=True)), + ('referral_authorization', rpki.irdb.models.SignedReferralField(default=None, serialize=False, null=True, blank=True)), + ('issuer', models.ForeignKey(related_name='parents', to='irdb.ResourceHolderCA')), + ], + bases=('irdb.turtle', models.Model), + ), + migrations.CreateModel( + name='Rootd', + fields=[ + ('turtle_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='irdb.Turtle')), + ('certificate', rpki.fields.CertificateField(default=None, serialize=False, blank=True)), + ('private_key', rpki.fields.KeyField(default=None, serialize=False, blank=True)), + ('issuer', models.OneToOneField(related_name='rootd', to='irdb.ResourceHolderCA')), + ], + options={ + 'abstract': False, + }, + bases=('irdb.turtle', models.Model), + ), + migrations.AddField( + model_name='repository', + name='issuer', + field=models.ForeignKey(related_name='repositories', to='irdb.ResourceHolderCA'), + ), + migrations.AddField( + model_name='repository', + name='turtle', + field=models.OneToOneField(related_name='repository', to='irdb.Turtle'), + ), + migrations.AddField( + model_name='referral', + name='issuer', + field=models.OneToOneField(related_name='referral_certificate', to='irdb.ResourceHolderCA'), + ), + migrations.AddField( + model_name='hostedca', + name='hosted', + field=models.OneToOneField(related_name='hosted_by', to='irdb.ResourceHolderCA'), + ), + migrations.AddField( + model_name='hostedca', + name='issuer', + field=models.ForeignKey(to='irdb.ServerCA'), + ), + migrations.AddField( + model_name='ghostbusterrequest', + name='issuer', + field=models.ForeignKey(related_name='ghostbuster_requests', to='irdb.ResourceHolderCA'), + ), + migrations.AddField( + model_name='eecertificaterequest', + name='issuer', + field=models.ForeignKey(related_name='ee_certificate_requests', to='irdb.ResourceHolderCA'), + ), + migrations.AddField( + model_name='client', + name='issuer', + field=models.ForeignKey(related_name='clients', to='irdb.ServerCA'), + ), + migrations.AddField( + model_name='child', + name='issuer', + field=models.ForeignKey(related_name='children', to='irdb.ResourceHolderCA'), + ), + migrations.AddField( + model_name='bsc', + name='issuer', + field=models.ForeignKey(related_name='bscs', to='irdb.ResourceHolderCA'), + ), + migrations.AlterUniqueTogether( + name='serverrevocation', + unique_together=set([('issuer', 'serial')]), + ), + migrations.AlterUniqueTogether( + name='serveree', + unique_together=set([('issuer', 'purpose')]), + ), + migrations.AlterUniqueTogether( + name='roarequestprefix', + unique_together=set([('roa_request', 'version', 'prefix', 'prefixlen', 'max_prefixlen')]), + ), + migrations.AlterUniqueTogether( + name='resourceholderrevocation', + unique_together=set([('issuer', 'serial')]), + ), + migrations.AlterUniqueTogether( + name='repository', + unique_together=set([('issuer', 'handle')]), + ), + migrations.AlterUniqueTogether( + name='hostedca', + unique_together=set([('issuer', 'hosted')]), + ), + migrations.AddField( + model_name='ghostbusterrequest', + name='parent', + field=models.ForeignKey(related_name='ghostbuster_requests', to='irdb.Parent', null=True), + ), + migrations.AlterUniqueTogether( + name='eecertificaterequestnet', + unique_together=set([('ee_certificate_request', 'start_ip', 'end_ip', 'version')]), + ), + migrations.AlterUniqueTogether( + name='eecertificaterequestasn', + unique_together=set([('ee_certificate_request', 'start_as', 'end_as')]), + ), + migrations.AlterUniqueTogether( + name='eecertificaterequest', + unique_together=set([('issuer', 'gski')]), + ), + migrations.AlterUniqueTogether( + name='client', + unique_together=set([('issuer', 'handle')]), + ), + migrations.AlterUniqueTogether( + name='childnet', + unique_together=set([('child', 'start_ip', 'end_ip', 'version')]), + ), + migrations.AlterUniqueTogether( + name='childasn', + unique_together=set([('child', 'start_as', 'end_as')]), + ), + migrations.AlterUniqueTogether( + name='child', + unique_together=set([('issuer', 'handle')]), + ), + migrations.AlterUniqueTogether( + name='bsc', + unique_together=set([('issuer', 'handle')]), + ), + migrations.AlterUniqueTogether( + name='parent', + unique_together=set([('issuer', 'handle')]), + ), + ] diff --git a/rpki/irdb/migrations/0002_remove_client_parent_handle.py b/rpki/irdb/migrations/0002_remove_client_parent_handle.py new file mode 100644 index 00000000..f86d165d --- /dev/null +++ b/rpki/irdb/migrations/0002_remove_client_parent_handle.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('irdb', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='client', + name='parent_handle', + ), + ] diff --git a/rpki/irdb/migrations/0003_repository_rrdp_notification_uri.py b/rpki/irdb/migrations/0003_repository_rrdp_notification_uri.py new file mode 100644 index 00000000..1e0e43c2 --- /dev/null +++ b/rpki/irdb/migrations/0003_repository_rrdp_notification_uri.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('irdb', '0002_remove_client_parent_handle'), + ] + + operations = [ + migrations.AddField( + model_name='repository', + name='rrdp_notification_uri', + field=models.TextField(null=True), + ), + ] diff --git a/rpki/irdb/migrations/__init__.py b/rpki/irdb/migrations/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/rpki/irdb/migrations/__init__.py diff --git a/rpki/irdb/models.py b/rpki/irdb/models.py index 6fa48c59..0911d7aa 100644 --- a/rpki/irdb/models.py +++ b/rpki/irdb/models.py @@ -32,7 +32,8 @@ import rpki.sundial import rpki.resource_set import socket import rpki.POW -from south.modelsinspector import add_introspection_rules + +from rpki.fields import EnumField, SundialField, CertificateField, DERField, KeyField, CRLField, PKCS10Field ## @var ip_version_choices # Choice argument for fields implementing IP version numbers. @@ -61,11 +62,11 @@ ee_certificate_lifetime = rpki.sundial.timedelta(days = 60) ### -# Field types +# Field classes class HandleField(django.db.models.CharField): """ - A handle field type. + A handle field class. Replace this with SlugField? """ description = 'A "handle" in one of the RPKI protocols' @@ -74,99 +75,6 @@ class HandleField(django.db.models.CharField): kwargs["max_length"] = 120 django.db.models.CharField.__init__(self, *args, **kwargs) -class EnumField(django.db.models.PositiveSmallIntegerField): - """ - An enumeration type that uses strings in Python and small integers - in SQL. - """ - - description = "An enumeration type" - - __metaclass__ = django.db.models.SubfieldBase - - def __init__(self, *args, **kwargs): - if isinstance(kwargs.get("choices"), (tuple, list)) and isinstance(kwargs["choices"][0], str): - kwargs["choices"] = tuple(enumerate(kwargs["choices"], 1)) - django.db.models.PositiveSmallIntegerField.__init__(self, *args, **kwargs) - self.enum_i2s = dict(self.flatchoices) - self.enum_s2i = dict((v, k) for k, v in self.flatchoices) - - def to_python(self, value): - return self.enum_i2s.get(value, value) - - def get_prep_value(self, value): - return self.enum_s2i.get(value, value) - -class SundialField(django.db.models.DateTimeField): - """ - A field type for our customized datetime objects. - """ - __metaclass__ = django.db.models.SubfieldBase - - description = "A datetime type using our customized datetime objects" - - def to_python(self, value): - if isinstance(value, rpki.sundial.pydatetime.datetime): - return rpki.sundial.datetime.from_datetime( - django.db.models.DateTimeField.to_python(self, value)) - else: - return value - - def get_prep_value(self, value): - if isinstance(value, rpki.sundial.datetime): - return value.to_datetime() - else: - return value - - -class DERField(django.db.models.Field): - """ - Field types for DER objects. - """ - - __metaclass__ = django.db.models.SubfieldBase - - def __init__(self, *args, **kwargs): - kwargs["serialize"] = False - kwargs["blank"] = True - kwargs["default"] = None - django.db.models.Field.__init__(self, *args, **kwargs) - - def db_type(self, connection): - if connection.settings_dict['ENGINE'] == "django.db.backends.posgresql": - return "bytea" - else: - return "BLOB" - - def to_python(self, value): - assert value is None or isinstance(value, (self.rpki_type, str)) - if isinstance(value, str): - return self.rpki_type(DER = value) - else: - return value - - def get_prep_value(self, value): - assert value is None or isinstance(value, (self.rpki_type, str)) - if isinstance(value, self.rpki_type): - return value.get_DER() - else: - return value - -class CertificateField(DERField): - description = "X.509 certificate" - rpki_type = rpki.x509.X509 - -class RSAKeyField(DERField): - description = "RSA keypair" - rpki_type = rpki.x509.RSA - -class CRLField(DERField): - description = "Certificate Revocation List" - rpki_type = rpki.x509.CRL - -class PKCS10Field(DERField): - description = "PKCS #10 certificate request" - rpki_type = rpki.x509.PKCS10 class SignedReferralField(DERField): description = "CMS signed object containing XML" @@ -231,7 +139,7 @@ class ResourceHolderEEManager(CertificateManager): class CA(django.db.models.Model): certificate = CertificateField() - private_key = RSAKeyField() + private_key = KeyField() latest_crl = CRLField() # Might want to bring these into line with what rpkid does. Current @@ -391,7 +299,7 @@ class ResourceHolderRevocation(Revocation): issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "revocations") class EECertificate(Certificate): - private_key = RSAKeyField() + private_key = KeyField() class Meta: abstract = True @@ -497,12 +405,12 @@ class Child(CrossCertification, ResourceSet): name = django.db.models.TextField(null = True, blank = True) def _select_resource_bag(self): - child_asn = rpki.irdb.ChildASN.objects.raw(""" + child_asn = rpki.irdb.models.ChildASN.objects.raw(""" SELECT * FROM irdb_childasn WHERE child_id = %s """, [self.id]) - child_net = list(rpki.irdb.ChildNet.objects.raw(""" + child_net = list(rpki.irdb.models.ChildNet.objects.raw(""" SELECT * FROM irdb_childnet WHERE child_id = %s @@ -542,7 +450,7 @@ class ROARequest(django.db.models.Model): @property def roa_prefix_bag(self): - prefixes = list(rpki.irdb.ROARequestPrefix.objects.raw(""" + prefixes = list(rpki.irdb.models.ROARequestPrefix.objects.raw(""" SELECT * FROM irdb_roarequestprefix WHERE roa_request_id = %s @@ -588,12 +496,12 @@ class EECertificateRequest(ResourceSet): eku = django.db.models.TextField(null = True) def _select_resource_bag(self): - ee_asn = rpki.irdb.EECertificateRequestASN.objects.raw(""" + ee_asn = rpki.irdb.models.EECertificateRequestASN.objects.raw(""" SELECT * FROM irdb_eecertificaterequestasn WHERE ee_certificate_request_id = %s """, [self.id]) - ee_net = rpki.irdb.EECertificateRequestNet.objects.raw(""" + ee_net = rpki.irdb.models.EECertificateRequestNet.objects.raw(""" SELECT * FROM irdb_eecertificaterequestnet WHERE ee_certificate_request_id = %s @@ -620,6 +528,7 @@ class Repository(CrossCertification): client_handle = HandleField() service_uri = django.db.models.CharField(max_length = 255) sia_base = django.db.models.TextField() + rrdp_notification_uri = django.db.models.TextField(null = True) turtle = django.db.models.OneToOneField(Turtle, related_name = "repository") # This shouldn't be necessary @@ -629,18 +538,7 @@ class Repository(CrossCertification): class Client(CrossCertification): issuer = django.db.models.ForeignKey(ServerCA, related_name = "clients") sia_base = django.db.models.TextField() - parent_handle = HandleField() # This shouldn't be necessary class Meta: unique_together = ("issuer", "handle") - -# for Django South -- these are just simple subclasses -add_introspection_rules([], - (r'^rpki\.irdb\.models\.CertificateField', - r'^rpki\.irdb\.models\.CRLField', - r'^rpki\.irdb\.models\.EnumField', - r'^rpki\.irdb\.models\.HandleField', - r'^rpki\.irdb\.models\.RSAKeyField', - r'^rpki\.irdb\.models\.SignedReferralField', - r'^rpki\.irdb\.models\.SundialField')) diff --git a/rpki/irdb/router.py b/rpki/irdb/router.py index 97e3d0b7..0aaf53ce 100644 --- a/rpki/irdb/router.py +++ b/rpki/irdb/router.py @@ -58,7 +58,7 @@ class DBContextRouter(object): else: return None - def allow_syncdb(self, db, model): + def allow_migrate(self, db, model): if db == self._database and model._meta.app_label == self._app: return True else: diff --git a/rpki/irdb/zookeeper.py b/rpki/irdb/zookeeper.py index 3fba99f9..4b4a2f46 100644 --- a/rpki/irdb/zookeeper.py +++ b/rpki/irdb/zookeeper.py @@ -24,17 +24,18 @@ Management code for the IRDB. import os import copy -import types + import rpki.config import rpki.sundial import rpki.oids -import rpki.http +import rpki.http_simple import rpki.resource_set import rpki.relaxng import rpki.left_right import rpki.x509 -import rpki.async import rpki.irdb +import rpki.publication_control + import django.db.transaction from lxml.etree import (Element, SubElement, ElementTree, @@ -42,12 +43,11 @@ from lxml.etree import (Element, SubElement, ElementTree, from rpki.csv_utils import csv_reader -# XML namespace and protocol version for OOB setup protocol. The name -# is historical and may change before we propose this as the basis for -# a standard. +# XML namespace and protocol version for OOB setup protocol. -myrpki_xmlns = rpki.relaxng.myrpki.xmlns -myrpki_version = rpki.relaxng.myrpki.version +oob_xmlns = rpki.relaxng.oob_setup.xmlns +oob_nsmap = rpki.relaxng.oob_setup.nsmap +oob_version = rpki.relaxng.oob_setup.version # XML namespace and protocol version for router certificate requests. # We probably ought to be pulling this sort of thing from the schema, @@ -56,8 +56,28 @@ myrpki_version = rpki.relaxng.myrpki.version # I'm ready to rewrite the rpki.relaxng code. routercert_xmlns = rpki.relaxng.router_certificate.xmlns +routercert_nsmap = rpki.relaxng.router_certificate.nsmap routercert_version = rpki.relaxng.router_certificate.version +# XML tags for elements in the above + +tag_oob_authorization = oob_xmlns + "authorization" +tag_oob_child_bpki_ta = oob_xmlns + "child_bpki_ta" +tag_oob_child_request = oob_xmlns + "child_request" +tag_oob_error = oob_xmlns + "error" +tag_oob_offer = oob_xmlns + "offer" +tag_oob_parent_bpki_ta = oob_xmlns + "parent_bpki_ta" +tag_oob_parent_response = oob_xmlns + "parent_response" +tag_oob_publisher_bpki_ta = oob_xmlns + "publisher_bpki_ta" +tag_oob_publisher_request = oob_xmlns + "publisher_request" +tag_oob_referral = oob_xmlns + "referral" +tag_oob_repository_bpki_ta = oob_xmlns + "repository_bpki_ta" +tag_oob_repository_response = oob_xmlns + "repository_response" + +tag_router_certificate_request = routercert_xmlns + "router_certificate_request" + +# Configuration file section names + myrpki_section = "myrpki" irdbd_section = "irdbd" rpkid_section = "rpkid" @@ -72,6 +92,7 @@ class CouldntTalkToDaemon(Exception): "Couldn't talk to daemon." class BadXMLMessage(Exception): "Bad XML message." class PastExpiration(Exception): "Expiration date has already passed." class CantRunRootd(Exception): "Can't run rootd." +class CouldntFindRepoParent(Exception): "Couldn't find repository's parent." def B64Element(e, tag, obj, **kwargs): @@ -128,19 +149,19 @@ class PEM_writer(object): self.wrote.add(filename) -def etree_read(filename): +def etree_read(filename_or_etree_wrapper, schema = rpki.relaxng.oob_setup): """ Read an etree from a file, verifying then stripping XML namespace - cruft. + cruft. As a convenience, we also accept an etree_wrapper object in + place of a filename, in which case we deepcopy the etree directly + from the etree_wrapper and there's no need for a file. """ - e = ElementTree(file = filename).getroot() - rpki.relaxng.myrpki.assertValid(e) - for i in e.getiterator(): - if i.tag.startswith(myrpki_xmlns): - i.tag = i.tag[len(myrpki_xmlns):] - else: - raise BadXMLMessage("XML tag %r is not in namespace %r" % (i.tag, myrpki_xmlns[1:-1])) + if isinstance(filename_or_etree_wrapper, etree_wrapper): + e = copy.deepcopy(filename_or_etree_wrapper.etree) + else: + e = ElementTree(file = filename_or_etree_wrapper).getroot() + schema.assertValid(e) return e @@ -148,20 +169,14 @@ class etree_wrapper(object): """ Wrapper for ETree objects so we can return them as function results without requiring the caller to understand much about them. - """ - def __init__(self, e, msg = None, debug = False): + def __init__(self, e, msg = None, debug = False, schema = rpki.relaxng.oob_setup): self.msg = msg e = copy.deepcopy(e) - e.set("version", myrpki_version) - for i in e.getiterator(): - if i.tag[0] != "{": - i.tag = myrpki_xmlns + i.tag - assert i.tag.startswith(myrpki_xmlns) if debug: print ElementToString(e) - rpki.relaxng.myrpki.assertValid(e) + schema.assertValid(e) self.etree = e def __str__(self): @@ -189,9 +204,9 @@ class etree_wrapper(object): class Zookeeper(object): ## @var show_xml - # Whether to show XML for debugging + # If not None, a file-like object to which to prettyprint XML, for debugging. - show_xml = False + show_xml = None def __init__(self, cfg = None, handle = None, logstream = None, disable_signal_handlers = False): @@ -259,7 +274,7 @@ class Zookeeper(object): if self.handle is None: raise HandleNotSet - return rpki.irdb.ResourceHolderCA.objects.get(handle = self.handle) + return rpki.irdb.models.ResourceHolderCA.objects.get(handle = self.handle) @property @@ -268,10 +283,10 @@ class Zookeeper(object): Get ServerCA object. """ - return rpki.irdb.ServerCA.objects.get() + return rpki.irdb.models.ServerCA.objects.get() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def initialize_server_bpki(self): """ Initialize server BPKI portion of an RPKI installation. Reads the @@ -280,18 +295,18 @@ class Zookeeper(object): """ if self.run_rpkid or self.run_pubd: - server_ca, created = rpki.irdb.ServerCA.objects.get_or_certify() - rpki.irdb.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "irbe") + server_ca, created = rpki.irdb.models.ServerCA.objects.get_or_certify() + rpki.irdb.models.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "irbe") if self.run_rpkid: - rpki.irdb.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "rpkid") - rpki.irdb.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "irdbd") + rpki.irdb.models.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "rpkid") + rpki.irdb.models.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "irdbd") if self.run_pubd: - rpki.irdb.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "pubd") + rpki.irdb.models.ServerEE.objects.get_or_certify(issuer = server_ca, purpose = "pubd") - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def initialize_resource_bpki(self): """ Initialize the resource-holding BPKI for an RPKI installation. @@ -305,7 +320,7 @@ class Zookeeper(object): resource-holding BPKI idenity if needed. """ - resource_ca, created = rpki.irdb.ResourceHolderCA.objects.get_or_certify(handle = self.handle) + resource_ca, created = rpki.irdb.models.ResourceHolderCA.objects.get_or_certify(handle = self.handle) return self.generate_identity() @@ -325,12 +340,13 @@ class Zookeeper(object): easier for the GUI this way. """ - e = Element("identity", handle = self.handle) - B64Element(e, "bpki_ta", self.resource_ca.certificate) + e = Element(tag_oob_child_request, nsmap = oob_nsmap, version = oob_version, + child_handle = self.handle) + B64Element(e, tag_oob_child_bpki_ta, self.resource_ca.certificate) return etree_wrapper(e, msg = 'This is the "identity" file you will need to send to your parent') - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def delete_self(self): """ Delete the ResourceHolderCA object corresponding to the current handle. @@ -349,14 +365,15 @@ class Zookeeper(object): self.log("No such ResourceHolderCA \"%s\"" % self.handle) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def configure_rootd(self): assert self.run_rpkid and self.run_pubd and self.run_rootd - rpki.irdb.Rootd.objects.get_or_certify( + rpki.irdb.models.Rootd.objects.get_or_certify( issuer = self.resource_ca, - service_uri = "http://localhost:%s/" % self.cfg.get("rootd_server_port", section = myrpki_section)) + service_uri = "http://localhost:%s/" % self.cfg.get("rootd_server_port", + section = myrpki_section)) return self.generate_rootd_repository_offer() @@ -367,17 +384,14 @@ class Zookeeper(object): configure_rootd() because that's easier for the GUI. """ - # The following assumes we'll set up the respository manually. - # Not sure this is a reasonable assumption, particularly if we - # ever fix rootd to use the publication protocol. - try: self.resource_ca.repositories.get(handle = self.handle) return None - except rpki.irdb.Repository.DoesNotExist: - e = Element("repository", type = "offer", handle = self.handle, parent_handle = self.handle) - B64Element(e, "bpki_client_ta", self.resource_ca.certificate) + except rpki.irdb.models.Repository.DoesNotExist: + e = Element(tag_oob_publisher_request, nsmap = oob_nsmap, version = oob_version, + publisher_handle = self.handle) + B64Element(e, tag_oob_publisher_bpki_ta, self.resource_ca.certificate) return etree_wrapper(e, msg = 'This is the "repository offer" file for you to use if you want to publish in your own repository') @@ -409,19 +423,19 @@ class Zookeeper(object): if self.run_rootd: try: - rootd = rpki.irdb.ResourceHolderCA.objects.get(handle = self.handle).rootd + rootd = rpki.irdb.models.ResourceHolderCA.objects.get(handle = self.handle).rootd writer(self.cfg.get("bpki-ta", section = rootd_section), self.server_ca.certificate) writer(self.cfg.get("rootd-bpki-crl", section = rootd_section), self.server_ca.latest_crl) writer(self.cfg.get("rootd-bpki-key", section = rootd_section), rootd.private_key) writer(self.cfg.get("rootd-bpki-cert", section = rootd_section), rootd.certificate) writer(self.cfg.get("child-bpki-cert", section = rootd_section), rootd.issuer.certificate) - except rpki.irdb.ResourceHolderCA.DoesNotExist: + except rpki.irdb.models.ResourceHolderCA.DoesNotExist: self.log("rootd enabled but resource holding entity not yet configured, skipping rootd setup") - except rpki.irdb.Rootd.DoesNotExist: + except rpki.irdb.models.Rootd.DoesNotExist: self.log("rootd enabled but not yet configured, skipping rootd setup") - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def update_bpki(self): """ Update BPKI certificates. Assumes an existing RPKI installation. @@ -435,17 +449,17 @@ class Zookeeper(object): Most likely this should be run under cron. """ - for model in (rpki.irdb.ServerCA, - rpki.irdb.ResourceHolderCA, - rpki.irdb.ServerEE, - rpki.irdb.Referral, - rpki.irdb.Rootd, - rpki.irdb.HostedCA, - rpki.irdb.BSC, - rpki.irdb.Child, - rpki.irdb.Parent, - rpki.irdb.Client, - rpki.irdb.Repository): + for model in (rpki.irdb.models.ServerCA, + rpki.irdb.models.ResourceHolderCA, + rpki.irdb.models.ServerEE, + rpki.irdb.models.Referral, + rpki.irdb.models.Rootd, + rpki.irdb.models.HostedCA, + rpki.irdb.models.BSC, + rpki.irdb.models.Child, + rpki.irdb.models.Parent, + rpki.irdb.models.Client, + rpki.irdb.models.Repository): for obj in model.objects.all(): self.log("Regenerating BPKI certificate %s" % obj.certificate.getSubject()) obj.avow() @@ -455,13 +469,33 @@ class Zookeeper(object): self.server_ca.generate_crl() self.server_ca.save() - for ca in rpki.irdb.ResourceHolderCA.objects.all(): + for ca in rpki.irdb.models.ResourceHolderCA.objects.all(): self.log("Regenerating BPKI CRL for Resource Holder %s" % ca.handle) ca.generate_crl() ca.save() - @django.db.transaction.commit_on_success + @staticmethod + def _compose_left_right_query(): + """ + Compose top level element of a left-right query. + """ + + return Element(rpki.left_right.tag_msg, nsmap = rpki.left_right.nsmap, + type = "query", version = rpki.left_right.version) + + + @staticmethod + def _compose_publication_control_query(): + """ + Compose top level element of a publication-control query. + """ + + return Element(rpki.publication_control.tag_msg, nsmap = rpki.publication_control.nsmap, + type = "query", version = rpki.publication_control.version) + + + @django.db.transaction.atomic def synchronize_bpki(self): """ Synchronize BPKI updates. This is separate from .update_bpki() @@ -472,85 +506,71 @@ class Zookeeper(object): """ if self.run_rpkid: - updates = [] - - updates.extend( - rpki.left_right.self_elt.make_pdu( - action = "set", - tag = "%s__self" % ca.handle, - self_handle = ca.handle, - bpki_cert = ca.certificate) - for ca in rpki.irdb.ResourceHolderCA.objects.all()) - - updates.extend( - rpki.left_right.bsc_elt.make_pdu( - action = "set", - tag = "%s__bsc__%s" % (bsc.issuer.handle, bsc.handle), - self_handle = bsc.issuer.handle, - bsc_handle = bsc.handle, - signing_cert = bsc.certificate, - signing_cert_crl = bsc.issuer.latest_crl) - for bsc in rpki.irdb.BSC.objects.all()) - - updates.extend( - rpki.left_right.repository_elt.make_pdu( - action = "set", - tag = "%s__repository__%s" % (repository.issuer.handle, repository.handle), - self_handle = repository.issuer.handle, - repository_handle = repository.handle, - bpki_cert = repository.certificate) - for repository in rpki.irdb.Repository.objects.all()) - - updates.extend( - rpki.left_right.parent_elt.make_pdu( - action = "set", - tag = "%s__parent__%s" % (parent.issuer.handle, parent.handle), - self_handle = parent.issuer.handle, - parent_handle = parent.handle, - bpki_cms_cert = parent.certificate) - for parent in rpki.irdb.Parent.objects.all()) - - updates.extend( - rpki.left_right.parent_elt.make_pdu( - action = "set", - tag = "%s__rootd" % rootd.issuer.handle, - self_handle = rootd.issuer.handle, - parent_handle = rootd.issuer.handle, - bpki_cms_cert = rootd.certificate) - for rootd in rpki.irdb.Rootd.objects.all()) - - updates.extend( - rpki.left_right.child_elt.make_pdu( - action = "set", - tag = "%s__child__%s" % (child.issuer.handle, child.handle), - self_handle = child.issuer.handle, - child_handle = child.handle, - bpki_cert = child.certificate) - for child in rpki.irdb.Child.objects.all()) - - if updates: - self.check_error_report(self.call_rpkid(updates)) + q_msg = self._compose_left_right_query() + + for ca in rpki.irdb.models.ResourceHolderCA.objects.all(): + q_pdu = SubElement(q_msg, rpki.left_right.tag_self, + action = "set", + tag = "%s__self" % ca.handle, + self_handle = ca.handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = ca.certificate.get_Base64() + + for bsc in rpki.irdb.models.BSC.objects.all(): + q_pdu = SubElement(q_msg, rpki.left_right.tag_bsc, + action = "set", + tag = "%s__bsc__%s" % (bsc.issuer.handle, bsc.handle), + self_handle = bsc.issuer.handle, + bsc_handle = bsc.handle) + SubElement(q_pdu, rpki.left_right.tag_signing_cert).text = bsc.certificate.get_Base64() + SubElement(q_pdu, rpki.left_right.tag_signing_cert_crl).text = bsc.issuer.latest_crl.get_Base64() + + for repository in rpki.irdb.models.Repository.objects.all(): + q_pdu = SubElement(q_msg, rpki.left_right.tag_repository, + action = "set", + tag = "%s__repository__%s" % (repository.issuer.handle, repository.handle), + self_handle = repository.issuer.handle, + repository_handle = repository.handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = repository.certificate.get_Base64() + + for parent in rpki.irdb.models.Parent.objects.all(): + q_pdu = SubElement(q_msg, rpki.left_right.tag_parent, + action = "set", + tag = "%s__parent__%s" % (parent.issuer.handle, parent.handle), + self_handle = parent.issuer.handle, + parent_handle = parent.handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = parent.certificate.get_Base64() + + for rootd in rpki.irdb.models.Rootd.objects.all(): + q_pdu = SubElement(q_msg, rpki.left_right.tag_parent, + action = "set", + tag = "%s__rootd" % rootd.issuer.handle, + self_handle = rootd.issuer.handle, + parent_handle = rootd.issuer.handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = rootd.certificate.get_Base64() + + for child in rpki.irdb.models.Child.objects.all(): + q_pdu = SubElement(q_msg, rpki.left_right.tag_child, + action = "set", + tag = "%s__child__%s" % (child.issuer.handle, child.handle), + self_handle = child.issuer.handle, + child_handle = child.handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = child.certificate.get_Base64() + + if len(q_msg) > 0: + self.call_rpkid(q_msg) if self.run_pubd: - updates = [] - - updates.append( - rpki.publication.config_elt.make_pdu( - action = "set", - bpki_crl = self.server_ca.latest_crl)) + q_msg = self._compose_publication_control_query() - updates.extend( - rpki.publication.client_elt.make_pdu( - action = "set", - client_handle = client.handle, - bpki_cert = client.certificate) - for client in self.server_ca.clients.all()) + for client in self.server_ca.clients.all(): + q_pdu = SubElement(q_msg, rpki.publication_control.tag_client, action = "set", client_handle = client.handle) + SubElement(q_pdu, rpki.publication_control.tag_bpki_cert).text = client.certificate.get_Base64() - if updates: - self.check_error_report(self.call_pubd(updates)) + if len(q_msg) > 0: + self.call_pubd(q_msg) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def configure_child(self, filename, child_handle = None, valid_until = None): """ Configure a new child of this RPKI entity, given the child's XML @@ -561,10 +581,10 @@ class Zookeeper(object): data and up-down protocol service URI. """ - c = etree_read(filename) + x = etree_read(filename) if child_handle is None: - child_handle = c.get("handle") + child_handle = x.get("child_handle") if valid_until is None: valid_until = rpki.sundial.now() + rpki.sundial.timedelta(days = 365) @@ -573,18 +593,18 @@ class Zookeeper(object): if valid_until < rpki.sundial.now(): raise PastExpiration("Specified new expiration time %s has passed" % valid_until) - self.log("Child calls itself %r, we call it %r" % (c.get("handle"), child_handle)) + self.log("Child calls itself %r, we call it %r" % (x.get("child_handle"), child_handle)) - child, created = rpki.irdb.Child.objects.get_or_certify( + child, created = rpki.irdb.models.Child.objects.get_or_certify( issuer = self.resource_ca, handle = child_handle, - ta = rpki.x509.X509(Base64 = c.findtext("bpki_ta")), + ta = rpki.x509.X509(Base64 = x.findtext(tag_oob_child_bpki_ta)), valid_until = valid_until) return self.generate_parental_response(child), child_handle - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def generate_parental_response(self, child): """ Generate parental response XML. Broken out of .configure_child() @@ -596,43 +616,41 @@ class Zookeeper(object): self.cfg.get("rpkid_server_port", section = myrpki_section), self.handle, child.handle) - e = Element("parent", parent_handle = self.handle, child_handle = child.handle, - service_uri = service_uri, valid_until = str(child.valid_until)) - B64Element(e, "bpki_resource_ta", self.resource_ca.certificate) - B64Element(e, "bpki_child_ta", child.ta) + e = Element(tag_oob_parent_response, nsmap = oob_nsmap, version = oob_version, + service_uri = service_uri, + child_handle = child.handle, + parent_handle = self.handle) + B64Element(e, tag_oob_parent_bpki_ta, self.resource_ca.certificate) try: if self.default_repository: repo = self.resource_ca.repositories.get(handle = self.default_repository) else: repo = self.resource_ca.repositories.get() - except rpki.irdb.Repository.DoesNotExist: + except rpki.irdb.models.Repository.DoesNotExist: repo = None if repo is None: self.log("Couldn't find any usable repositories, not giving referral") elif repo.handle == self.handle: - SubElement(e, "repository", type = "offer") + SubElement(e, tag_oob_offer) else: proposed_sia_base = repo.sia_base + child.handle + "/" - referral_cert, created = rpki.irdb.Referral.objects.get_or_certify(issuer = self.resource_ca) + referral_cert, created = rpki.irdb.models.Referral.objects.get_or_certify(issuer = self.resource_ca) auth = rpki.x509.SignedReferral() - auth.set_content(B64Element(None, myrpki_xmlns + "referral", child.ta, - version = myrpki_version, + auth.set_content(B64Element(None, tag_oob_authorization, child.ta, + nsmap = oob_nsmap, version = oob_version, authorized_sia_base = proposed_sia_base)) auth.schema_check() auth.sign(referral_cert.private_key, referral_cert.certificate, self.resource_ca.latest_crl) - - r = SubElement(e, "repository", type = "referral") - B64Element(r, "authorization", auth, referrer = repo.client_handle) - SubElement(r, "contact_info") + B64Element(e, tag_oob_referral, auth, referrer = repo.client_handle) return etree_wrapper(e, msg = "Send this file back to the child you just configured") - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def delete_child(self, child_handle): """ Delete a child of this RPKI entity. @@ -641,7 +659,7 @@ class Zookeeper(object): self.resource_ca.children.get(handle = child_handle).delete() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def configure_parent(self, filename, parent_handle = None): """ Configure a new parent of this RPKI entity, given the output of @@ -654,35 +672,39 @@ class Zookeeper(object): the user wants to avail herself of the referral or offer. """ - p = etree_read(filename) + x = etree_read(filename) if parent_handle is None: - parent_handle = p.get("parent_handle") + parent_handle = x.get("parent_handle") - r = p.find("repository") + offer = x.find(tag_oob_offer) + referral = x.find(tag_oob_referral) - repository_type = "none" - referrer = None - referral_authorization = None + if offer is not None: + repository_type = "offer" + referrer = None + referral_authorization = None - if r is not None: - repository_type = r.get("type") + elif referral is not None: + repository_type = "referral" + referrer = referral.get("referrer") + referral_authorization = rpki.x509.SignedReferral(Base64 = referral.text) - if repository_type == "referral": - a = r.find("authorization") - referrer = a.get("referrer") - referral_authorization = rpki.x509.SignedReferral(Base64 = a.text) + else: + repository_type = "none" + referrer = None + referral_authorization = None - self.log("Parent calls itself %r, we call it %r" % (p.get("parent_handle"), parent_handle)) - self.log("Parent calls us %r" % p.get("child_handle")) + self.log("Parent calls itself %r, we call it %r" % (x.get("parent_handle"), parent_handle)) + self.log("Parent calls us %r" % x.get("child_handle")) - parent, created = rpki.irdb.Parent.objects.get_or_certify( + parent, created = rpki.irdb.models.Parent.objects.get_or_certify( issuer = self.resource_ca, handle = parent_handle, - child_handle = p.get("child_handle"), - parent_handle = p.get("parent_handle"), - service_uri = p.get("service_uri"), - ta = rpki.x509.X509(Base64 = p.findtext("bpki_resource_ta")), + child_handle = x.get("child_handle"), + parent_handle = x.get("parent_handle"), + service_uri = x.get("service_uri"), + ta = rpki.x509.X509(Base64 = x.findtext(tag_oob_parent_bpki_ta)), repository_type = repository_type, referrer = referrer, referral_authorization = referral_authorization) @@ -695,16 +717,17 @@ class Zookeeper(object): Generate repository request for a given parent. """ - e = Element("repository", handle = self.handle, - parent_handle = parent.handle, type = parent.repository_type) + e = Element(tag_oob_publisher_request, nsmap = oob_nsmap, version = oob_version, + publisher_handle = self.handle) + B64Element(e, tag_oob_publisher_bpki_ta, self.resource_ca.certificate) if parent.repository_type == "referral": - B64Element(e, "authorization", parent.referral_authorization, referrer = parent.referrer) - SubElement(e, "contact_info") - B64Element(e, "bpki_client_ta", self.resource_ca.certificate) + B64Element(e, tag_oob_referral, parent.referral_authorization, + referrer = parent.referrer) + return etree_wrapper(e, msg = "This is the file to send to the repository operator") - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def delete_parent(self, parent_handle): """ Delete a parent of this RPKI entity. @@ -713,7 +736,7 @@ class Zookeeper(object): self.resource_ca.parents.get(handle = parent_handle).delete() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def delete_rootd(self): """ Delete rootd associated with this RPKI entity. @@ -722,7 +745,7 @@ class Zookeeper(object): self.resource_ca.rootd.delete() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def configure_publication_client(self, filename, sia_base = None, flat = False): """ Configure publication server to know about a new client, given the @@ -732,65 +755,67 @@ class Zookeeper(object): and service URI. """ - client = etree_read(filename) + x = etree_read(filename) + + client_ta = rpki.x509.X509(Base64 = x.findtext(tag_oob_publisher_bpki_ta)) - client_ta = rpki.x509.X509(Base64 = client.findtext("bpki_client_ta")) + referral = x.find(tag_oob_referral) + + default_sia_base = "rsync://{self.rsync_server}/{self.rsync_module}/{handle}/".format( + self = self, handle = x.get("publisher_handle")) if sia_base is None and flat: self.log("Flat publication structure forced, homing client at top-level") - sia_base = "rsync://%s/%s/%s/" % (self.rsync_server, self.rsync_module, client.get("handle")) + sia_base = default_sia_base - if sia_base is None and client.get("type") == "referral": + if sia_base is None and referral is not None: self.log("This looks like a referral, checking") try: - auth = client.find("authorization") - referrer = self.server_ca.clients.get(handle = auth.get("referrer")) - referral_cms = rpki.x509.SignedReferral(Base64 = auth.text) - referral_xml = referral_cms.unwrap(ta = (referrer.certificate, self.server_ca.certificate)) - if rpki.x509.X509(Base64 = referral_xml.text) != client_ta: + referrer = referral.get("referrer") + referrer = self.server_ca.clients.get(handle = referrer) + referral = rpki.x509.SignedReferral(Base64 = referral.text) + referral = referral.unwrap(ta = (referrer.certificate, self.server_ca.certificate)) + if rpki.x509.X509(Base64 = referral.text) != client_ta: raise BadXMLMessage("Referral trust anchor does not match") - sia_base = referral_xml.get("authorized_sia_base") - except rpki.irdb.Client.DoesNotExist: - self.log("We have no record of the client (%s) alleged to have made this referral" % auth.get("referrer")) + sia_base = referral.get("authorized_sia_base") + except rpki.irdb.models.Client.DoesNotExist: + self.log("We have no record of the client ({}) alleged to have made this referral".format(referrer)) - if sia_base is None and client.get("type") == "offer": - self.log("This looks like an offer, checking") + if sia_base is None and referral is None: + self.log("This might be an offer, checking") try: - parent = rpki.irdb.ResourceHolderCA.objects.get(children__ta__exact = client_ta) + parent = rpki.irdb.models.ResourceHolderCA.objects.get(children__ta__exact = client_ta) if "/" in parent.repositories.get(ta = self.server_ca.certificate).client_handle: self.log("Client's parent is not top-level, this is not a valid offer") else: self.log("Found client and its parent, nesting") - sia_base = "rsync://%s/%s/%s/%s/" % (self.rsync_server, self.rsync_module, - parent.handle, client.get("handle")) - except rpki.irdb.Repository.DoesNotExist: + sia_base = "rsync://{self.rsync_server}/{self.rsync_module}/{parent_handle}/{client_handle}/".format( + self = self, parent_handle = parent.handle, client_handle = x.get("publisher_handle")) + except rpki.irdb.models.Repository.DoesNotExist: self.log("Found client's parent, but repository isn't set, this shouldn't happen!") - except rpki.irdb.ResourceHolderCA.DoesNotExist: + except rpki.irdb.models.ResourceHolderCA.DoesNotExist: try: - rpki.irdb.Rootd.objects.get(issuer__certificate__exact = client_ta) - except rpki.irdb.Rootd.DoesNotExist: - self.log("We don't host this client's parent, so we didn't make this offer") - else: + rpki.irdb.models.Rootd.objects.get(issuer__certificate__exact = client_ta) self.log("This client's parent is rootd") + sia_base = default_sia_base + except rpki.irdb.models.Rootd.DoesNotExist: + self.log("We don't host this client's parent, so we didn't make an offer") if sia_base is None: - self.log("Don't know where to nest this client, defaulting to top-level") - sia_base = "rsync://%s/%s/%s/" % (self.rsync_server, self.rsync_module, client.get("handle")) + self.log("Don't know where else to nest this client, so defaulting to top-level") + sia_base = default_sia_base if not sia_base.startswith("rsync://"): raise BadXMLMessage("Malformed sia_base parameter %r, should start with 'rsync://'" % sia_base) client_handle = "/".join(sia_base.rstrip("/").split("/")[4:]) - parent_handle = client.get("parent_handle") + self.log("Client calls itself %r, we call it %r" % ( + x.get("publisher_handle"), client_handle)) - self.log("Client calls itself %r, we call it %r" % (client.get("handle"), client_handle)) - self.log("Client says its parent handle is %r" % parent_handle) - - client, created = rpki.irdb.Client.objects.get_or_certify( + client, created = rpki.irdb.models.Client.objects.get_or_certify( issuer = self.server_ca, handle = client_handle, - parent_handle = parent_handle, ta = client_ta, sia_base = sia_base) @@ -802,24 +827,27 @@ class Zookeeper(object): Generate repository response XML to a given client. """ - service_uri = "http://%s:%s/client/%s" % ( - self.cfg.get("pubd_server_host", section = myrpki_section), - self.cfg.get("pubd_server_port", section = myrpki_section), - client.handle) - - e = Element("repository", type = "confirmed", - client_handle = client.handle, - parent_handle = client.parent_handle, - sia_base = client.sia_base, - service_uri = service_uri) - - B64Element(e, "bpki_server_ta", self.server_ca.certificate) - B64Element(e, "bpki_client_ta", client.ta) - SubElement(e, "contact_info").text = self.pubd_contact_info + service_uri = "http://{host}:{port}/client/{handle}".format( + host = self.cfg.get("pubd_server_host", section = myrpki_section), + port = self.cfg.get("pubd_server_port", section = myrpki_section), + handle = client.handle) + + rrdp_uri = self.cfg.get("publication_rrdp_notification_uri", section = myrpki_section, + default = "") or None + + e = Element(tag_oob_repository_response, nsmap = oob_nsmap, version = oob_version, + service_uri = service_uri, + publisher_handle = client.handle, + sia_base = client.sia_base) + + if rrdp_uri is not None: + e.set("rrdp_notification_uri", rrdp_uri) + + B64Element(e, tag_oob_repository_bpki_ta, self.server_ca.certificate) return etree_wrapper(e, msg = "Send this file back to the publication client you just configured") - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def delete_publication_client(self, client_handle): """ Delete a publication client of this RPKI entity. @@ -828,7 +856,7 @@ class Zookeeper(object): self.server_ca.clients.get(handle = client_handle).delete() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def configure_repository(self, filename, parent_handle = None): """ Configure a publication repository for this RPKI entity, given the @@ -838,35 +866,56 @@ class Zookeeper(object): corresponding parent data in our local database. """ - r = etree_read(filename) + x = etree_read(filename) - if parent_handle is None: - parent_handle = r.get("parent_handle") + self.log("Repository calls us %r" % (x.get("client_handle"))) - self.log("Repository calls us %r" % (r.get("client_handle"))) - self.log("Repository response associated with parent_handle %r" % parent_handle) + if parent_handle is not None: + self.log("Explicit parent_handle given") + try: + if parent_handle == self.handle: + turtle = self.resource_ca.rootd + else: + turtle = self.resource_ca.parents.get(handle = parent_handle) + except (rpki.irdb.models.Parent.DoesNotExist, rpki.irdb.models.Rootd.DoesNotExist): + self.log("Could not find parent %r in our database" % parent_handle) + raise CouldntFindRepoParent - try: - if parent_handle == self.handle: - turtle = self.resource_ca.rootd + else: + turtles = [] + for parent in self.resource_ca.parents.all(): + try: + _ = parent.repository + except rpki.irdb.models.Repository.DoesNotExist: + turtles.append(parent) + try: + _ = self.resource_ca.rootd.repository + except rpki.irdb.models.Repository.DoesNotExist: + turtles.append(self.resource_ca.rootd) + except rpki.irdb.models.Rootd.DoesNotExist: + pass + if len(turtles) != 1: + self.log("No explicit parent_handle given and unable to guess") + raise CouldntFindRepoParent + turtle = turtles[0] + if isinstance(turtle, rpki.irdb.models.Rootd): + parent_handle = self.handle else: - turtle = self.resource_ca.parents.get(handle = parent_handle) + parent_handle = turtle.handle + self.log("No explicit parent_handle given, guessing parent {}".format(parent_handle)) - except (rpki.irdb.Parent.DoesNotExist, rpki.irdb.Rootd.DoesNotExist): - self.log("Could not find parent %r in our database" % parent_handle) - - else: - rpki.irdb.Repository.objects.get_or_certify( - issuer = self.resource_ca, - handle = parent_handle, - client_handle = r.get("client_handle"), - service_uri = r.get("service_uri"), - sia_base = r.get("sia_base"), - ta = rpki.x509.X509(Base64 = r.findtext("bpki_server_ta")), - turtle = turtle) + rpki.irdb.models.Repository.objects.get_or_certify( + issuer = self.resource_ca, + handle = parent_handle, + client_handle = x.get("publisher_handle"), + service_uri = x.get("service_uri"), + sia_base = x.get("sia_base"), + rrdp_notification_uri = x.get("rrdp_notification_uri"), + ta = rpki.x509.X509(Base64 = x.findtext(tag_oob_repository_bpki_ta)), + turtle = turtle) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def delete_repository(self, repository_handle): """ Delete a repository of this RPKI entity. @@ -875,7 +924,7 @@ class Zookeeper(object): self.resource_ca.repositories.get(handle = repository_handle).delete() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def renew_children(self, child_handle, valid_until = None): """ Update validity period for one child entity or, if child_handle is @@ -901,7 +950,7 @@ class Zookeeper(object): child.save() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def load_prefixes(self, filename, ignore_missing_children = False): """ Whack IRDB to match prefixes.csv. @@ -923,25 +972,25 @@ class Zookeeper(object): for handle, prefixes in grouped.iteritems(): try: child = self.resource_ca.children.get(handle = handle) - except rpki.irdb.Child.DoesNotExist: + except rpki.irdb.models.Child.DoesNotExist: if not ignore_missing_children: raise else: for prefix in rset(",".join(prefixes)): - obj, created = rpki.irdb.ChildNet.objects.get_or_create( + obj, created = rpki.irdb.models.ChildNet.objects.get_or_create( child = child, start_ip = str(prefix.min), end_ip = str(prefix.max), version = version) primary_keys.append(obj.pk) - q = rpki.irdb.ChildNet.objects + q = rpki.irdb.models.ChildNet.objects q = q.filter(child__issuer__exact = self.resource_ca) q = q.exclude(pk__in = primary_keys) q.delete() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def load_asns(self, filename, ignore_missing_children = False): """ Whack IRDB to match asns.csv. @@ -959,24 +1008,24 @@ class Zookeeper(object): for handle, asns in grouped.iteritems(): try: child = self.resource_ca.children.get(handle = handle) - except rpki.irdb.Child.DoesNotExist: + except rpki.irdb.models.Child.DoesNotExist: if not ignore_missing_children: raise else: for asn in rpki.resource_set.resource_set_as(",".join(asns)): - obj, created = rpki.irdb.ChildASN.objects.get_or_create( + obj, created = rpki.irdb.models.ChildASN.objects.get_or_create( child = child, start_as = str(asn.min), end_as = str(asn.max)) primary_keys.append(obj.pk) - q = rpki.irdb.ChildASN.objects + q = rpki.irdb.models.ChildASN.objects q = q.filter(child__issuer__exact = self.resource_ca) q = q.exclude(pk__in = primary_keys) q.delete() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def load_roa_requests(self, filename): """ Whack IRDB to match roa.csv. @@ -1017,7 +1066,7 @@ class Zookeeper(object): max_prefixlen = int(p.max_prefixlen)) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def load_ghostbuster_requests(self, filename, parent = None): """ Whack IRDB to match ghostbusters.vcard. @@ -1038,14 +1087,9 @@ class Zookeeper(object): vcard = [] - def call_rpkid(self, *pdus): + def call_rpkid(self, q_msg, suppress_error_check = False): """ Issue a call to rpkid, return result. - - Implementation is a little silly, constructs a wrapper object, - invokes it once, then throws it away. Hard to do better without - rewriting a bit of the HTTP code, as we want to be sure we're - using the current BPKI certificate and key objects. """ url = "http://%s:%s/left-right" % ( @@ -1055,23 +1099,28 @@ class Zookeeper(object): rpkid = self.server_ca.ee_certificates.get(purpose = "rpkid") irbe = self.server_ca.ee_certificates.get(purpose = "irbe") - if len(pdus) == 1 and isinstance(pdus[0], types.GeneratorType): - pdus = tuple(pdus[0]) - elif len(pdus) == 1 and isinstance(pdus[0], (tuple, list)): - pdus = pdus[0] + r_msg = rpki.http_simple.client( + proto_cms_msg = rpki.left_right.cms_msg, + client_key = irbe.private_key, + client_cert = irbe.certificate, + server_ta = self.server_ca.certificate, + server_cert = rpkid.certificate, + url = url, + q_msg = q_msg, + debug = self.show_xml) + + if not suppress_error_check: + self.check_error_report(r_msg) + return r_msg - call_rpkid = rpki.async.sync_wrapper( - disable_signal_handlers = self.disable_signal_handlers, - func = rpki.http.caller( - proto = rpki.left_right, - client_key = irbe.private_key, - client_cert = irbe.certificate, - server_ta = self.server_ca.certificate, - server_cert = rpkid.certificate, - url = url, - debug = self.show_xml)) - return call_rpkid(*pdus) + def _rpkid_self_control(self, *bools): + assert all(isinstance(b, str) for b in bools) + q_msg = self._compose_left_right_query() + q_pdu = SubElement(q_msg, rpki.left_right.tag_self, action = "set", self_handle = self.handle) + for b in bools: + q_pdu.set(b, "yes") + return self.call_rpkid(q_msg) def run_rpkid_now(self): @@ -1083,8 +1132,7 @@ class Zookeeper(object): to force the object to be immediately issued. """ - self.call_rpkid(rpki.left_right.self_elt.make_pdu( - action = "set", self_handle = self.handle, run_now = "yes")) + return self._rpkid_self_control("run_now") def publish_world_now(self): @@ -1092,8 +1140,7 @@ class Zookeeper(object): Poke rpkid to (re)publish everything for the current handle. """ - self.call_rpkid(rpki.left_right.self_elt.make_pdu( - action = "set", self_handle = self.handle, publish_world_now = "yes")) + return self._rpkid_self_control("publish_world_now") def reissue(self): @@ -1101,8 +1148,8 @@ class Zookeeper(object): Poke rpkid to reissue everything for the current handle. """ - self.call_rpkid(rpki.left_right.self_elt.make_pdu( - action = "set", self_handle = self.handle, reissue = "yes")) + return self._rpkid_self_control("reissue") + def rekey(self): """ @@ -1110,8 +1157,7 @@ class Zookeeper(object): handle. """ - self.call_rpkid(rpki.left_right.self_elt.make_pdu( - action = "set", self_handle = self.handle, rekey = "yes")) + return self._rpkid_self_control("rekey") def revoke(self): @@ -1119,8 +1165,7 @@ class Zookeeper(object): Poke rpkid to revoke old RPKI keys for the current handle. """ - self.call_rpkid(rpki.left_right.self_elt.make_pdu( - action = "set", self_handle = self.handle, revoke = "yes")) + return self._rpkid_self_control("revoke") def revoke_forgotten(self): @@ -1128,8 +1173,7 @@ class Zookeeper(object): Poke rpkid to revoke old forgotten RPKI keys for the current handle. """ - self.call_rpkid(rpki.left_right.self_elt.make_pdu( - action = "set", self_handle = self.handle, revoke_forgotten = "yes")) + return self._rpkid_self_control("revoke_forgotten") def clear_all_sql_cms_replay_protection(self): @@ -1137,27 +1181,27 @@ class Zookeeper(object): Tell rpkid and pubd to clear replay protection for all SQL-based entities. This is a fairly blunt instrument, but as we don't expect this to be necessary except in the case of gross - misconfiguration, it should suffice + misconfiguration, it should suffice. """ - self.call_rpkid(rpki.left_right.self_elt.make_pdu(action = "set", self_handle = ca.handle, - clear_replay_protection = "yes") - for ca in rpki.irdb.ResourceHolderCA.objects.all()) + if self.run_rpkid: + q_msg = self._compose_left_right_query() + for ca in rpki.irdb.models.ResourceHolderCA.objects.all(): + SubElement(q_msg, rpki.left_right.tag_self, action = "set", + self_handle = ca.handle, clear_replay_protection = "yes") + self.call_rpkid(q_msg) + if self.run_pubd: - self.call_pubd(rpki.publication.client_elt.make_pdu(action = "set", - client_handle = client.handle, - clear_replay_protection = "yes") - for client in self.server_ca.clients.all()) + q_msg = self._compose_publication_control_query() + for client in self.server_ca.clients.all(): + SubElement(q_msg, rpki.publication_control.tag_client, action = "set", + client_handle = client.handle, clear_reply_protection = "yes") + self.call_pubd(q_msg) - def call_pubd(self, *pdus): + def call_pubd(self, q_msg): """ Issue a call to pubd, return result. - - Implementation is a little silly, constructs a wrapper object, - invokes it once, then throws it away. Hard to do better without - rewriting a bit of the HTTP code, as we want to be sure we're - using the current BPKI certificate and key objects. """ url = "http://%s:%s/control" % ( @@ -1167,45 +1211,42 @@ class Zookeeper(object): pubd = self.server_ca.ee_certificates.get(purpose = "pubd") irbe = self.server_ca.ee_certificates.get(purpose = "irbe") - if len(pdus) == 1 and isinstance(pdus[0], types.GeneratorType): - pdus = tuple(pdus[0]) - elif len(pdus) == 1 and isinstance(pdus[0], (tuple, list)): - pdus = pdus[0] + r_msg = rpki.http_simple.client( + proto_cms_msg = rpki.publication_control.cms_msg, + client_key = irbe.private_key, + client_cert = irbe.certificate, + server_ta = self.server_ca.certificate, + server_cert = pubd.certificate, + url = url, + q_msg = q_msg, + debug = self.show_xml) - call_pubd = rpki.async.sync_wrapper( - disable_signal_handlers = self.disable_signal_handlers, - func = rpki.http.caller( - proto = rpki.publication, - client_key = irbe.private_key, - client_cert = irbe.certificate, - server_ta = self.server_ca.certificate, - server_cert = pubd.certificate, - url = url, - debug = self.show_xml)) + self.check_error_report(r_msg) + return r_msg - return call_pubd(*pdus) - - def check_error_report(self, pdus): + def check_error_report(self, r_msg): """ Check a response from rpkid or pubd for error_report PDUs, log and throw exceptions as needed. """ - if any(isinstance(pdu, (rpki.left_right.report_error_elt, rpki.publication.report_error_elt)) for pdu in pdus): - for pdu in pdus: - if isinstance(pdu, rpki.left_right.report_error_elt): - self.log("rpkid reported failure: %s" % pdu.error_code) - elif isinstance(pdu, rpki.publication.report_error_elt): - self.log("pubd reported failure: %s" % pdu.error_code) + if any(r_pdu.tag in (rpki.left_right.tag_report_error, + rpki.publication_control.tag_report_error) + for r_pdu in r_msg): + for r_pdu in r_msg: + if r_pdu.tag == rpki.left_right.tag_report_error: + self.log("rpkid reported failure: %s" % r_pdu.get("error_code")) + elif r_pdu.tag == rpki.publication_control.tag_report_error: + self.log("pubd reported failure: %s" % r_pdu.get("error_code")) else: continue - if pdu.error_text: - self.log(pdu.error_text) + if r_pdu.text: + self.log(r_pdu.text) raise CouldntTalkToDaemon - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def synchronize(self, *handles_to_poke): """ Configure RPKI daemons with the data built up by the other @@ -1217,13 +1258,13 @@ class Zookeeper(object): <self run_now="yes"/> operation. """ - for ca in rpki.irdb.ResourceHolderCA.objects.all(): + for ca in rpki.irdb.models.ResourceHolderCA.objects.all(): self.synchronize_rpkid_one_ca_core(ca, ca.handle in handles_to_poke) self.synchronize_pubd_core() self.synchronize_rpkid_deleted_core() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def synchronize_ca(self, ca = None, poke = False): """ Synchronize one CA. Most commands which modify a CA should call @@ -1235,7 +1276,7 @@ class Zookeeper(object): self.synchronize_rpkid_one_ca_core(ca, poke) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def synchronize_deleted_ca(self): """ Delete CAs which are present in rpkid's database but not in the @@ -1245,7 +1286,7 @@ class Zookeeper(object): self.synchronize_rpkid_deleted_core() - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def synchronize_pubd(self): """ Synchronize pubd. Most commands which modify pubd should call this. @@ -1293,94 +1334,103 @@ class Zookeeper(object): # See what rpkid already has on file for this entity. - rpkid_reply = self.call_rpkid( - rpki.left_right.self_elt.make_pdu( action = "get", tag = "self", self_handle = ca.handle), - rpki.left_right.bsc_elt.make_pdu( action = "list", tag = "bsc", self_handle = ca.handle), - rpki.left_right.repository_elt.make_pdu(action = "list", tag = "repository", self_handle = ca.handle), - rpki.left_right.parent_elt.make_pdu( action = "list", tag = "parent", self_handle = ca.handle), - rpki.left_right.child_elt.make_pdu( action = "list", tag = "child", self_handle = ca.handle)) - - self_pdu = rpkid_reply[0] - bsc_pdus = dict((x.bsc_handle, x) for x in rpkid_reply if isinstance(x, rpki.left_right.bsc_elt)) - repository_pdus = dict((x.repository_handle, x) for x in rpkid_reply if isinstance(x, rpki.left_right.repository_elt)) - parent_pdus = dict((x.parent_handle, x) for x in rpkid_reply if isinstance(x, rpki.left_right.parent_elt)) - child_pdus = dict((x.child_handle, x) for x in rpkid_reply if isinstance(x, rpki.left_right.child_elt)) + q_msg = self._compose_left_right_query() + SubElement(q_msg, rpki.left_right.tag_self, action = "get", self_handle = ca.handle) + SubElement(q_msg, rpki.left_right.tag_bsc, action = "list", self_handle = ca.handle) + SubElement(q_msg, rpki.left_right.tag_repository, action = "list", self_handle = ca.handle) + SubElement(q_msg, rpki.left_right.tag_parent, action = "list", self_handle = ca.handle) + SubElement(q_msg, rpki.left_right.tag_child, action = "list", self_handle = ca.handle) - rpkid_query = [] + r_msg = self.call_rpkid(q_msg, suppress_error_check = True) - self_cert, created = rpki.irdb.HostedCA.objects.get_or_certify( + if r_msg[0].tag == rpki.left_right.tag_self: + self.check_error_report(r_msg) + self_pdu = r_msg[0] + else: + self_pdu = None + + bsc_pdus = dict((r_pdu.get("bsc_handle"), r_pdu) + for r_pdu in r_msg + if r_pdu.tag == rpki.left_right.tag_bsc) + repository_pdus = dict((r_pdu.get("repository_handle"), r_pdu) + for r_pdu in r_msg + if r_pdu.tag == rpki.left_right.tag_repository) + parent_pdus = dict((r_pdu.get("parent_handle"), r_pdu) + for r_pdu in r_msg + if r_pdu.tag == rpki.left_right.tag_parent) + child_pdus = dict((r_pdu.get("child_handle"), r_pdu) + for r_pdu in r_msg + if r_pdu.tag == rpki.left_right.tag_child) + + q_msg = self._compose_left_right_query() + + self_cert, created = rpki.irdb.models.HostedCA.objects.get_or_certify( issuer = self.server_ca, hosted = ca) # There should be exactly one <self/> object per hosted entity, by definition - if (isinstance(self_pdu, rpki.left_right.report_error_elt) or - self_pdu.crl_interval != self_crl_interval or - self_pdu.regen_margin != self_regen_margin or - self_pdu.bpki_cert != self_cert.certificate): - rpkid_query.append(rpki.left_right.self_elt.make_pdu( - action = "create" if isinstance(self_pdu, rpki.left_right.report_error_elt) else "set", - tag = "self", - self_handle = ca.handle, - bpki_cert = ca.certificate, - crl_interval = self_crl_interval, - regen_margin = self_regen_margin)) + if (self_pdu is None or + self_pdu.get("crl_interval") != str(self_crl_interval) or + self_pdu.get("regen_margin") != str(self_regen_margin) or + self_pdu.findtext(rpki.left_right.tag_bpki_cert, "").decode("base64") != self_cert.certificate.get_DER()): + q_pdu = SubElement(q_msg, rpki.left_right.tag_self, + action = "create" if self_pdu is None else "set", + tag = "self", + self_handle = ca.handle, + crl_interval = str(self_crl_interval), + regen_margin = str(self_regen_margin)) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = ca.certificate.get_Base64() # In general we only need one <bsc/> per <self/>. BSC objects # are a little unusual in that the keypair and PKCS #10 - # subelement is generated by rpkid, so complete setup requires + # subelement are generated by rpkid, so complete setup requires # two round trips. bsc_pdu = bsc_pdus.pop(bsc_handle, None) - if bsc_pdu is None: - rpkid_query.append(rpki.left_right.bsc_elt.make_pdu( - action = "create", - tag = "bsc", - self_handle = ca.handle, - bsc_handle = bsc_handle, - generate_keypair = "yes")) - - elif bsc_pdu.pkcs10_request is None: - rpkid_query.append(rpki.left_right.bsc_elt.make_pdu( - action = "set", - tag = "bsc", - self_handle = ca.handle, - bsc_handle = bsc_handle, - generate_keypair = "yes")) - - rpkid_query.extend(rpki.left_right.bsc_elt.make_pdu( - action = "destroy", self_handle = ca.handle, bsc_handle = b) for b in bsc_pdus) + if bsc_pdu is None or bsc_pdu.find(rpki.left_right.tag_pkcs10_request) is None: + SubElement(q_msg, rpki.left_right.tag_bsc, + action = "create" if bsc_pdu is None else "set", + tag = "bsc", + self_handle = ca.handle, + bsc_handle = bsc_handle, + generate_keypair = "yes") + + for bsc_handle in bsc_pdus: + SubElement(q_msg, rpki.left_right.tag_bsc, + action = "destroy", self_handle = ca.handle, bsc_handle = bsc_handle) # If we've already got actions queued up, run them now, so we # can finish setting up the BSC before anything tries to use it. - if rpkid_query: - rpkid_query.append(rpki.left_right.bsc_elt.make_pdu(action = "list", tag = "bsc", self_handle = ca.handle)) - rpkid_reply = self.call_rpkid(rpkid_query) - bsc_pdus = dict((x.bsc_handle, x) - for x in rpkid_reply - if isinstance(x, rpki.left_right.bsc_elt) and x.action == "list") + if len(q_msg) > 0: + SubElement(q_msg, rpki.left_right.tag_bsc, action = "list", tag = "bsc", self_handle = ca.handle) + r_msg = self.call_rpkid(q_msg) + bsc_pdus = dict((r_pdu.get("bsc_handle"), r_pdu) + for r_pdu in r_msg + if r_pdu.tag == rpki.left_right.tag_bsc and r_pdu.get("action") == "list") bsc_pdu = bsc_pdus.pop(bsc_handle, None) - self.check_error_report(rpkid_reply) - rpkid_query = [] + q_msg = self._compose_left_right_query() - assert bsc_pdu.pkcs10_request is not None + bsc_pkcs10 = bsc_pdu.find(rpki.left_right.tag_pkcs10_request) + assert bsc_pkcs10 is not None - bsc, created = rpki.irdb.BSC.objects.get_or_certify( + bsc, created = rpki.irdb.models.BSC.objects.get_or_certify( issuer = ca, handle = bsc_handle, - pkcs10 = bsc_pdu.pkcs10_request) - - if bsc_pdu.signing_cert != bsc.certificate or bsc_pdu.signing_cert_crl != ca.latest_crl: - rpkid_query.append(rpki.left_right.bsc_elt.make_pdu( - action = "set", - tag = "bsc", - self_handle = ca.handle, - bsc_handle = bsc_handle, - signing_cert = bsc.certificate, - signing_cert_crl = ca.latest_crl)) + pkcs10 = rpki.x509.PKCS10(Base64 = bsc_pkcs10.text)) + + if (bsc_pdu.findtext(rpki.left_right.tag_signing_cert, "").decode("base64") != bsc.certificate.get_DER() or + bsc_pdu.findtext(rpki.left_right.tag_signing_cert_crl, "").decode("base64") != ca.latest_crl.get_DER()): + q_pdu = SubElement(q_msg, rpki.left_right.tag_bsc, + action = "set", + tag = "bsc", + self_handle = ca.handle, + bsc_handle = bsc_handle) + SubElement(q_pdu, rpki.left_right.tag_signing_cert).text = bsc.certificate.get_Base64() + SubElement(q_pdu, rpki.left_right.tag_signing_cert_crl).text = ca.latest_crl.get_Base64() # At present we need one <repository/> per <parent/>, not because # rpkid requires that, but because pubd does. pubd probably should @@ -1393,20 +1443,24 @@ class Zookeeper(object): repository_pdu = repository_pdus.pop(repository.handle, None) if (repository_pdu is None or - repository_pdu.bsc_handle != bsc_handle or - repository_pdu.peer_contact_uri != repository.service_uri or - repository_pdu.bpki_cert != repository.certificate): - rpkid_query.append(rpki.left_right.repository_elt.make_pdu( - action = "create" if repository_pdu is None else "set", - tag = repository.handle, - self_handle = ca.handle, - repository_handle = repository.handle, - bsc_handle = bsc_handle, - peer_contact_uri = repository.service_uri, - bpki_cert = repository.certificate)) - - rpkid_query.extend(rpki.left_right.repository_elt.make_pdu( - action = "destroy", self_handle = ca.handle, repository_handle = r) for r in repository_pdus) + repository_pdu.get("bsc_handle") != bsc_handle or + repository_pdu.get("peer_contact_uri") != repository.service_uri or + repository_pdu.get("rrdp_notification_uri") != repository.rrdp_notification_uri or + repository_pdu.findtext(rpki.left_right.tag_bpki_cert, "").decode("base64") != repository.certificate.get_DER()): + q_pdu = SubElement(q_msg, rpki.left_right.tag_repository, + action = "create" if repository_pdu is None else "set", + tag = repository.handle, + self_handle = ca.handle, + repository_handle = repository.handle, + bsc_handle = bsc_handle, + peer_contact_uri = repository.service_uri) + if repository.rrdp_notification_uri: + q_pdu.set("rrdp_notification_uri", repository.rrdp_notification_uri) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = repository.certificate.get_Base64() + + for repository_handle in repository_pdus: + SubElement(q_msg, rpki.left_right.tag_repository, action = "destroy", + self_handle = ca.handle, repository_handle = repository_handle) # <parent/> setup code currently assumes 1:1 mapping between # <repository/> and <parent/>, and further assumes that the handles @@ -1419,31 +1473,30 @@ class Zookeeper(object): for parent in ca.parents.all(): try: - parent_pdu = parent_pdus.pop(parent.handle, None) if (parent_pdu is None or - parent_pdu.bsc_handle != bsc_handle or - parent_pdu.repository_handle != parent.handle or - parent_pdu.peer_contact_uri != parent.service_uri or - parent_pdu.sia_base != parent.repository.sia_base or - parent_pdu.sender_name != parent.child_handle or - parent_pdu.recipient_name != parent.parent_handle or - parent_pdu.bpki_cms_cert != parent.certificate): - rpkid_query.append(rpki.left_right.parent_elt.make_pdu( - action = "create" if parent_pdu is None else "set", - tag = parent.handle, - self_handle = ca.handle, - parent_handle = parent.handle, - bsc_handle = bsc_handle, - repository_handle = parent.handle, - peer_contact_uri = parent.service_uri, - sia_base = parent.repository.sia_base, - sender_name = parent.child_handle, - recipient_name = parent.parent_handle, - bpki_cms_cert = parent.certificate)) - - except rpki.irdb.Repository.DoesNotExist: + parent_pdu.get("bsc_handle") != bsc_handle or + parent_pdu.get("repository_handle") != parent.handle or + parent_pdu.get("peer_contact_uri") != parent.service_uri or + parent_pdu.get("sia_base") != parent.repository.sia_base or + parent_pdu.get("sender_name") != parent.child_handle or + parent_pdu.get("recipient_name") != parent.parent_handle or + parent_pdu.findtext(rpki.left_right.tag_bpki_cert, "").decode("base64") != parent.certificate.get_DER()): + q_pdu = SubElement(q_msg, rpki.left_right.tag_parent, + action = "create" if parent_pdu is None else "set", + tag = parent.handle, + self_handle = ca.handle, + parent_handle = parent.handle, + bsc_handle = bsc_handle, + repository_handle = parent.handle, + peer_contact_uri = parent.service_uri, + sia_base = parent.repository.sia_base, + sender_name = parent.child_handle, + recipient_name = parent.parent_handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = parent.certificate.get_Base64() + + except rpki.irdb.models.Repository.DoesNotExist: pass try: @@ -1451,31 +1504,32 @@ class Zookeeper(object): parent_pdu = parent_pdus.pop(ca.handle, None) if (parent_pdu is None or - parent_pdu.bsc_handle != bsc_handle or - parent_pdu.repository_handle != ca.handle or - parent_pdu.peer_contact_uri != ca.rootd.service_uri or - parent_pdu.sia_base != ca.rootd.repository.sia_base or - parent_pdu.sender_name != ca.handle or - parent_pdu.recipient_name != ca.handle or - parent_pdu.bpki_cms_cert != ca.rootd.certificate): - rpkid_query.append(rpki.left_right.parent_elt.make_pdu( - action = "create" if parent_pdu is None else "set", - tag = ca.handle, - self_handle = ca.handle, - parent_handle = ca.handle, - bsc_handle = bsc_handle, - repository_handle = ca.handle, - peer_contact_uri = ca.rootd.service_uri, - sia_base = ca.rootd.repository.sia_base, - sender_name = ca.handle, - recipient_name = ca.handle, - bpki_cms_cert = ca.rootd.certificate)) - - except rpki.irdb.Rootd.DoesNotExist: + parent_pdu.get("bsc_handle") != bsc_handle or + parent_pdu.get("repository_handle") != ca.handle or + parent_pdu.get("peer_contact_uri") != ca.rootd.service_uri or + parent_pdu.get("sia_base") != ca.rootd.repository.sia_base or + parent_pdu.get("sender_name") != ca.handle or + parent_pdu.get("recipient_name") != ca.handle or + parent_pdu.findtext(rpki.left_right.tag_bpki_cert).decode("base64") != ca.rootd.certificate.get_DER()): + q_pdu = SubElement(q_msg, rpki.left_right.tag_parent, + action = "create" if parent_pdu is None else "set", + tag = ca.handle, + self_handle = ca.handle, + parent_handle = ca.handle, + bsc_handle = bsc_handle, + repository_handle = ca.handle, + peer_contact_uri = ca.rootd.service_uri, + sia_base = ca.rootd.repository.sia_base, + sender_name = ca.handle, + recipient_name = ca.handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = ca.rootd.certificate.get_Base64() + + except rpki.irdb.models.Rootd.DoesNotExist: pass - rpkid_query.extend(rpki.left_right.parent_elt.make_pdu( - action = "destroy", self_handle = ca.handle, parent_handle = p) for p in parent_pdus) + for parent_handle in parent_pdus: + SubElement(q_msg, rpki.left_right.tag_parent, action = "destroy", + self_handle = ca.handle, parent_handle = parent_handle) # Children are simpler than parents, because they call us, so no URL # to construct and figuring out what certificate to use is their @@ -1486,33 +1540,29 @@ class Zookeeper(object): child_pdu = child_pdus.pop(child.handle, None) if (child_pdu is None or - child_pdu.bsc_handle != bsc_handle or - child_pdu.bpki_cert != child.certificate): - rpkid_query.append(rpki.left_right.child_elt.make_pdu( - action = "create" if child_pdu is None else "set", - tag = child.handle, - self_handle = ca.handle, - child_handle = child.handle, - bsc_handle = bsc_handle, - bpki_cert = child.certificate)) - - rpkid_query.extend(rpki.left_right.child_elt.make_pdu( - action = "destroy", self_handle = ca.handle, child_handle = c) for c in child_pdus) + child_pdu.get("bsc_handle") != bsc_handle or + child_pdu.findtext(rpki.left_right.tag_bpki_cert).decode("base64") != child.certificate.get_DER()): + q_pdu = SubElement(q_msg, rpki.left_right.tag_child, + action = "create" if child_pdu is None else "set", + tag = child.handle, + self_handle = ca.handle, + child_handle = child.handle, + bsc_handle = bsc_handle) + SubElement(q_pdu, rpki.left_right.tag_bpki_cert).text = child.certificate.get_Base64() + + for child_handle in child_pdus: + SubElement(q_msg, rpki.left_right.tag_child, action = "destroy", + self_handle = ca.handle, child_handle = child_handle) # If caller wants us to poke rpkid, add that to the very end of the message if poke: - rpkid_query.append(rpki.left_right.self_elt.make_pdu( - action = "set", self_handle = ca.handle, run_now = "yes")) + SubElement(q_msg, rpki.left_right.tag_self, action = "set", self_handle = ca.handle, run_now = "yes") - # If we changed anything, ship updates off to rpkid + # If we changed anything, ship updates off to rpkid. - if rpkid_query: - rpkid_reply = self.call_rpkid(rpkid_query) - bsc_pdus = dict((x.bsc_handle, x) for x in rpkid_reply if isinstance(x, rpki.left_right.bsc_elt)) - if bsc_handle in bsc_pdus and bsc_pdus[bsc_handle].pkcs10_request: - bsc_req = bsc_pdus[bsc_handle].pkcs10_request - self.check_error_report(rpkid_reply) + if len(q_msg) > 0: + self.call_rpkid(q_msg) def synchronize_pubd_core(self): @@ -1532,43 +1582,57 @@ class Zookeeper(object): if not self.run_pubd: return - # Make sure that pubd's BPKI CRL is up to date. - - self.call_pubd(rpki.publication.config_elt.make_pdu( - action = "set", - bpki_crl = self.server_ca.latest_crl)) - # See what pubd already has on file - pubd_reply = self.call_pubd(rpki.publication.client_elt.make_pdu(action = "list")) - client_pdus = dict((x.client_handle, x) for x in pubd_reply if isinstance(x, rpki.publication.client_elt)) - pubd_query = [] + q_msg = self._compose_publication_control_query() + SubElement(q_msg, rpki.publication_control.tag_client, action = "list") + r_msg = self.call_pubd(q_msg) + client_pdus = dict((r_pdu.get("client_handle"), r_pdu) + for r_pdu in r_msg) # Check all clients + q_msg = self._compose_publication_control_query() + for client in self.server_ca.clients.all(): client_pdu = client_pdus.pop(client.handle, None) if (client_pdu is None or - client_pdu.base_uri != client.sia_base or - client_pdu.bpki_cert != client.certificate): - pubd_query.append(rpki.publication.client_elt.make_pdu( - action = "create" if client_pdu is None else "set", - client_handle = client.handle, - bpki_cert = client.certificate, - base_uri = client.sia_base)) + client_pdu.get("base_uri") != client.sia_base or + client_pdu.findtext(rpki.publication_control.tag_bpki_cert, "").decode("base64") != client.certificate.get_DER()): + q_pdu = SubElement(q_msg, rpki.publication_control.tag_client, + action = "create" if client_pdu is None else "set", + client_handle = client.handle, + base_uri = client.sia_base) + SubElement(q_pdu, rpki.publication_control.tag_bpki_cert).text = client.certificate.get_Base64() + + # rootd instances are also a weird sort of client + + for rootd in rpki.irdb.models.Rootd.objects.all(): + + client_handle = rootd.issuer.handle + "-root" + client_pdu = client_pdus.pop(client_handle, None) + sia_base = "rsync://%s/%s/%s/" % (self.rsync_server, self.rsync_module, client_handle) + + if (client_pdu is None or + client_pdu.get("base_uri") != sia_base or + client_pdu.findtext(rpki.publication_control.tag_bpki_cert, "").decode("base64") != rootd.issuer.certificate.get_DER()): + q_pdu = SubElement(q_msg, rpki.publication_control.tag_client, + action = "create" if client_pdu is None else "set", + client_handle = client_handle, + base_uri = sia_base) + SubElement(q_pdu, rpki.publication_control.tag_bpki_cert).text = rootd.issuer.certificate.get_Base64() # Delete any unknown clients - pubd_query.extend(rpki.publication.client_elt.make_pdu( - action = "destroy", client_handle = p) for p in client_pdus) + for client_handle in client_pdus: + SubElement(q_msg, rpki.publication_control.tag_client, action = "destroy", client_handle = client_handle) # If we changed anything, ship updates off to pubd - if pubd_query: - pubd_reply = self.call_pubd(pubd_query) - self.check_error_report(pubd_reply) + if len(q_msg) > 0: + self.call_pubd(q_msg) def synchronize_rpkid_deleted_core(self): @@ -1579,22 +1643,23 @@ class Zookeeper(object): inside a Django commit wrapper. """ - rpkid_reply = self.call_rpkid(rpki.left_right.self_elt.make_pdu(action = "list")) - self.check_error_report(rpkid_reply) + q_msg = self._compose_left_right_query() + SubElement(q_msg, rpki.left_right.tag_self, action = "list") + self.call_rpkid(q_msg) - self_handles = set(s.self_handle for s in rpkid_reply) - ca_handles = set(ca.handle for ca in rpki.irdb.ResourceHolderCA.objects.all()) + self_handles = set(s.get("self_handle") for s in q_msg) + ca_handles = set(ca.handle for ca in rpki.irdb.models.ResourceHolderCA.objects.all()) assert ca_handles <= self_handles - rpkid_query = [rpki.left_right.self_elt.make_pdu(action = "destroy", self_handle = handle) - for handle in (self_handles - ca_handles)] + q_msg = self._compose_left_right_query() + for handle in (self_handles - ca_handles): + SubElement(q_msg, rpki.left_right.tag_self, action = "destroy", self_handle = handle) - if rpkid_query: - rpkid_reply = self.call_rpkid(rpkid_query) - self.check_error_report(rpkid_reply) + if len(q_msg) > 0: + self.call_rpkid(q_msg) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def add_ee_certificate_request(self, pkcs10, resources): """ Check a PKCS #10 request to see if it complies with the @@ -1619,7 +1684,7 @@ class Zookeeper(object): ee_request.address_ranges.create(start_ip = str(r.min), end_ip = str(r.max), version = 6) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def add_router_certificate_request(self, router_certificate_request_xml, valid_until = None): """ Read XML file containing one or more router certificate requests, @@ -1631,16 +1696,15 @@ class Zookeeper(object): router-ID supplied in the XML. """ - xml = ElementTree(file = router_certificate_request_xml).getroot() - rpki.relaxng.router_certificate.assertValid(xml) + x = etree_read(router_certificate_request_xml, schema = rpki.relaxng.router_certificate) - for req in xml.getiterator(routercert_xmlns + "router_certificate_request"): + for x in x.getiterator(tag_router_certificate_request): - pkcs10 = rpki.x509.PKCS10(Base64 = req.text) - router_id = long(req.get("router_id")) - asns = rpki.resource_set.resource_set_as(req.get("asn")) + pkcs10 = rpki.x509.PKCS10(Base64 = x.text) + router_id = long(x.get("router_id")) + asns = rpki.resource_set.resource_set_as(x.get("asn")) if not valid_until: - valid_until = req.get("valid_until") + valid_until = x.get("valid_until") if valid_until and isinstance(valid_until, (str, unicode)): valid_until = rpki.sundial.datetime.fromXMLtime(valid_until) @@ -1667,7 +1731,7 @@ class Zookeeper(object): ee_request.asns.create(start_as = str(r.min), end_as = str(r.max)) - @django.db.transaction.commit_on_success + @django.db.transaction.atomic def delete_router_certificate_request(self, gski): """ Delete a router certificate request from this RPKI entity. |