diff options
author | Michael Elkins <melkins@tislabs.com> | 2013-03-04 22:54:47 +0000 |
---|---|---|
committer | Michael Elkins <melkins@tislabs.com> | 2013-03-04 22:54:47 +0000 |
commit | 79696d9ca55ed3360e87869bde14d873ceba9586 (patch) | |
tree | 3160e054661d2727326203cfbd7b66177ba7c335 | |
parent | afaadf9b7f241713e26f71dd30e909e95d9afe48 (diff) |
move core function to rpki.gui.app.check_expired
see #442
svn path=/trunk/; revision=5100
-rw-r--r-- | rpkid/portal-gui/scripts/rpkigui-check-expired.py | 184 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/check_expired.py | 199 |
2 files changed, 217 insertions, 166 deletions
diff --git a/rpkid/portal-gui/scripts/rpkigui-check-expired.py b/rpkid/portal-gui/scripts/rpkigui-check-expired.py index 131b3d69..7fcf7026 100644 --- a/rpkid/portal-gui/scripts/rpkigui-check-expired.py +++ b/rpkid/portal-gui/scripts/rpkigui-check-expired.py @@ -14,120 +14,10 @@ __version__ = '$Id$' -from rpki.gui.cacheview.models import Cert -from rpki.gui.cacheview.views import cert_chain -from rpki.gui.app.models import Conf, ResourceCert -from rpki.gui.app.glue import list_received_resources, get_email_list -from rpki.irdb import Zookeeper -from rpki.left_right import report_error_elt, list_published_objects_elt -from rpki.x509 import X509 - -from django.core.mail import send_mail - -import datetime -import sys -import socket from optparse import OptionParser -from cStringIO import StringIO - - -def check_cert(handle, p, errs): - """Check the expiration date on the X.509 certificates in each element of - the list. - - The displayed object name defaults to the class name, but can be overridden - using the `object_name` argument. - - """ - t = p.certificate.getNotAfter() - if t <= expire_time: - e = 'expired' if t <= now else 'will expire' - errs.write("%(handle)s's %(type)s %(desc)s %(expire)s on %(date)s\n" % { - 'handle': handle, 'type': p.__class__.__name__, 'desc': str(p), - 'expire': e, 'date': t}) - - -def check_cert_list(handle, x, errs): - for p in x: - check_cert(handle, p, errs) - - -def check_expire(conf, errs): - # get certs for `handle' - cert_set = ResourceCert.objects.filter(conf=conf) - for cert in cert_set: - # look up cert in cacheview db - obj_set = Cert.objects.filter(repo__uri=cert.uri) - if not obj_set: - # since the <list_received_resources/> output is cached, this can - # occur if the cache is out of date as well.. - errs.write("Unable to locate rescert in rcynic cache: handle=%s uri=%s not_after=%s\n" % (conf.handle, cert.uri, cert.not_after)) - continue - obj = obj_set[0] - cert_list = cert_chain(obj) - msg = [] - expired = False - for n, c in cert_list: - if c.not_after <= expire_time: - expired = True - f = '*' - else: - f = ' ' - msg.append("%s [%d] uri=%s ski=%s name=%s expires=%s" % (f, n, c.repo.uri, c.keyid, c.name, c.not_after)) - - # find ghostbuster records attached to this cert - for gbr in c.ghostbusters.all(): - info = [] - for s in ('full_name', 'organization', 'email_address', 'telephone'): - t = getattr(gbr, s, None) - if t: - info.append(t) - - msg.append(" Contact: " + ", ".join(info)) - - if expired: - errs.write("%s's rescert from parent %s will expire soon:\n" % ( - conf.handle, - # parent is None for the root cert - cert.parent.handle if cert.parent else 'self' - )) - errs.write("Certificate chain:\n") - errs.write("\n".join(msg)) - errs.write("\n") +import logging - -def check_child_certs(conf, errs): - """Fetch the list of published objects from rpkid, and inspect the issued - resource certs (uri ending in .cer). - - """ - z = Zookeeper(handle=conf.handle) - req = list_published_objects_elt.make_pdu(action="list", - tag="list_published_objects", - self_handle=conf.handle) - pdus = z.call_rpkid(req) - for pdu in pdus: - if isinstance(pdu, report_error_elt): - print >>sys.stderr, "rpkid reported an error: %s" % pdu.error_code - elif isinstance(pdu, list_published_objects_elt): - if pdu.uri.endswith('.cer'): - cert = X509() - cert.set(Base64=pdu.obj) - t = cert.getNotAfter() - if t <= expire_time: - e = 'expired' if t <= now else 'will expire' - errs.write("%(handle)s's rescert for Child %(child)s %(expire)s on %(date)s uri=%(uri)s subject=%(subject)s\n" % { - 'handle': conf.handle, - 'child': pdu.child_handle, - 'uri': pdu.uri, - 'subject': cert.getSubject(), - 'expire': e, - 'date': t}) - - -# this is not exactly right, since we have no way of knowing what the -# vhost for the web portal running on this machine is -host = socket.getfqdn() +from rpki.gui.app.check_expired import notify_expired usage = '%prog [ -nV ] [ handle1 handle2... ]' @@ -138,65 +28,27 @@ generated.""" parser = OptionParser(usage, description=description) parser.add_option('-V', '--version', help='display script version', - action='store_true', dest='version', default=False) -parser.add_option('-f', '--from', metavar='ADDRESS', default='root@' + host, - dest='from_email', - help='specify the return email address for notifications [default: %default]') -parser.add_option('-n', '--no-email', action='store_false', dest='email', default=True, - help='do not send any email reports [default: %default]') -parser.add_option('-t', '--expire-time', - dest='expire_days', - default=14, - metavar='DAYS', - help='specify the number of days in the future to check [default: %default]') + action='store_true', dest='version', default=False) +parser.add_option('-f', '--from', metavar='ADDRESS', dest='from_email', + help='specify the return email address for notifications') +parser.add_option('-t', '--expire-time', dest='expire_days', metavar='DAYS', + help='specify the number of days in the future to check') +parser.add_option('-l', '--level', dest='log_level', default='INFO', + help='Set logging level [Default: %default]') (options, args) = parser.parse_args() if options.version: print __version__ sys.exit(0) -now = datetime.datetime.utcnow() -expire_time = now + datetime.timedelta(int(options.expire_days)) -from_email = options.from_email - -# if not arguments are given, query all resource holders -qs = Conf.objects.all() if not args else Conf.objects.filter(handle__in=args) - -# check expiration of certs for all handles managed by the web portal -for h in qs: - # Force cache update since several checks require fresh data - try: - list_received_resources(sys.stdout, h) - except socket.error, e: - sys.exit('Error while talking to rpkid: %s' % e) - - errs = StringIO() - - check_cert(h.handle, h, errs) - - # HostedCA is the ResourceHolderCA cross certified under ServerCA, so check - # the ServerCA expiration date as well - check_cert(h.handle, h.hosted_by, errs) - check_cert(h.handle, h.hosted_by.issuer, errs) - - check_cert_list(h.handle, h.bscs.all(), errs) - check_cert_list(h.handle, h.parents.all(), errs) - check_cert_list(h.handle, h.children.all(), errs) - check_cert_list(h.handle, h.repositories.all(), errs) - - check_expire(h, errs) - check_child_certs(h, errs) - - # if there was output, display it now - s = errs.getvalue() - if s: - print s - - if options.email: - notify_emails = get_email_list(h) - if notify_emails: - t = """This is an automated notice about the upcoming expiration of RPKI resources for the handle %s on %s. You are receiving this notification because your email address is either registered in a Ghostbuster record, or as the default email address for the account.\n\n""" % (h.handle, host) +v = getattr(logging, options.log_level.upper()) +logging.basicConfig(level=v) +logging.info('logging level set to ' + logging.getLevelName(v)) - send_mail(subject='RPKI expiration notice for %s' % h.handle, - message=t + s, from_email=from_email, recipient_list=notify_emails) +kwargs = {} +if options.from_email: + kwargs['from_email'] = options.from_email +if options.expire_days: + kwargs['expire_days'] = int(options.expire_days) +notify_expired(**kwargs) sys.exit(0) diff --git a/rpkid/rpki/gui/app/check_expired.py b/rpkid/rpki/gui/app/check_expired.py new file mode 100644 index 00000000..5f35dbb7 --- /dev/null +++ b/rpkid/rpki/gui/app/check_expired.py @@ -0,0 +1,199 @@ +# Copyright (C) 2012, 2013 SPARTA, Inc. a Parsons Company +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +__version__ = '$Id$' +__all__ = ('notify_expired', 'NetworkError') + +import sys +import socket +from cStringIO import StringIO +import logging +import datetime + +from rpki.gui.cacheview.models import Cert +from rpki.gui.app.models import Conf, ResourceCert +from rpki.gui.app.glue import list_received_resources, get_email_list +from rpki.irdb import Zookeeper +from rpki.left_right import report_error_elt, list_published_objects_elt +from rpki.x509 import X509 + +from django.core.mail import send_mail + +logger = logging.getLogger(__name__) +expire_time = 0 # set by notify_expired() +now = 0 + + +def check_cert(handle, p, errs): + """Check the expiration date on the X.509 certificates in each element of + the list. + + The displayed object name defaults to the class name, but can be overridden + using the `object_name` argument. + + """ + t = p.certificate.getNotAfter() + if t <= expire_time: + e = 'expired' if t <= now else 'will expire' + errs.write("%(handle)s's %(type)s %(desc)s %(expire)s on %(date)s\n" % { + 'handle': handle, 'type': p.__class__.__name__, 'desc': str(p), + 'expire': e, 'date': t}) + + +def check_cert_list(handle, x, errs): + for p in x: + check_cert(handle, p, errs) + + +def check_expire(conf, errs): + # get certs for `handle' + cert_set = ResourceCert.objects.filter(conf=conf) + for cert in cert_set: + # look up cert in cacheview db + obj_set = Cert.objects.filter(repo__uri=cert.uri) + if not obj_set: + # since the <list_received_resources/> output is cached, this can + # occur if the cache is out of date as well.. + errs.write("Unable to locate rescert in rcynic cache: handle=%s uri=%s not_after=%s\n" % (conf.handle, cert.uri, cert.not_after)) + continue + obj = obj_set[0] + msg = [] + expired = False + for n, c in enumerate(obj.cert_chain): + if c.not_after <= expire_time: + expired = True + f = '*' + else: + f = ' ' + msg.append("%s [%d] uri=%s ski=%s name=%s expires=%s" % (f, n, c.repo.uri, c.keyid, c.name, c.not_after)) + + # find ghostbuster records attached to this cert + for gbr in c.ghostbusters.all(): + info = [] + for s in ('full_name', 'organization', 'email_address', 'telephone'): + t = getattr(gbr, s, None) + if t: + info.append(t) + + msg.append(" Contact: " + ", ".join(info)) + + if expired: + errs.write("%s's rescert from parent %s will expire soon:\n" % ( + conf.handle, + # parent is None for the root cert + cert.parent.handle if cert.parent else 'self' + )) + errs.write("Certificate chain:\n") + errs.write("\n".join(msg)) + errs.write("\n") + + +def check_child_certs(conf, errs): + """Fetch the list of published objects from rpkid, and inspect the issued + resource certs (uri ending in .cer). + + """ + z = Zookeeper(handle=conf.handle) + req = list_published_objects_elt.make_pdu(action="list", + tag="list_published_objects", + self_handle=conf.handle) + pdus = z.call_rpkid(req) + for pdu in pdus: + if isinstance(pdu, report_error_elt): + logger.error("rpkid reported an error: %s" % pdu.error_code) + elif isinstance(pdu, list_published_objects_elt): + if pdu.uri.endswith('.cer'): + cert = X509() + cert.set(Base64=pdu.obj) + t = cert.getNotAfter() + if t <= expire_time: + e = 'expired' if t <= now else 'will expire' + errs.write("%(handle)s's rescert for Child %(child)s %(expire)s on %(date)s uri=%(uri)s subject=%(subject)s\n" % { + 'handle': conf.handle, + 'child': pdu.child_handle, + 'uri': pdu.uri, + 'subject': cert.getSubject(), + 'expire': e, + 'date': t}) + + +class NetworkError(Exception): + pass + + +def notify_expired(expire_days=14, from_email=None): + """Send email notificates about impending expirations of resource + and BPKI certificates. + + expire_days: the number of days ahead of today to warn + + from_email: set the From: address for the email + + """ + global expire_time # so i don't have to pass it around + global now + + now = datetime.datetime.utcnow() + expire_time = now + datetime.timedelta(expire_days) + + # this is not exactly right, since we have no way of knowing what the + # vhost for the web portal running on this machine is + host = socket.getfqdn() + if not from_email: + from_email = 'root@' + host + + # if not arguments are given, query all resource holders + qs = Conf.objects.all() + + # check expiration of certs for all handles managed by the web portal + for h in qs: + # Force cache update since several checks require fresh data + try: + list_received_resources(sys.stdout, h) + except socket.error as e: + raise NetworkError('Error while talking to rpkid: %s' % e) + + errs = StringIO() + + check_cert(h.handle, h, errs) + + # HostedCA is the ResourceHolderCA cross certified under ServerCA, so + # check the ServerCA expiration date as well + check_cert(h.handle, h.hosted_by, errs) + check_cert(h.handle, h.hosted_by.issuer, errs) + + check_cert_list(h.handle, h.bscs.all(), errs) + check_cert_list(h.handle, h.parents.all(), errs) + check_cert_list(h.handle, h.children.all(), errs) + check_cert_list(h.handle, h.repositories.all(), errs) + + check_expire(h, errs) + check_child_certs(h, errs) + + # if there was output, display it now + s = errs.getvalue() + if s: + logger.info(s) + + notify_emails = get_email_list(h) + + if notify_emails: + t = """This is an automated notice about the upcoming expiration of RPKI resources for the handle %s on %s. You are receiving this notification because your email address is either registered in a Ghostbuster record, or as the default email address for the account.\n\n""" % (h.handle, host) + + send_mail( + subject='RPKI expiration notice for %s' % h.handle, + message=t + s, + from_email=from_email, + recipient_list=notify_emails + ) |