diff options
author | Rob Austein <sra@hactrn.net> | 2011-12-23 06:20:41 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2011-12-23 06:20:41 +0000 |
commit | 75c16c86b64dc47bc8559946d4e133586b9a2919 (patch) | |
tree | 8348cf0ddd300a2f448912c9dce0d13876dec299 | |
parent | 081d4284a989485236514ff80c9cce6676f35102 (diff) |
Rework schema using abstract models rather than stuffing everything
with the same syntax into the same SQL table.
svn path=/branches/tk100/; revision=4133
-rw-r--r-- | rpkid/rpki/irdb/models.py | 136 | ||||
-rw-r--r-- | rpkid/rpki/rpkic.py | 44 | ||||
-rw-r--r-- | scripts/convert-from-entitydb-to-sql.py | 95 |
3 files changed, 162 insertions, 113 deletions
diff --git a/rpkid/rpki/irdb/models.py b/rpkid/rpki/irdb/models.py index 9ab50de5..2cc3f5aa 100644 --- a/rpkid/rpki/irdb/models.py +++ b/rpkid/rpki/irdb/models.py @@ -154,13 +154,7 @@ class CertificateManager(django.db.models.Manager): changed = False try: - # Seriously icky, but does what we need. Rewrite using some - # kind of method routine that returns the key names. - try: - keys = self.model._meta.unique_together[0] or ("handle",) - except (AttributeError, IndexError): - keys = ("handle",) - obj = self.get(**dict((k, kwargs[k]) for k in keys)) + obj = self.get(**self._get_or_certify_keys(kwargs)) except self.model.DoesNotExist: obj = self.model(**kwargs) @@ -178,10 +172,24 @@ class CertificateManager(django.db.models.Manager): return obj, changed + def _get_or_certify_keys(self, kwargs): + return dict((k, kwargs[k]) for k in self.model._meta.unique_together[0]) + +class ResourceHolderCAManager(CertificateManager): + def _get_or_certify_keys(self, kwargs): + return { "handle" : kwargs["handle"] } + +class ServerCAManager(CertificateManager): + def _get_or_certify_keys(self, kwargs): + return { "pk" : 1 } + +class ReferralCertificateManager(CertificateManager): + def _get_or_certify_keys(self, kwargs): + return { "issuer" : kwargs["issuer"] } + ### class CA(django.db.models.Model): - handle = HandleField(unique = True) certificate = CertificateField() private_key = RSAKeyField() latest_crl = CRLField() @@ -195,27 +203,21 @@ class CA(django.db.models.Model): last_crl_update = SundialField() next_crl_update = SundialField() - objects = CertificateManager() - # These should come from somewhere, but I don't yet know where ca_certificate_lifetime = rpki.sundial.timedelta(days = 3652) crl_interval = rpki.sundial.timedelta(days = 1) - def __unicode__(self): - return self.handle + class Meta: + abstract = True def avow(self): if self.private_key is None: self.private_key = rpki.x509.RSA.generate() - if self.handle: - subject_name = rpki.x509.X501DN("%s BPKI resource CA" % self.handle) - else: - subject_name = rpki.x509.X501DN("%s BPKI server CA" % socket.gethostname()) now = rpki.sundial.now() notAfter = now + self.ca_certificate_lifetime self.certificate = rpki.x509.X509.bpki_self_certify( keypair = self.private_key, - subject_name = subject_name, + subject_name = self.subject_name, serial = self.next_serial, now = now, notAfter = notAfter) @@ -263,6 +265,27 @@ class CA(django.db.models.Model): self.next_crl_update = now + self.crl_interval self.next_crl_number += 1 +class ServerCA(CA): + objects = ServerCAManager() + + def __unicode__(self): + return "" + + @property + def subject_name(self): + return rpki.x509.X501DN("%s BPKI server CA" % socket.gethostname()) + +class ResourceHolderCA(CA): + handle = HandleField(unique = True) + objects = ResourceHolderCAManager() + + def __unicode__(self): + return self.handle + + @property + def subject_name(self): + return rpki.x509.X501DN("%s BPKI resource CA" % self.handle) + class Certificate(django.db.models.Model): certificate = CertificateField() @@ -286,25 +309,25 @@ class CrossCertification(Certificate): def avow(self): self.certificate = self.issuer.certify( - subject_name = self.ta.getSubject(), - subject_key = self.ta.getPublicKey(), - interval = self.default_interval, - is_ca = True, + subject_name = self.ta.getSubject(), + subject_key = self.ta.getPublicKey(), + validity_interval = self.default_interval, + is_ca = True, pathLenConstraint = 0) def __unicode__(self): return self.handle class HostedCA(Certificate): - issuer = django.db.models.ForeignKey(CA) - hosted = django.db.models.OneToOneField(CA, related_name = "hosted_by") + issuer = django.db.models.ForeignKey(ServerCA) + hosted = django.db.models.OneToOneField(ResourceHolderCA, related_name = "hosted_by") def avow(self): self.certificate = self.issuer.certify( - subject_name = self.hosted_ca.getSubject(), - subject_key = self.hosted_ca.getPublicKey(), - interval = self.default_interval, - is_ca = True, + subject_name = self.hosted_ca.getSubject(), + subject_key = self.hosted_ca.getPublicKey(), + validity_interval = self.default_interval, + is_ca = True, pathLenConstraint = 1) class Meta: @@ -314,51 +337,72 @@ class HostedCA(Certificate): return self.hosted_ca.handle class Revocation(django.db.models.Model): - issuer = django.db.models.ForeignKey(CA, related_name = "revocations") serial = django.db.models.BigIntegerField() revoked = SundialField() expires = SundialField() class Meta: + abstract = True unique_together = ("issuer", "serial") +class ServerRevocation(Revocation): + issuer = django.db.models.ForeignKey(ServerCA, related_name = "revocations") + +class ResourceHolderRevocation(Revocation): + issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "revocations") + class EECertificate(Certificate): - issuer = django.db.models.ForeignKey(CA, related_name = "ee_certificates") - purpose = EnumField(choices = ("rpkid", "pubd", "irdbd", "irbe", "rootd", "referral")) private_key = RSAKeyField() class Meta: - unique_together = ("issuer", "purpose") + abstract = True def avow(self): if self.private_key is None: self.private_key = rpki.x509.RSA.generate() - subject_name = rpki.x509.X501DN("%s BPKI %s EE" % ( - self.issuer.handle if self.issuer.handle else socket.gethostname(), - self.get_purpose_display())) self.certificate = self.issuer.certify( - subject_name = subject_name, + subject_name = self.subject_name, subject_key = self.private_key.get_RSApublic(), validity_interval = self.default_interval, is_ca = False) +class ServerCertificate(EECertificate): + issuer = django.db.models.ForeignKey(ServerCA, related_name = "ee_certificates") + purpose = EnumField(choices = ("rpkid", "pubd", "irdbd", "irbe", "rootd")) + + class Meta: + unique_together = ("issuer", "purpose") + + @property + def subject_name(self): + return rpki.x509.X501DN("%s BPKI %s EE" % (socket.gethostname(), self.get_purpose_display())) + +class ReferralCertificate(EECertificate): + issuer = django.db.models.OneToOneField(ResourceHolderCA, related_name = "referral_certificate") + objects = ReferralCertificateManager() + + @property + def subject_name(self): + return rpki.x509.X501DN("%s BPKI Referral EE" % self.issuer.handle) + + class BSC(Certificate): - issuer = django.db.models.ForeignKey(CA, related_name = "bscs") + issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "bscs") handle = HandleField() pkcs10 = PKCS10Field() def avow(self): self.certificate = self.issuer.certify( - subject_name = self.pkcs10.getSubject(), - subject_key = self.pkcs10.getPublicKey(), - interval = self.default_interval, - is_ca = False) + subject_name = self.pkcs10.getSubject(), + subject_key = self.pkcs10.getPublicKey(), + validity_interval = self.default_interval, + is_ca = False) def __unicode__(self): return self.handle class Child(CrossCertification): - issuer = django.db.models.ForeignKey(CA, related_name = "children") + issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "children") name = django.db.models.TextField(null = True, blank = True) valid_until = SundialField() @@ -380,7 +424,7 @@ class ChildNet(django.db.models.Model): unique_together = ("child", "start_ip", "end_ip", "version") class Parent(CrossCertification): - issuer = django.db.models.ForeignKey(CA, related_name = "parents") + issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "parents") parent_handle = HandleField() child_handle = HandleField() service_uri = django.db.models.CharField(max_length = 255) @@ -389,7 +433,7 @@ class Parent(CrossCertification): referral_authorization = SignedReferralField(null = True, blank = True) class ROARequest(django.db.models.Model): - issuer = django.db.models.ForeignKey(CA, related_name = "roa_requests") + issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "roa_requests") asn = django.db.models.BigIntegerField() class ROARequestPrefix(django.db.models.Model): @@ -403,18 +447,18 @@ class ROARequestPrefix(django.db.models.Model): unique_together = ("roa_request", "version", "prefix", "prefixlen", "max_prefixlen") class GhostbusterRequest(django.db.models.Model): - issuer = django.db.models.ForeignKey(CA, related_name = "ghostbuster_requests") + issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "ghostbuster_requests") parent = django.db.models.ForeignKey(Parent, related_name = "ghostbuster_requests", null = True) vcard = django.db.models.TextField() class Repository(CrossCertification): - issuer = django.db.models.ForeignKey(CA, related_name = "repositories") + issuer = django.db.models.ForeignKey(ResourceHolderCA, related_name = "repositories") client_handle = HandleField() service_uri = django.db.models.CharField(max_length = 255) sia_base = django.db.models.TextField() parent = django.db.models.OneToOneField(Parent, related_name = "repository") class Client(CrossCertification): - issuer = django.db.models.ForeignKey(CA, related_name = "clients") + issuer = django.db.models.ForeignKey(ServerCA, related_name = "clients") sia_base = django.db.models.TextField() diff --git a/rpkid/rpki/rpkic.py b/rpkid/rpki/rpkic.py index ea2b7b2a..88e3f26b 100644 --- a/rpkid/rpki/rpkic.py +++ b/rpkid/rpki/rpkic.py @@ -218,12 +218,12 @@ class main(rpki.cli.Cmd): def reset_identity(self): try: - self.resource_ca = rpki.irdb.CA.objects.get(handle = self.handle) - except rpki.irdb.CA.DoesNotExist: + self.resource_ca = rpki.irdb.ResourceHolderCA.objects.get(handle = self.handle) + except rpki.irdb.ResourceHolderCA.DoesNotExist: self.resource_ca = None try: - self.server_ca = rpki.irdb.CA.objects.get(handle = "*") - except rpki.irdb.CA.DoesNotExist: + self.server_ca = rpki.irdb.ServerCA.objects.get() + except rpki.irdb.ServerCA.DoesNotExist: self.server_ca = None def help_overview(self): @@ -252,7 +252,7 @@ class main(rpki.cli.Cmd): self.reset_identity() def complete_select_identity(self, *args): - return self.irdb_handle_complete(rpki.irdb.CA, *args) + return self.irdb_handle_complete(rpki.irdb.ResourceHolderCA, *args) def do_initialize(self, arg): @@ -266,23 +266,23 @@ class main(rpki.cli.Cmd): if arg: raise BadCommandSyntax, "This command takes no arguments" - self.resource_ca, created = rpki.irdb.CA.objects.get_or_certify(handle = self.handle) + self.resource_ca, created = rpki.irdb.ResourceHolderCA.objects.get_or_certify(handle = self.handle) if created: print "Created new BPKI resource CA for identity %s" % self.handle if self.run_rpkid or self.run_pubd or self.run_rootd: - self.server_ca, created = rpki.irdb.CA.objects.get_or_certify(handle = "*") + self.server_ca, created = rpki.irdb.ServerCA.objects.get_or_certify() if created: print "Created new BPKI server CA" if self.run_rpkid: - rpki.irdb.EECertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "rpkid") - rpki.irdb.EECertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "irdbd") + rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "rpkid") + rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "irdbd") if self.run_pubd: - rpki.irdb.EECertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "pubd") + rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "pubd") if self.run_rpkid or self.run_pubd: - rpki.irdb.EECertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "irbe") + rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "irbe") if self.run_rootd: - rpki.irdb.EECertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "rootd") + rpki.irdb.ServerCertificate.objects.get_or_certify(issuer = self.server_ca, purpose = "rootd") ## @todo # Why do we issue root's EE certificate under our server CA? @@ -345,19 +345,25 @@ class main(rpki.cli.Cmd): Most likely this should be run under cron. """ - for model in (rpki.irdb.CA, - rpki.irdb.EECertificate, + for model in (rpki.irdb.ServerCA, + rpki.irdb.ResourceHolderCA, + rpki.irdb.ServerCertificate, + rpki.irdb.HostedCA, + rpki.irdb.ReferralCertificate, rpki.irdb.BSC, rpki.irdb.Child, rpki.irdb.Parent, rpki.irdb.Client, rpki.irdb.Repository): - for obj in model.all(): + for obj in model.objects.all(): print "Regenerating certificate", obj.certificate.getSubject() obj.avow() - for ca in rpki.irdb.CA.all(): - print "Regenerating CRL for", ca.handle if ca.handle else "[servers]" + print "Regenerating Server CRL" + self.server_ca.generate_crl() + + for ca in rpki.irdb.ResourceHolderCA.objects.all(): + print "Regenerating CRL for", ca.handle ca.generate_crl() def do_configure_child(self, arg): @@ -547,7 +553,7 @@ class main(rpki.cli.Cmd): client_ta = rpki.x509.X509(Base64 = client.findtext("bpki_client_ta")) - if sia_base is None and client.get("handle") == self.handle and self.bpki_resources.ca.certificate == client_ta: + if sia_base is None and client.get("handle") == self.handle and self.resource_ca.certificate == client_ta: print "This looks like self-hosted publication" sia_base = "rsync://%s/%s/%s/" % (self.rsync_server, self.rsync_module, self.handle) @@ -869,7 +875,7 @@ class main(rpki.cli.Cmd): action = "set", bpki_crl = self.server_ca.latest_crl)) - for ca in rpki.irdb.CA.objects.exclude(handle = "*"): + for ca in rpki.irdb.ResourceHolderCA.objects.all(): # See what rpkid and pubd already have on file for this entity. diff --git a/scripts/convert-from-entitydb-to-sql.py b/scripts/convert-from-entitydb-to-sql.py index dbdde34c..64f0d31a 100644 --- a/scripts/convert-from-entitydb-to-sql.py +++ b/scripts/convert-from-entitydb-to-sql.py @@ -109,37 +109,40 @@ def read_openssl_serial(filename): f.close() return int(text.strip(), 16) -def get_or_create_CA(purpose): - cer = rpki.x509.X509(Auto_file = os.path.join(bpki, purpose, "ca.cer")) - key = rpki.x509.RSA(Auto_file = os.path.join(bpki, purpose, "ca.key")) - crl = rpki.x509.CRL(Auto_file = os.path.join(bpki, purpose, "ca.crl")) - serial = read_openssl_serial(os.path.join(bpki, purpose, "serial")) - crl_number = read_openssl_serial(os.path.join(bpki, purpose, "crl_number")) - - return rpki.irdb.CA.objects.get_or_create( - handle = self_handle if purpose == "resources" else "*", - certificate = cer, - private_key = key, - latest_crl = crl, - next_serial = serial, - next_crl_number = crl_number, - last_crl_update = crl.getThisUpdate().to_sql(), - next_crl_update = crl.getNextUpdate().to_sql())[0] - -def get_or_create_EECertificate(issuer, capurpose, eepurpose): - cer = rpki.x509.X509(Auto_file = os.path.join(bpki, capurpose, eepurpose + ".cer")) - key = rpki.x509.RSA(Auto_file = os.path.join(bpki, capurpose, eepurpose + ".key")) - rpki.irdb.EECertificate.objects.get_or_create( +def get_or_create_ServerCertificate(issuer, purpose): + cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "servers", purpose + ".cer")) + key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", purpose + ".key")) + rpki.irdb.ServerCertificate.objects.get_or_create( issuer = issuer, - purpose = eepurpose, + purpose = purpose, certificate = cer, private_key = key) # Load BPKI CA data -resource_ca = get_or_create_CA("resources") +cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "resources", "ca.cer")) +key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "resources", "ca.key")) +crl = rpki.x509.CRL(Auto_file = os.path.join(bpki, "resources", "ca.crl")) +serial = read_openssl_serial(os.path.join(bpki, "resources", "serial")) +crl_number = read_openssl_serial(os.path.join(bpki, "resources", "crl_number")) + +resource_ca = rpki.irdb.ResourceHolderCA.objects.get_or_create( + handle = self_handle, + certificate = cer, + private_key = key, + latest_crl = crl, + next_serial = serial, + next_crl_number = crl_number, + last_crl_update = crl.getThisUpdate().to_sql(), + next_crl_update = crl.getNextUpdate().to_sql())[0] + if os.path.exists(os.path.join(bpki, "resources", "referral.cer")): - get_or_create_EECertificate(resource_ca, "resources", "referral") + cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "resources", "referral.cer")) + key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "resources", "referral.key")) + rpki.irdb.ReferralCertificate.objects.get_or_create( + issuer = resource_ca, + certificate = cer, + private_key = key) # Load BPKI server EE certificates and keys @@ -147,15 +150,29 @@ run_flags = dict((i, cfg.getboolean(i, section = "myrpki")) for i in ("run_rpkid", "run_pubd", "run_rootd")) if any(run_flags.itervalues()): - server_ca = get_or_create_CA("servers") - get_or_create_EECertificate(server_ca, "servers", "irbe") + cer = rpki.x509.X509(Auto_file = os.path.join(bpki, "servers", "ca.cer")) + key = rpki.x509.RSA(Auto_file = os.path.join(bpki, "servers", "ca.key")) + crl = rpki.x509.CRL(Auto_file = os.path.join(bpki, "servers", "ca.crl")) + serial = read_openssl_serial(os.path.join(bpki, "servers", "serial")) + crl_number = read_openssl_serial(os.path.join(bpki, "servers", "crl_number")) + + server_ca = rpki.irdb.ServerCA.objects.get_or_create( + certificate = cer, + private_key = key, + latest_crl = crl, + next_serial = serial, + next_crl_number = crl_number, + last_crl_update = crl.getThisUpdate().to_sql(), + next_crl_update = crl.getNextUpdate().to_sql())[0] + + get_or_create_ServerCertificate(server_ca, "irbe") if run_flags["run_rpkid"]: - get_or_create_EECertificate(server_ca, "servers", "rpkid") - get_or_create_EECertificate(server_ca, "servers", "irdbd") + get_or_create_ServerCertificate(server_ca, "rpkid") + get_or_create_ServerCertificate(server_ca, "irdbd") if run_flags["run_pubd"]: - get_or_create_EECertificate(server_ca, "servers", "pubd") + get_or_create_ServerCertificate(server_ca, "pubd") if run_flags["run_rootd"]: - get_or_create_EECertificate(server_ca, "servers", "rootd") + get_or_create_ServerCertificate(server_ca, "rootd") else: server_ca = None @@ -194,24 +211,6 @@ def xcert_hash(cert): hash = hash[len("(stdin)="):] return hash -# OK, all this wretched cross-certification looks complicated, but -# that's partly because of the way we've been doing it on disk. The -# new SQL/object based approach should make it much clearer: -# -# Child cross certifies parent's resource TA in child's resource CA. -# -# Parent cross certifies child's resource TA in parent's resource -# CA. -# -# Repository cross certifies client's resource TA in repository's -# server CA. -# -# Client cross certifies repository's server TA in client's resource -# CA. -# -# The remaining xcert files look to be TLS relics which no longer -# serve any real purpose; in theory, those can just go away. - # Let's try keeping track of all the xcert filenames we use, so we can # list the ones we didn't. |