diff options
Diffstat (limited to 'potpourri')
-rwxr-xr-x | potpourri/bgpsec-rpki-router-key-hack | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/potpourri/bgpsec-rpki-router-key-hack b/potpourri/bgpsec-rpki-router-key-hack new file mode 100755 index 00000000..b1e3845c --- /dev/null +++ b/potpourri/bgpsec-rpki-router-key-hack @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +# $Id$ +# +# Copyright (C) 2014 Dragon Research Labs ("DRL") +# Portions copyright (C) 2009-2013 Internet Systems Consortium ("ISC") +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notices and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND DRL AND ISC DISCLAIM ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL OR +# ISC 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. + +import os +import glob +import shutil +import sqlite3 +import rpki.rtr.client +import rpki.rtr.channels + +def find_free_name(fmt, n = 10): + """ + Handle SKI hash collisions by allowing a small number of distinct + filenames for each ASN/SKI pair. + """ + + for i in xrange(n): + fn = fmt % i + if not os.path.exists(fn): + return fn + raise RuntimeError("Couldn't find a free filename for key %s after %d tries" % (fmt, n)) + +class ClientChannel(rpki.rtr.client.ClientChannel): + """ + Subclass ClientChannel to extend .end_of_data() method. + """ + + def end_of_data(self, version, serial, nonce, refresh, retry, expire): + """ + Call base method to do all the normal EndOfData processing, then + dump out the key database using the symlink-to-directory hack so + we can rename() the result to perform an atomic installation. + """ + + # Run the base method + super(ClientChannel, self).end_of_data(version, serial, nonce, refresh, retry, expire) + + # Set up our new output directory + dn = "%s.%s" % (self.args.bgpsec_key_directory, rpki.rtr.channels.Timestamp.now()) + ln = "%s.%s" % (self.args.bgpsec_key_directory, ".tmp") + if os.path.exists(ln): + os.unlink(ln) + os.makedirs(dn) + + # Write all the keys + for asn, gski, key in self.sql.execute("SELECT asn, ski, key FROM routerkey"): + with open(find_free_name("%s/%s.%s.%%d.key" % (dn, asn, gski)), "wb") as f: + f.write(key.decode("base64")) + + # Install the new directory + os.symlink(os.path.basename(dn), ln) + os.rename(ln, self.args.bgpsec_key_directory) + + # Clean up old output directories + pattern = ".[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z" + for gn in glob.iglob(self.args.bgpsec_key_directory + pattern): + if gn != dn: + shutil.rmtree(gn) + +def SymlinkToDirectory(path): + """ + argparse "type" function to clean up and sanity check --bgpsec-key-directory. + """ + + path = os.path.abspath(path).rstrip("/") + if os.path.exists(path) and not os.path.islink(path): + raise ValueError + return path + +# Grab handle on the normal client argparse setup function +client_argparse_setup = rpki.rtr.client.argparse_setup + +# Extend argparse setup to add our own (required) parameter +def argparse_setup(subparsers): + subparser = client_argparse_setup(subparsers) + subparser.add_argument("--bgpsec-key-directory", required = True, type = SymlinkToDirectory, + help = "where to write BGPSEC router keys") + return subparser + +# Splice our extensions into client +rpki.rtr.client.argparse_setup = argparse_setup +rpki.rtr.client.ClientChannelClass = ClientChannel + +# And run the program +import rpki.rtr.main +rpki.rtr.main.main() |